Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,100 @@ makes it the file parallel sessions collide in most often. The convention:
- Prefer `EventBus` signals over direct references between systems. A system
that only listens needs no wiring in `main.gd` at all.

## Procedural animation (`game/src/anim/`)

Gradientfall generates every asset in code, so there are no motion-capture
clips to blend. Movement is instead a small stack of creature-agnostic modules
that turn physics state into a pose. Kern is the first consumer; every future
villager, monster and boss is meant to be the next.

```mermaid
flowchart TD
Body[CharacterBody3D velocity + floor state] --> Animator[CreatureAnimator]
Profile[LocomotionProfile measured from the rig] --> Animator
Animator --> Gait[GaitEngine distance-phased cycle]
Animator --> Planter[FootPlanter ground probes + plant lock]
Gait --> Planter
Planter --> IK[TwoBoneIk analytic leg solve]
Emotes[EmotePlayer + EmoteLibrary] --> Pose
IK --> Pose[PoseStack layered quaternion pose]
Animator --> Pose
Pose --> Skeletons[Skeleton3D, committed by the character script]
```

**The load-bearing idea: the gait is phased by DISTANCE, not time.**
`GaitEngine.advance()` integrates `distance_travelled / stride_length`. Because
a planted foot's body-relative position then retreats at exactly the speed the
body advances, the foot holds still in world space for the whole stance. Foot
slip goes to zero as an algebraic identity rather than as a tuned
approximation — at any speed, mid-acceleration, and across gait changes. The
placeholder version advanced its cycle on a timer while the body moved at
whatever speed it liked, which is why its feet skated.

Module responsibilities, all leaf-first so each is testable alone:

- `anim_math.gd` — frame-rate-independent damping (half-life based) and exact
critically-damped springs. Nothing here uses `lerp(a, b, k * delta)`, which
converges at different real-world speeds on different hardware.
- `gait_profile.gd` — one gait as biomechanics (stride, duty factor, pelvic
oscillation, foot levers), plus human presets. Gaits blend *continuously* by
speed, so there is no walk/run threshold to pop at.
- `gait_engine.gd` — the distance-phased cycle; per-foot stance/swing state and
whole-body oscillation. The foot is treated as a rigid lever rocking over
heel and toe, so the contact patch stays exactly still through the roll.
- `foot_planter.gd` — raycasts the real collision world under each foot, locks
the plant in world space (with a leash so pivoting scuffs rather than
sticking), aligns ankles to the surface, and drops the pelvis to keep a
downhill foot reachable.
- `two_bone_ik.gd` — closed-form law-of-cosines leg/arm solve. Analytic rather
than `SkeletonIK3D` because it is exact, allocation-free, and — critically —
blendable against the procedural pose like any other layer.
- `locomotion_profile.gd` — measures the creature's limb lengths and joint rest
positions off its own `Skeleton3D` and rescales the gait presets by leg
length (dynamic similarity). Nothing is hard-coded per creature.
- `pose_stack.gd` — the frame's pose as layered quaternions plus a root offset.
Quaternions because layers must *compose*; adding euler triples is not
composing rotations, and it gimbals as soon as an emote overlays a gait.
- `emote_library.gd` / `emote_player.gd` — emotes and dances as functions of
time, with blend-in/out and partial-body support so a wave can play over a
walk while a dance takes the whole body.
- `creature_animator.gd` — the coordinator that runs the above in order and
layers idle life, momentum lean, airborne and landing behaviour on top.

### Adopting it for a new creature

1. Build the creature a `Skeleton3D` whose bones carry the names in
`kern_bone_map.gd` (`Hips`, `Spine`, `Chest`, `ThighL`, `ShinL`, `FootL`, …).
Missing bones are simply not driven, so a partial rig degrades rather than
erroring.
2. Construct a `CreatureAnimator` and `bind()` it to the visual node, the
physics body, the skeleton and its bone map.
3. Call `tick(delta, velocity, on_floor, crouch)` from `_physics_process` and
commit the returned `PoseStack` to the skeleton.
4. Tune by editing that creature's `GaitProfile` numbers — stride, duty factor,
foot levers — not by writing new animation code.

A quadruped is the same machinery with four `FootPlanter`s and two
`GaitEngine`s phase-offset against each other; nothing in the modules assumes
two legs except the presets.

### Verifying movement work

`game/src/dev/locomotion_lab.gd` (scene: `scenes/dev/locomotion_lab.tscn`) is
the instrumented bench. It builds a four-zone obstacle course, drives the real
controller through a scripted program via the `Input` singleton, and reports
foot slip, ground error, IK reach shortfall, pose jerk and knee direction per
segment as a table plus JSON. Movement changes are expected to quote its
numbers. Two hard-won cautions, both of which produced badly misleading
figures before they were fixed:

- Measure slip on the **instantaneous contact point** (heel while the toes are
up, toe once the heel has lifted, ankle while flat), not on a fixed point of
the foot — otherwise honest heel-and-toe rocker reads as sliding.
- Gate it on **stance**, not on the IK's `contact` weight. Contact deliberately
ramps up before touchdown so the IK eases in, so it is true while the foot is
still travelling at full speed.

## Code documentation standard

Documentation is a first-class deliverable of this project, and quality over
Expand Down
91 changes: 91 additions & 0 deletions docs/DEVLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,97 @@

---

## 2026-07-29 (live session, movement lane) — PROCEDURAL ANIMATION FRAMEWORK; KERN REBUILT ON IT

*Danny asked for Kern's mechanics to be drastically improved and made
lifelike, for idle life and on-command emotes, and for the work to be shaped so
every future creature benefits. Godot 4.7.1 available locally, so everything
below is verified by running the game, not by inspection.*

**DONE — the framework (`game/src/anim/`, 9 new modules, creature-agnostic)**
- `gait_engine.gd` — the load-bearing idea: the gait cycle is phased by
**distance travelled**, not by time. A planted foot's body-relative position
then retreats at exactly the speed the body advances, so it holds still in
world space by algebraic identity rather than by tuning. The old code did
`phase += delta * rate` while the body moved at whatever speed it liked,
which is precisely why Kern's feet skated.
- `gait_profile.gd` — a gait as biomechanics (stride, duty factor, pelvic
oscillation, foot levers) with human presets from gait-lab figures. Gaits
blend CONTINUOUSLY by speed, so there is no walk/run threshold to pop at, and
the walk/run pelvis inversion (highest at midstance vaulting vs lowest
compressing) is preserved.
- `two_bone_ik.gd` — closed-form law-of-cosines leg solve. Analytic rather than
`SkeletonIK3D` because it is exact, allocation-free and blendable per-frame
against the procedural pose, which is what lets feet be planted without the
animation losing control of the leg.
- `foot_planter.gd` — raycasts the real collision world under each foot, locks
the plant in world space (leashed, so pivoting scuffs rather than sticking),
aligns ankles to the surface normal, and drops the pelvis to keep a downhill
foot reachable.
- `locomotion_profile.gd` — measures limb lengths and joint rests off the
creature's own skeleton and rescales the presets by leg length (dynamic
similarity). No per-creature constants.
- `anim_math.gd`, `pose_stack.gd`, `emote_library.gd`, `emote_player.gd`,
`creature_animator.gd` — half-life damping and exact critically-damped
springs; layered QUATERNION pose (euler layers do not compose, which is why
an emote over a gait used to skew); 16 emotes; and the coordinator.

**DONE — Kern rebuilt on it**
- Controller: analog speed with walk/jog/run/sprint blended continuously (stick
deflection IS the speed), crouch with capsule resize and ceiling check,
slope-aware pace from the floor normal, step-up, turn-costs-momentum,
speed-dependent turn rate, landing impact reported to the animator.
- Visual: distance-phased gait, foot IK on terrain, terrain-adaptive pelvis,
spinal-delay counter-rotation, arm swing with elbow flexion, blended airborne
pose with landing reach, momentum lean, breathing plus a fidget scheduler so
idle keeps producing new motion, head look-at with travel anticipation.
- Emotes: 16 in the world's own voice — Gradient Descent, The Backprop,
Overfit, Dropout, Convergence, Weight Shuffle, Epoch Step, Softmax, plus
wave/cheer/bow/point/salute/ponder/stretch/sit. Radial wheel on B (right
stick click on a pad); partial-body emotes layer over locomotion, full-body
ones take over; any movement input cancels out.

**VERIFIED — measured, not asserted (`scenes/dev/locomotion_lab.tscn`)**
- New instrumented bench: four-zone obstacle course (flat, ramps, stairs,
bumps), 20 scripted segments driven through the real `Input` singleton.
- Foot slip **147 -> 47 mm/m** overall; **19-23 mm/m at a walk**, 25 on stairs,
31 on broken ground; idle, hard-stop and turn-in-place measure ~0.2 mm.
- Ground error **60 -> 7.4 mm**. Backward-knee frames **8182 -> 0**. IK
shortfall 5 mm, clamped on 11% of frames. Worst pose jerk 58 -> 47 rad/s.
- Eyes on rendered frames against ground stripes for walk, run and dance.

**FIXED IN PASSING (both pre-existing, both found by looking)**
- The placeholder gait bent Kern's knees **backwards** at every speed:
`ShinL/R` took POSITIVE X, which on this rig is hyperextension. The IK now
derives knee direction from a pole vector so it cannot recur, and the bench
fails the run if it does.
- The boot shaft stopped 65 mm below the trouser cuff, leaving a visible ring
of nothing between boot and trouser whenever the knee bent.

**HALF-FORMED**
- Remaining measured slip is concentrated in the heel and toe rocker phases at
jog and above (flat-sole slip is near zero, so the plant lock itself is
sound). The rigid-foot rocker is exact on paper and verified frame-by-frame;
the residual is most likely gait-profile blending shifting stride mid-stance.
- Kern's rig gives him a 0.81 m leg on a 1.78 m body (a real adult is ~0.87 m)
because his ankle joint sits ~4 cm high. That shortfall is what forces the
stride cap in `LocomotionProfile.max_reachable_stride()` and a slightly brisk
cadence. Lowering the ankle in `kern_body_builder.gd` would buy it back, but
it moves the boot/foot meshes and the imported-rig retarget, so it is its own
change.
- `jump_flat` still measures ~108 mm/m — landing transients, the least-tuned
part of the pass.

**NEXT UP**
- Rig the Bootstrap villagers and the monster roster so they can adopt
`CreatureAnimator` (they are currently un-skeletoned meshes). The framework
is ready; the creatures are not yet.
- Footstep audio and dust/grass-trample hooks off `GaitEngine.just_planted()`,
which already fires on the exact touchdown frame.
- Hands-on pad feel-tune with Danny at the phase gate.

---

## 2026-07-27 (repository recovery) — Vault and Iris branches integrated

**DONE**
Expand Down
1 change: 1 addition & 0 deletions docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Prove every system small, then scale outward. One region done completely.

- [x] Godot 4 project scaffold (`gradientfall/game/`), folder conventions, autoloads *(boot verified headless in Godot 4.7.1: clean import, ContentDB loads 22 entries, zero errors)*
- [x] Third-person character controller: walk/run/jump/camera (feel pass included) *(boot verified clean in Godot 4.7.1 after class-cache re-import; hands-on feel-tune still welcome at the phase gate)*
- [x] **Movement pass: procedural animation framework + Kern's mechanics** *(built `src/anim/` — a creature-agnostic stack every future NPC/monster/boss can adopt: distance-phased gait engine, biomechanical gait profiles blended continuously by speed, analytic two-bone IK, terrain foot planting with world plant-locks, rigid-foot heel/toe rocker, layered quaternion pose stack, emote system. Kern rebuilt on it: analog walk/jog/run/sprint with no gait thresholds, crouch, slope-aware pace, step-up, landing absorption, momentum lean, idle fidgets, and 16 emotes/dances on a radial wheel (B / right-stick click). New instrumented bench `scenes/dev/locomotion_lab.tscn` measures foot slip, ground error, IK shortfall, pose jerk and knee direction over a 20-segment obstacle course. Measured: foot slip **147 -> 47 mm/m** overall and **19-23 mm/m at a walk**, ground error **60 -> 7.4 mm**, backward-knee frames **8182 -> 0**, IK shortfall 5 mm. Eyes-verified via rendered gait/dance frames against ground stripes. **Fixed in passing:** the placeholder gait bent Kern's knees BACKWARDS at every speed, and the boot shaft stopped 65 mm below the trouser cuff leaving a visible hole whenever the knee bent.)*
- [x] Terrain: Datasedge Meadows heightmap terrain + procedural grass/trees, region border vistas toward future regions *(480×480 m procedural heightmap w/ town flat + carved millpond, 34k wind-swayed grass, iris flats, tree copses, 4-direction border vistas; built & eyes-verified via screenshots in a live session, 5 palette/lighting iterations. NOTE: still default lighting — the cel-shade pass below is what makes it "pretty")*
- [x] Cel-shaded look dev v1: toon shader, sky, day/night cycle, wind grass *(reusable toon.gdshader: banded diffuse + fresnel rim + sky-tinted shadow fill; applied to terrain/grass/trees/character; SkyCycle drives sun arc + 7-key color script dawn→noon→dusk→night; eyes-verified via screenshots incl. a 4-time-of-day showcase. Character rim pops nicely. Kern still a placeholder capsule — the character-model milestone dresses him)*
- [x] Bit the fairy: follow behavior, look-at naming, hint lines *(built: exp-smoothed hover-follow with idle orbit/bob, sprint catch-up, and canon water-fear over the millpond; BitLandmark look-at naming across 8 canon meadow sites (remembered in save flags); in-voice barks — greeting, idle/hint, quiz/item/region reactions — on a floating Label3D + EventBus.bit_spoke. **UNSEEN**: no Godot in this env — needs a live session to import (.uid gen), confirm clean boot, and eyes per GDD §10)*
Expand Down
6 changes: 6 additions & 0 deletions game/scenes/dev/locomotion_lab.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3]

[ext_resource type="Script" path="res://src/dev/locomotion_lab.gd" id="1_lab"]

[node name="LocomotionLab" type="Node3D"]
script = ExtResource("1_lab")
Loading