The Unfinished  ·  Dev Log
Day 4 · Wednesday, September 30, 2026

Water, light, mist and a misunderstood guardian

The Heartroot gets its streams, a lower-key light and mist with depth; the Misunderstood Guardian is rigged, polished and given a relationship with the Seed; a debug mode for play-testing; and the agents start taking the Editor in turns.

24entries42screenshots7agents
debuggaitguardianhistorymossstormterrain
07:49terrain

The Heartroot rises: the first Rootbound region

worldheartroot
v1 from the lookout ledge: milky and overexposed
v1 from the lookout ledge: milky and overexposed
v1 from the air: the sinkhole, terraces and arches
v1 from the air: the sinkhole, terraces and arches

Jeremy chose the Rootbound for the first civilization, in its own region scene. The research set the approach: stay on URP, terrain is only the floor, the hero forms are meshes made by scripts. Overnight a pipeline came together: 13 free (CC0) material sets on one world shader, a layout file both Blender and Unity read, a Blender builder that braids roots out of strands (35 pieces), and a region builder for the terrain, chasm, roots, walkways and mist. Version 1 was milky and overexposed, the bark tan instead of ivory.

08:07terrain

Fixes from the captures

worldheartroot
v3: ivory roots, side light, the chasm open again
v3: ivory roots, side light, the chasm open again
The Returning Plaza at the south bridgehead
The Returning Plaza at the south bridgehead

Judged from post-processed captures and numbers rather than by eye: exposure and ambient down, ivory bark, warm hearths instead of glowing white panels. A far-ground plane at 8 m had capped the chasm and flooded the terraces; it became a ring from the terrain's edge. All captures pass the value screens from v1 on - the Grove baseline passed 2 of 10.

08:51terrain

Forests: from 8 fps to 9 ms

worldvegetationperformance
Generated trees in the valleys
Generated trees in the valleys
v6: forest, karst pillars with groves on top
v6: forest, karst pillars with groves on top

Trees and shrubs generated in Blender from tubes and leaf cards on a CC0 leaf atlas. As Unity terrain trees they cost 44 million triangles and 122,000 batches - 8 fps on the M4. A new instanced renderer (VegetationField) draws the same forest in 4.7 million triangles, about 550 batches, a 9 ms frame.

14:52terrain

Water: streams, cascades and falls into the chasm

worldwater
A cascade down a terrace bank into its pool
A cascade down a terrace bank into its pool
A stream over a notched weir, falling into the chasm
A stream over a notched weir, falling into the chasm

Four streams cross the terraces in carved channels, run down each bank as white water and pour over the rim. Reviews and measurements on the built meshes caught a stream ribbon running under a bank, falls hanging inside the rock, cascades floating 3.7 m on one side, and a Unity trap: meshes rebuilt with CopySerialized kept drawing their old shape. All fixed and measured.

16:23terrain

First milestone committed

heartroot
The south bridge to the ring and the Heartroot Hall
The south bridge to the ring and the Heartroot Hall

461 files: the research, the pipeline, the region, tests (31/31), five fresh-context reviews. Names and layout are proposals.

17:13debug

Debug / god mode for play-testing

debugtoolsplay-testing
The panel in Heartroot: live readout, Seed switches, travel (seedbeds, areas, regions). Taken before the review fixes; a stray horizontal scrollbar was removed afterwards.
The panel in Heartroot: live readout, Seed switches, travel (seedbeds, areas, regions). Taken before the review fixes; a stray horizontal scrollbar was removed afterwards.
Flying (F2) up to the Heartroot: 22 m forward and 18 m up in two seconds. The badge top right shows which debug switches are on.
Flying (F2) up to the Heartroot: 22 m forward and 18 m up in two seconds. The badge top right shows which debug switches are on.
Verdant Spire, loaded from the panel's region buttons: 6 seedbeds (✓ = awakened) and every area as a teleport.
Verdant Spire, loaded from the panel's region buttons: 6 seedbeds (✓ = awakened) and every area as a teleport.

A debug mode for play-testing, in the Editor and development builds only (never in a release build). ` `` or F1 opens a panel. Hotkeys work without it:

  • F2 fly (through walls by default)
  • F3 god mode
  • F4 teleport to where the camera points
  • F5 / F6 bookmark a spot / go back to it

The panel teleports to every seedbed, area and region; scales run, jump and game speed; makes enemies ignore the Seed, or defeats or resets them; grants Threadroot and Root Sense; sets save flags; and can stop the save file being written. The Seed's own controls pause while the panel is open. Checked in Play in Heartroot and Verdant Spire. A fresh-context review then found 8 bugs, which were fixed (see the 18:32 entry). Commit ea1f6c5.

18:32debug

Review fixes checked in Play; iCloud was evicting project files

debugtoolsenvironment

The 8 review fixes, checked in Verdant Spire (the first one by reading the code: a text-field focus can't be faked from a script):

  • F1 and Esc close the panel even while a text field has focus.
  • Closing the panel re-enables only the controls it paused, so a Look switched off by Moss's meeting stays off.
  • Flying ignores keys typed into the panel.
  • 'Enemies ignore the Seed' also stops Stalker ambushes and enemies already chasing.
  • New game here switches the abilities off.
  • The save switch restores a test bench's own setting.

PlayMode tests 31/31.

Earlier the Editor had stopped compiling: iCloud's Optimize Mac Storage had evicted about 9,000 project files (Library, Assets, .git), so reads stalled or failed. Jeremy fixed it; find UnityProject -flags +dataless | wc -l now reports 0. The Editor was shared with the guardian session by message and handed back with its scene reopened.

18:40terrain

Three agents, one Editor

projectworkflow

Jeremy asked for a better way for the agents to share the project. The Mac fits one Unity Editor (it alone used 11 GB of 16), so the Editor is now taken in turns through a queue, and everything else happens in each agent's own git worktree. After asking the other two agents what slowed them down, the tools came together: Tools/team.sh (worktrees, landing a branch during a turn, a who-works-where board, a health check) and a queue and handback check in the Editor lock. Restarting the Editor brought it from 11 GB to 3.5 GB.

19:16history

The Misunderstood Guardian arrives

charactersguardian
Blender pose sheet of the clips (not in game)
Blender pose sheet of the clips (not in game)
First Unity preview of the prefab on the shared creature look
First Unity preview of the prefab on the shared creature look
First capture in Play, taken at 19:51 once the Editor was free
First capture in Play, taken at 19:51 once the Editor was free

Jeremy brought the Misunderstood Guardian concept sheets and a Tripo model, and chose: a new creature (the Root Guardian enemy stays), about 6 m tall and 11 m long, the whole behaviour loop built now with its rules marked provisional, and a test arena only for now.

  • Model: the mesh matched the concept but had no skeleton and was 1 m long. A rig built from scratch in Blender, with a jaw for the roar, and 11 in-place clips: idle, walk, charge, sense, sniff, paw, rest, roar, rear-up slam, skid, shake.
  • Behaviour: not an enemy, no health bar. It goes for the strongest Veyr it senses and ignores the Seed unless the Seed carries Veyr. Its charge barely steers, so a sidestep dodges it. It smashes corrupted Veyr for good, and a vessel the Seed fills lures it to a blocked passage, which its slam clears.
  • Checks: GuardianShowcase arena; guardian tests 4/4, grounding 3/3; a review found 15 issues, 13 fixed (the other two needed no change).

Slowed twice: iCloud had evicted 13,335 project files, then the shared Editor stalled at 11 of 16 GB. Commit 57b48f7.

19:30debug

Editor Play outside the main menu no longer overwrites the save

debugsave

Play started in the Editor from a region, Bootstrap or a test bench begins from an empty save, and its first seedbed used to overwrite the real save.json. Now, in the Editor, Play started anywhere but the main menu turns the debug save switch on by itself; Play from the main menu (New Game, Continue) saves as before. There's no on-screen badge for this case, so captures stay clean, and switching it off in the panel loads the real save. Checked in StalkerShowcase: a forced save left save.json untouched. Requested by Jeremy via the terrain session (C-SAVE-1). Commit 05524c8.

19:58history

Guardian polish: swaying moss, slam cracks, a first-witness scene

charactersguardiancinematics
The witness scene's wide shot: the Seed, the guardian and the corrupted node (pink)
The witness scene's wide shot: the Seed, the guardian and the corrupted node (pink)
The low shot after the slam, with the cracks bottom right
The low shot after the slam, with the cracks bottom right
At night the crystals and Veyr veins glow
At night the crystals and Veyr veins glow

Jeremy liked the three polish ideas the session proposed.

  • Moss that sways: the 24 longest hanging moss strands each got a bone. A new generic SpringBones swings them, so they lag, toss during a charge or shake, and settle.
  • Slam cracks: every slam leaves a crater with jagged cracks, a thin teal seam and a ring of soil dust; the cracks sink away after about 9 s. Shattering corrupted Veyr throws violet dust. The first version looked like drawn lines, so the cracks became wider and more broken and the seam dimmer.
  • First-witness scene: the first time the Seed sees it slam corrupted Veyr, about 5 s play: the Seed is held and turns to watch, a wide shot, slow motion across the impact, a low shot of the guardian shaking off the dust, a soft chord, then control returns. No on-screen text. Afterwards it warns longer before charging. The first low shot sat under its belly; fixed.

Done in its own worktree with one Editor turn. Guardian tests 5/5, grounding 3/3. Not yet tried: the scene from the normal game camera.

20:12terrain

A light pass toward the concept painting

looklighting
Before: bright and hazy (median brightness 0.63)
Before: bright and hazy (median brightness 0.63)
After: deeper midtones, ivory roots against darker greens (0.47)
After: deeper midtones, ivory roots against darker greens (0.47)

Five light settings tried live in Play against fixed viewpoints. Less ambient fill and haze, a stronger warmer sun, more contrast, and the ground's greens a step darker so the roots read as the brightest mass.

  • Lookout median 0.63 → 0.47, near-black 1% → 7%, spread 0.39 → 0.46 (the painting: 0.37 / 7% / 0.46)
  • Shadow sides are now very dark; baked bounce light (probe volumes) is the next thing to try.
20:59moss

Test benches: turn the camera without closing the panel

toolstesting

Jeremy couldn't move the camera with the mouse in the showcase scenes. The benches that use the real Seed turned the game's mouse look off whenever the settings panel was open, and the panel starts open, so only Tab brought the camera back. Now you can hold the right mouse button outside the panel and drag to turn the camera; the panel stays usable and Tab still hides it. Cutscenes (Moss's meeting, the guardian's witness scene) keep control of the camera while they play. One shared helper, ShowcaseMouseLook, serves all four benches. Also settled today: Moss is they / them.

21:00terrain

Mist with depth in the chasm

lookmistshaders
Before: flat mist sheets read as a milky disc
Before: flat mist sheets read as a milky disc
After: a ray-marched mist volume
After: a ray-marched mist volume
From inside the chasm, looking across
From inside the chasm, looking across

A new shader marches each pixel's view ray through the chasm, gathering mist from drifting 3D noise that thickens toward the bottom and glows toward the sun. The trunk now sinks into deepening mist and the walls fade with depth. The first version was a uniform orange wash; tuned live to a pale, cool mist.

21:25history

Where the guardian will live, and how it treats the Seed

guardianworldnavigation

Jeremy said the guardian work wasn't finished. Its last step, placement in a region, needed three of his decisions; he took the recommended answer to each:

  • Region: the Heartroot's outer terrace (south-west), between the arrival path and a stream. A placement tool sets up its home, a corrupted node on open ground, corrupted growth choking the stream with a well and vessel beside it, and a hidden corrupted node.
  • Navigation: a new "Large creature" NavMesh agent type (radius 2.4 m, height 6 m), so it keeps off narrow bridges and out of doorways.
  • Relationship: once the Seed has watched it smash, it follows a charged Seed at about 14 m instead of charging, and attacks only if the Seed stays within 10 m. Its sniffing reveals hidden corrupted Veyr (saved), and it patrols the sites it cleared.

A review found real bugs, all fixed: an interrupted roar could leave it ready to charge a Seed that kept its distance, and the stream vessel stood 7.7 m from the node feeding the growth, outside the 7 m slam. The code is ready, but nothing is placed in the Heartroot scene yet. Commits a29b36a, 73961e8.

21:40history

The guardian hovered 40 cm

guardiannavigationgroundingtests
Before: the paws float well above the arena floor
Before: the paws float well above the arena floor
After the height mesh: the hover is gone; some paws are still 4-10 cm up
After the height mesh: the hover is gone; some paws are still 4-10 cm up

The Large creature agent type went in (slope 30°, step 1 m), with its own NavMesh bake in the arena; guardian tests 7/7 (c2195f3). Then Jeremy saw the guardian hovering above the ground and asked why the pipeline hadn't caught it. Three gaps lined up:

  • the coarse large-agent NavMesh floats about 40 cm above the ground;
  • GroundSnap, which lowers models onto the real ground, gave up beyond 35 cm;
  • the grounding test read only the single lowest point, so one touching claw passed while the other paws floated.

Fix (3ba7f2f): large-creature bakes build a height mesh, the guardian's GroundSnap corrects up to 0.8 m, and a new per-foot check fails any standing creature with a foot more than 8 cm off. It runs in the arena and the Heartroot, not only on a test floor.

Still open: the 40 cm hover is gone, but the per-foot check now finds paws 4-10 cm up while standing, because the paws are modelled at different heights. Planting each foot in Blender comes next.

21:51terrain

Ferns and fuller trees

worldvegetation
Ferns along a stream bank below a cascade
Ferns along a stream bank below a cascade
A fern up close on the forest floor
A fern up close on the forest floor
The forest before: thin crowns
The forest before: thin crowns
After: fuller crowns
After: fuller crowns

Ground ferns from a free (CC0) Poly Haven fern model, made into three ferns with three detail levels. They clump on the forest floor and, in the settlement, along the stream banks, at the foot of each terrace bank and at the chasm edge. Tree crowns got about 1.8 times the leaf cards.

  • 60,654 plants now (about 47,000 ferns); 5.05 million triangles and about 10.5 ms per frame on the CPU (+1.5 ms).
  • A trap on the way: the fern texture, given a bare .meta outside the Editor, imported as a cubemap and every fern came out white. The import rule now forces flat textures.
22:08storm

Storm Capacitor: rigged and animated from a bare Tripo pod

Front view, left to right and top to bottom: Young, Dormant, Charging, Active; Discharge gather, burst, recoil; Absorb swell.
Front view, left to right and top to bottom: Young, Dormant, Charging, Active; Discharge gather, burst, recoil; Absorb swell.

The Tripo seed pod came with no rig. It now has a stem, a pod axis, a curled crown, a pulsing core and eight lobe chains around the bulb (ArtSource/StormCapacitor_rig.py). Opening a lobe tilts its base outward while its top curls back, so the bulb keeps its height instead of flattening. Six clips (StormCapacitor_clips.py): Dormant, Charging and Active loops that the game blends by stored charge, a closed Young pose for the young-plant stand-in, and Discharge (gather, burst, recoil) and Absorb one-shots. No clip goes below the floor (the roots are rigid to the ground).

22:25storm

Storm Capacitor in the game: storms charge it, discharges clear corruption

Dormant: looks diseased and inert, nothing glows.
Dormant: looks diseased and inert, nothing glows.
Under the storm: charging, the chamber lights through the shell.
Under the storm: charging, the chamber lights through the shell.
A guided discharge relays from the near plant to the relay plant (slowed down).
A guided discharge relays from the near plant to the relay plant (slowed down).
The stand on the Heartroot's outer ring.
The stand on the Heartroot's outer ring.

The plant is in Unity with its own storm. A local storm cycles calm, gathering, storm and clearing: the sky darkens, rain falls, and lightning strikes the plants. A plant goes from dead-looking (Dormant) to glowing violet and blue (Charging, Active). When full it discharges: arcs shatter corrupted Veyr within 16 m and pass the energy to plants within 24 m, which can discharge in turn. A natural discharge shocks a Seed within 7 m. The Seed can Connect to a plant to set off a guided discharge that spares it, and through a chain of plants clear a passage far away. A small stand now grows on the Heartroot's outer ring. All rules are provisional. Tests: StormCapacitorTests 6/6.

22:30moss

Moss answers the d-pad

companioncontrols

Jeremy found the d-pad did nothing for Moss. I recorded his controller's raw signal outside Unity and it was read correctly; the d-pad reached Moss too. The real fault was Seek (d-pad down / G): it sent Moss to whatever the camera's aim line hit, and with a level camera that is a wall, the top of a ridge, or nothing, so Moss almost always shook their head ("can't get there"). Now Moss goes to the first ground under the aim that they can actually walk to. The on-screen prompts also show d-pad icons now instead of text, and a new test presses the d-pad on a virtual gamepad.

22:40terrain

Stall organisms and karst-tower hills

worldsettlementheartroot
A stall organism at the plaza's corner: a root knot, a bowl of glowing pods, a leaf canopy
A stall organism at the plaza's corner: a root knot, a bowl of glowing pods, a leaf canopy
The stall street on the middle terrace at dusk
The stall street on the middle terrace at dusk
The four stall variants straight out of the Blender generator
The four stall variants straight out of the Blender generator
The basin with karst-tower hills, the forest climbing them
The basin with karst-tower hills, the forest climbing them

The design calls for nutrient-growing stall organisms clustered near the arrival. A new Blender generator grows four variants: a braided root knot rising into a counter bowl of glowing nutrient pods, with roots arching overhead into a leaf canopy. Ten of them now line the arrival path down the middle terrace and the plaza's corners (proposals).

  • The rounded green hills became karst towers: uneven outlines, a steep rock wall to about 70% of the height, then a forested top. The forest now climbs them.
  • The region builder now calls the Guardian's and the Storm Capacitor's placement tools on every rebuild, so their work survives mine.
  • A Unity trap: a model's parts come in the order each material first appears, not Blender's slot order - the canopy first got the pods' glow.
  • Still to do: the stalls read small and dark at dusk, and the forest view is too dark.
22:52gait

Feet that roll instead of rock

The Seed running on the grounding-test floor in Play with the new clips (mid-stride)
The Seed running on the grounding-test floor in Play with the new clips (mid-stride)
The Glimmer Grazer running with the rolled hoof keys
The Glimmer Grazer running with the rolled hoof keys

Running characters used to push a toe through the floor at every touchdown and a heel at every lift-off. The animation generator tilted each planted foot around a point at the ankle end of the foot, so the sole rocked through the ground and its contact point slid. Now a planted foot rolls on its sole: on the heel until mid-step, then on the toe, the way a real foot does. The generator reads where the heel and toe are from the model itself, so this works for the Seed, Moss, the Stalker's claws and the Grazer's hooves alike. Measured by the grounding tests in Play: the Seed's run dipped 5.2 cm into the floor, now 1.8 cm; the Grazer's run 4.4 cm, now 0.2 cm; the Grazer's hooves slid 52-64% of its speed while planted, now 15-19%. Both are off the known-issues list. Ground speeds did not change, so the drivers needed no retuning.

23:15guardian

The guardian stands on slopes and walks on rolling feet

Resting on the Heartroot's outer terrace after smashing the open-ground node: all four paws on the ground (Play mode)
Resting on the Heartroot's outer terrace after smashing the open-ground node: all four paws on the ground (Play mode)

On the Heartroot terrace the Misunderstood Guardian's right rear paw hung about 10 cm in the air: its 11 m body stayed level while the ground under it sloped. Its ground snap now reads the ground under all four paws and tilts the body to match. Its walk and run were re-made with the new rolling feet (heel first, then toe), with the foot angle reversed so the toes no longer dig in: now within about 3 cm in both. The sniffing pose also keeps the body level now, because raising it lifted the front paws. All arena, grounding and Heartroot paw tests pass (12/12). On twisted ground one tilt can't fit all four paws, so a few cm per paw remain there.

23:29gait

Animation research, and the first things it caught

Moss's look-around at its highest point: before (left) the foot stays flat and the leg locks straight; after (right) the heel lifts and the knee keeps a soft bend
Moss's look-around at its highest point: before (left) the foot stays flat and the leg locks straight; after (right) the heel lifts and the knee keeps a soft bend

Jeremy asked what the industry does that we don't. A survey of game animation practice (GDC talks, Unity and Unreal docs, studio write-ups) compared against our own setup is in Docs/research/2026-09-30-animation.md. Much of it we already do: clips matched to the real speed, lean into turns, head look, feet that roll on the floor. The gaps became work. First, a new automatic check for every animation clip (loop seams, sudden jerks, the body creeping, when each foot lands). Its first run over all seven characters' 95 clips found Moss's look-around: Moss 'rises on its toes', but only the body rose, so both knees snapped straight and the feet left the floor. Now the heels lift and Moss rolls up onto its toes. Second, the Stalker played a footstep sound on every frame while backing away, because the footstep code assumed animations only play forwards; footsteps now follow the moments each foot actually lands, in either direction. Tests: 50 of 50, and the grounding test is now stricter (3 cm instead of 4). Next, approved by Jeremy: hit-stop in combat, feet that meet sloped ground, head look for the Seed and the Stalker, and turning on the spot with steps.