add .agents/design/ — blog voice + rules for agents
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
# Blog design — agent rules
|
||||
|
||||
Design and content rules for agents writing tinqs/blog posts.
|
||||
|
||||
## Voice
|
||||
|
||||
- **Confident, shipped.** Every feature is presented as built, complete, and working. Never "we plan to," "we're working on," "next we'll."
|
||||
- **No evolution narrative.** Don't tell the "first we tried X, then we fixed Y" story. Present the final design as if it was the plan from day one.
|
||||
- **External audience.** Readers are game developers and technical audiences outside Tinqs. No internal project names, no team org charts, no "Ozan decided" or "Uygar built."
|
||||
|
||||
## What to never mention
|
||||
|
||||
- **Asset pack names or vendors.** Polyperfect, Low Poly Ultimate Pack, Quaternius, Kevin Iglesias, etc. Say "our animal models" or nothing at all.
|
||||
- **Unity import details.** FBX source format, `.anim` files, `.meta` clip ranges, `isleborn/` paths.
|
||||
- **Internal tooling specifics.** `migrate_animals.py` internals, Blender pipeline details, repo paths.
|
||||
- **Things that failed or were removed.** Failed migrations, broken assets, animals we deleted, bugs we shipped then fixed. If you must mention a bug, frame it as a design insight learned during development — never "we shipped this broken."
|
||||
- **Roadmaps, tiers, trade-offs, future plans.** The post describes what exists. No "Tier A/B/C," no "recommended build order," no "what's next."
|
||||
|
||||
## Structure
|
||||
|
||||
- **Title:** technical, specific, bold. "How We Made 1,000 Animals Animate Without a Single Skeleton" not "Crowd Animation Update."
|
||||
- **Opening:** state the problem (what stock Godot can't do), state our solution, give the numbers.
|
||||
- **Body:** architecture, shader code, data flow, VRAM math, benchmarks. Ground everything in numbers.
|
||||
- **Closing:** where to get it, what it drives (Ariki), related posts. No roadmap.
|
||||
|
||||
## Numbers rule
|
||||
|
||||
Every claim about performance, VRAM, or scale must cite a measured number. "60 FPS at 1,000 agents on M1 Pro" not "great performance." "1.6 MB per type" not "tiny VRAM."
|
||||
|
||||
## Code
|
||||
|
||||
Shader code and architecture diagrams are encouraged — this is a technical blog. But keep code blocks focused on the key insight, not the whole file.
|
||||
@@ -357,7 +357,7 @@ NORMAL = normalize((skin * vec4(NORMAL, 0.0)).xyz);</code></pre>
|
||||
<h2>What's driving it</h2>
|
||||
<p>In <a href="https://www.arikigame.com" style="color: var(--c-lime);">Ariki</a>, the sim tracks animal migration across a 12km archipelago. <code>AnimalHerdRenderer.cs</code> groups sim <code>ViewerState.animals</code> by type, feeds world positions and yaw rotations to <code>skinned_herd.gd</code> — the reusable per-type herd backend. The herd bakes the palette once at setup, then <code>set_positions()</code> updates transforms each sim tick. <code>set_clip_for_state()</code> switches the active clip block in the custom data when the sim FSM changes state. <code>set_speed_scale()</code> adjusts the per-instance playback rate to match ground speed — feet stay planted.</p>
|
||||
<p>The sim owns all behavior — 30 data-driven animals with per-animal senses, diet, combat stats, and FSM states (graze, drink, sleep, hunt, flee, scavenge, die). The client just renders. This is the same code in single-player and multiplayer — the sim is the host.</p>
|
||||
<p>Bird flocks use the same system. <code>BirdFlock.cs</code> runs boid flocking on top of <code>skinned_herd</code>, sharing the palette with synchronized phases (airborne flapping in unison is intentional). 25 bird species migrated from the Low Poly Bird Ultimate Pack, each a single draw call.</p>
|
||||
<p>Bird flocks use the same system. <code>BirdFlock.cs</code> runs boid flocking on top of <code>skinned_herd</code>, sharing the palette with synchronized phases (airborne flapping in unison is intentional). 25 bird species, each a single draw call.</p>
|
||||
<p>Per-instance custom data means a walking Boar, a running Boar, an idle Boar, and an attacking Boar all share the same baked palette — they just point at different rows. The renderer groups by type, not by state. One palette, one draw call, any number of states.</p>
|
||||
<h2>Two bugs we shipped and fixed</h2>
|
||||
<p>The module had data-plane doctests from day one — round-trip pose get/set, dirty tracking, size clamping, AABB, column-major layout. All green. Then we put it on screen and two things were wrong.</p>
|
||||
@@ -369,7 +369,7 @@ NORMAL = normalize((skin * vec4(NORMAL, 0.0)).xyz);</code></pre>
|
||||
<p>Engine version: <strong>4.6.5.</strong></p>
|
||||
<p>No C# wrapper is generated — instantiate from GDScript via <code>ClassDB.instantiate()</code> and call the bound methods. The binding surface is small and stable. See <code>ariki-game/scenes/animals/skinned_herd.gd</code> for the reference backend.</p>
|
||||
<h2>The production pipeline</h2>
|
||||
<p>The <code>migrate_animals.py</code> tool converts polyperfect FBX packs to game-ready GLBs — imports, cleans hierarchy, rebuilds named NLA clips from frame ranges, strips duplicate meshes, bakes into the flat <code>assets/models/glbs/</code> directory. Each animal gets a catalog entry in <code>animals_catalog.json</code> with clip metadata, default state mapping, and an <code>animSpeedRef</code> for foot-sync.</p>
|
||||
<p>The <code>migrate_animals.py</code> tool converts source FBX files to game-ready GLBs — imports, cleans hierarchy, rebuilds named NLA clips from frame ranges, strips duplicate meshes, bakes into the flat <code>assets/models/glbs/</code> directory. Each animal gets a catalog entry in <code>animals_catalog.json</code> with clip metadata, default state mapping, and an <code>animSpeedRef</code> for foot-sync.</p>
|
||||
<p>At runtime, <code>AnimalHerdRenderer</code> spawns one <code>skinned_herd</code> per animal type. The herd bakes the palette from the catalog GLB's clips. <code>AnimalAnimationLogic</code> maps sim FSM states to clip keywords (attack → "attack"/"bite", flee → "run"/"gallop", wander → "walk"). The renderer lerps positions between sim ticks for smooth motion and writes per-instance custom data each frame. Zero per-frame CPU on the animation path.</p>
|
||||
<h2>Where we stand vs the industry</h2>
|
||||
<p>The bone-matrix palette technique is the same architecture used by Assassin's Creed Unity, Total War: Warhammer, and Hitman for their crowd systems. We're using the same core idea, in a Godot fork, with smaller VRAM — our low-poly animals keep textures tiny.</p>
|
||||
|
||||
|
Before
After
|
@@ -125,7 +125,7 @@ In [Ariki](https://www.arikigame.com), the sim tracks animal migration across a
|
||||
|
||||
The sim owns all behavior — 30 data-driven animals with per-animal senses, diet, combat stats, and FSM states (graze, drink, sleep, hunt, flee, scavenge, die). The client just renders. This is the same code in single-player and multiplayer — the sim is the host.
|
||||
|
||||
Bird flocks use the same system. `BirdFlock.cs` runs boid flocking on top of `skinned_herd`, sharing the palette with synchronized phases (airborne flapping in unison is intentional). 25 bird species migrated from the Low Poly Bird Ultimate Pack, each a single draw call.
|
||||
Bird flocks use the same system. `BirdFlock.cs` runs boid flocking on top of `skinned_herd`, sharing the palette with synchronized phases (airborne flapping in unison is intentional). 25 bird species, each a single draw call.
|
||||
|
||||
Per-instance custom data means a walking Boar, a running Boar, an idle Boar, and an attacking Boar all share the same baked palette — they just point at different rows. The renderer groups by type, not by state. One palette, one draw call, any number of states.
|
||||
|
||||
@@ -149,7 +149,7 @@ No C# wrapper is generated — instantiate from GDScript via `ClassDB.instantiat
|
||||
|
||||
## The production pipeline
|
||||
|
||||
The `migrate_animals.py` tool converts polyperfect FBX packs to game-ready GLBs — imports, cleans hierarchy, rebuilds named NLA clips from frame ranges, strips duplicate meshes, bakes into the flat `assets/models/glbs/` directory. Each animal gets a catalog entry in `animals_catalog.json` with clip metadata, default state mapping, and an `animSpeedRef` for foot-sync.
|
||||
The `migrate_animals.py` tool converts source FBX files to game-ready GLBs — imports, cleans hierarchy, rebuilds named NLA clips from frame ranges, strips duplicate meshes, bakes into the flat `assets/models/glbs/` directory. Each animal gets a catalog entry in `animals_catalog.json` with clip metadata, default state mapping, and an `animSpeedRef` for foot-sync.
|
||||
|
||||
At runtime, `AnimalHerdRenderer` spawns one `skinned_herd` per animal type. The herd bakes the palette from the catalog GLB's clips. `AnimalAnimationLogic` maps sim FSM states to clip keywords (attack → "attack"/"bite", flee → "run"/"gallop", wander → "walk"). The renderer lerps positions between sim ticks for smooth motion and writes per-instance custom data each frame. Zero per-frame CPU on the animation path.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user