Docs: DR-023 enemy animation + Synty asset inventory; trim CLAUDE.md

DR-023 decision record (client-derived enemy animation, monster-mash roster,
EnemyRigTools, WaveSystem scale-fix, GUID-preserving rebuild) + session log +
Synty_Asset_Inventory (enemy-grade table + future-dev catalog for the 14 new
packs). CLAUDE.md: add the enemy-animation gotchas bullet and condense several
build-gotcha bullets back below pre-session size.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-06 23:30:26 -07:00
parent f4c861ee91
commit 0df0b45163
4 changed files with 163 additions and 14 deletions
@@ -0,0 +1,51 @@
---
title: Synty Asset Inventory — enemy picks + future-development catalog
type: reference
date: 2026-06-06
tags: [reference, synty, assets, enemies, environment, vfx, ui, roadmap]
permalink: gamevault/06-roadmap/synty-asset-inventory
---
# Synty Asset Inventory
Catalog of the Synty Polygon packs under `Assets/Synty/`, produced by the [[DR-023_Enemy_Animation_MonsterMash]] research pass. Purpose: (1) pick enemy characters, (2) note what each pack offers future development. **Enemy-grade = shares the unified Synty Polygon *Generic* skeleton** (`animationType:3` + Optimize-Game-Objects OFF) so the existing `AC_Polygon` / Base-Locomotion clips + the DR-022/DR-023 Rukhanka recipe drive it with **zero per-pack avatar work**.
## Character packs
| Pack | Characters | Rig | Enemy use |
|---|---|---|---|
| **PolygonSciFiSpace** | the player soldier + aliens (`SM_Chr_Alien_01`), `BR_BigAlien`, robots | Generic ✓ (player reference) | Already the player. Aliens/BigAlien = a ready sci-fi enemy faction. |
| **PolygonSciFiCity** | 20 cyberpunk humanoids: Robot, CyborgNinja, Android, Cyber, Alien, Muscle, Junky/Garbage | Generic ✓ (verified) | **Best next enemy faction** — one skeleton + one atlas → one material; on-theme. |
| **PolygonKaiju** | 4 giant monsters (+40 recolor skins) | Generic core ✓ (extra Jaw/Eye bones idle; tail = separate attach) | **Brute/boss** (in use: Kaiju_01 = Brute). Reskin alts = cheap variants. |
| **PolygonWerewolf** | 1 werewolf + undead variant | Generic ✓ | **In use** (Grunt + Swarmer). Eye-glow mat; 21 biped attachments + 2 axes reusable. |
| **PolygonMech** | humanoid pilots (drop-in); the mech walker | pilots Generic ✓; walker = bespoke rig | Pilots = drop-in Grunts. Walker = its own rig (separate task). |
| **PolygonGeneric** | Robot / Skeleton / **Charred** (a literal "husk") | Generic ✓ | Charred/Robot/Skeleton are sci-fi-adjacent enemy options. |
| **PolygonDog** | Hellhound / Wolf / Zombie / Robot / SciFi dogs | **custom quadruped** (`animationType:2`, ships its own clips) | The ideal quadruped **Swarmer** — but a SEPARATE Rukhanka rig + dog controller (independent track, not drop-in). |
| PolygonFantasyHeroCharacters / Vikings / Western / DungeonRealms | fantasy/western humanoids (DungeonRealms has Undead Knight + Demon) | Generic ✓ / likely ✓ | Off-theme for sci-fi; fine as test beds or a themed mode. |
## Environment / world (no characters — map + set-dressing)
- **PolygonNatureBiomes** (highest env value): **Arid_Desert** reads as an off-world expedition site out of the box (satellite, solar panels, turbines, beacons, artefacts, even a **Warpgate**) — perfect for the **+1000 expedition region** ([[DR-013_M6_Aether_Cycle_Region_Split]]). Meadow_Forest (ships a post-process volume) for the home base.
- **PolygonNature**: trees/rocks/foliage/water + skybox — terrain dressing, harvestable-node visuals.
- **PNB_Core**: shared sky/weather/water/aurora atmosphere (rain/snow/fog FX, skydome, 9 ShaderGraphs — verify under URP 17.4; use the skydome in the classic-URP cosmetic root, not baked EG meshes).
- **PolygonKaiju** (env half): 23 buildings incl. **pre-destroyed** highrises/blocks + 40 props (cars, boats, bridge+rubble, neon signs, powerlines, crane, jet, missile) — a ruined-city map + FX meshes (bullets/fire/stomp).
- **PolygonFantasyKingdom**: castle/base kit + 15 **siege engines** → turret/structure visuals. **PolygonGeneric** `Models/Base`: sci-fi wall/floor kit with destroyed variants → buildable structures. **PolygonWestern/DungeonRealms/Carnival**: modular building kits, props.
## FX / props
- **PolygonParticleFX** (high presentation value): ~180 ready FX prefabs — muzzle/tracer/shell-eject (player gun + turret), impacts, **BloodSplat/Explosion_Body/GoreChunk** (Husk-death VFX), pickup/heal/levelup/coins (resource & goal feedback), **Portal** (expedition gateway), fire/smoke/dust (base-damage & machine ambience). Wire into `VFXConfig`/`CombatFeedbackSystem` as client-only cosmetics; strip any self-propelling Rigidbody/colliders; verify mats under URP.
## UI / icons
- **PolygonIcons**: 520 3D icon meshes (Network/Error/Node, Gear/Volume, Play/Pause, Heart/Shield, Crafting/Coin/Ingot, Skull/Star) — need a render-texture→sprite bake to use as UITK background-images.
- **InterfaceSciFiSoldierHUD**: a UGUI kit (does NOT drop into the code-built UITK HUD) — **harvest its sprites/fonts**: Icons_Status (Health/Armor/Oxygen/Radiation), Icons_Resources (125), Icons_Weapons/Ammo, HUD 9-slice panel skins, 3 cursors (for `AimReticleSystem`), Exo 2.0 + Orbitron fonts.
- **InterfaceCore**: UGUI dependency base — harvest 631 input-prompt glyph PNGs (Icons_Input) for scheme-correct (KBM/Gamepad) control hints.
- **SyntyPackageHelper / AnimationBaseLocomotion**: import helper (no game content) / the shared `AC_Polygon_*` controllers + locomotion clips already driving every Generic-Polygon character.
## Roadmap hooks (from this catalog)
1. **Cyberpunk enemy faction** — add PolygonSciFiCity meshes as new wave variants on the exact DR-023 pipeline (one new material + variant-table rows in `EnemyRigTools`).
2. **Quadruped Swarmer** — PolygonDog as a separate Rukhanka rig + dog controller (its bundled Attack/Death clips enable a real death anim).
3. **Ruined-city / off-world maps** — Kaiju destroyed-city + NatureBiomes Arid_Desert for the base + expedition regions.
4. **Combat/heal/portal VFX** — wire PolygonParticleFX into `VFXConfig`.
5. **HUD polish** — bake PolygonIcons + harvest Interface* sprites/fonts into the UITK HUD ([[DR-021_HUD_UITK_BuildPalette]]).
@@ -0,0 +1,45 @@
---
date: 2026-06-06
type: session
tags: [session, animation, rukhanka, synty, enemies, netcode, presentation, dots, slice, inventory]
---
# Session 2026-06-06 — Enemy animation (Rukhanka, client-derived), Slice 2 + Synty inventory
## Goal
Operator (via `/dots-dev`, ultracode): *"Extend the animation system to encompass enemies. I've added a ton more Synty assets — inventory them and pick the enemies to use. Note other assets for future goals and development."*
## Process
- **Research/inventory Workflow (≈22 read-only agents):** one agent per Synty character pack + batched env/FX/UI packs (→ structured inventory), 3 agents mapping the enemy + animation code surface, 2 context7/source agents confirming the Rukhanka enemy extension, 1 synthesis. Key finding: a Husk = an ownerless interpolated ghost = structurally a **remote player**, which DR-022's `RemoteDriveJob` already animates from `LocalTransform` deltas → the enemy drive is that path generalized; **no new netcode surface**.
- **Clarifying gate (AskUserQuestion):** roster = **monster-mash** (Werewolf=Grunt, Werewolf-Undead=Swarmer, Kaiju=Brute); scope = **locomotion + attack telegraph** (death-anim deferred).
- **Plan → approval → execution**, serialized through the one live editor (research fanned out in parallel; all Unity mutations done inline, validating each).
## Done
- **Inventory + picks** → [[Synty_Asset_Inventory]] (enemy-grade table + future-dev catalog: env/FX/UI/build). All enemy picks confirmed Generic + Optimize-OFF (drop-in for the DR-022 recipe).
- **Reusable `EnemyRigTools` editor tool** (`ProjectM/Animation` menu) — 3 idempotent steps building the materials, `AC_EnemyTopDown` + `EnemyAttackWindup.anim`, and the 3 rigged prefabs via real `PrefabUtility` C# (mirrors the DR-022 player recipe; `RigDefinitionAuthoring` via reflection). **GUID-preserving** in-place rebuild so re-runs never orphan the subscene refs.
- **`EnemyAnimationDriveSystem`** (client-only, mirrors the remote-player path): `[WithAll(EnemyTag)]`, velocity from `LocalTransform` delta (prevPos cache + per-frame prune, NOT `[RequireMatchingQueriesForUpdate]` so the prune always runs — Husks die often), facing from `LocalTransform.Rotation`, maxSpeed from baked `EnemyStats`, `IsAttacking = AttackWindup != 0`. Reuses `AnimParamMath.LocomotionParams`; added `AnimParamMath.PlanarForward` (+4 EditMode tests). No new `[GhostField]`, no server change, no asmdef change.
- **3 animated prefabs**: `EnemyWerewolf` (Grunt 0.8), `EnemyWerewolfUndead` (Swarmer 0.6, green-tinted undead skin), `EnemyKaiju` (Brute 1.3). Wired into the gameplay subscene `WaveDirector.EnemyPrefabs[]`. Capsule prefabs kept as pristine ghost templates.
- **`WaveSystem` Scale-clobber fix**: spawner used `LocalTransform.FromPosition` (resets Scale→1, a replicated `[GhostField]`) → now `baked.WithPosition(pos)` preserves the variant scale.
## Validation (runtime, focused editor, real Server+Client)
- EditMode **204 → 208** (+4 PlanarForward). Clean compile; console clear; **no "does not support skinning"**.
- Spawned one of each variant (debug-RPC `SpawnWave`, then direct server instantiate from the prefab pool). All 3 replicate to the client with the **Rukhanka rig + 5-param buffer** and **correct baked scales** (0.80/0.60/1.30 — scale-fix works through replication).
- **Drive proven via live param sampling:** moving Husk `MoveZ≈Speed≈0.95` (forward run); stopped-at-player `Speed=0`; winding-up `IsAttacking=true` — locomotion + telegraph both correct.
- **Feet-on-ground (`WorldRenderBounds`):** werewolves feetY ≈ 0.05/0.04 ✓; Kaiju measured 0.32 sunk → Root-Y 0.77 → **0.52** (GUID-preserving re-run kept the subscene refs). Kaiju eyeball on next Play.
## Gotchas captured
- `WaveSystem.FromPosition` Scale-clobber (above). `DeleteAsset+CopyAsset` mints new GUIDs → orphans subscene refs (→ GUID-preserving in-place rebuild). `execute_code`: no `using`/aliases; `GetComponentData`/`GetBuffer` ambiguous via reflection (use concrete generics); `Rukhanka.ParameterValue` is a union (read `floatValue`/`boolValue`). → folded into [[DR-023_Enemy_Animation_MonsterMash]] + CLAUDE.md.
## Next-session intent
- Eyeball the Kaiju feet/scale in a natural Play; per-SMR eye-glow material.
- **Death animation** (deliberate netcode change: `Dead` enableable from replicated Health + delayed despawn) so a death clip plays before despawn.
- **Cyberpunk enemy faction** (PolygonSciFiCity) as new wave variants on this exact pipeline — one material + `EnemyRigTools` variant rows.
- Wire **PolygonParticleFX** (blood/gore/explosion) into `VFXConfig` for richer Husk-death VFX.
See [[DR-023_Enemy_Animation_MonsterMash]].
@@ -0,0 +1,56 @@
---
id: DR-023
title: Enemy skeletal animation (Rukhanka, client-derived) — Slice 2, monster-mash roster + EnemyRigTools
status: accepted
date: 2026-06-06
tags:
- decision
- animation
- rukhanka
- synty
- netcode
- enemies
- presentation
- dots
permalink: gamevault/07-sessions/decisions/dr-023-enemy-animation-monster-mash
---
# DR-023 — Enemy skeletal animation (Rukhanka + Synty), client-derived — Slice 2
## Context
[[DR-022_Animation_Pipeline_Rukhanka_Synty]] brought the **player** alive (Synty SciFiSpace soldier, Rukhanka 2.9, client-derived, netcode-OFF). Its roadmap named enemies next. Operator (via `/dots-dev`, ultracode): *"extend the animation system to encompass enemies. I've added a ton more Synty assets — inventory them and pick the enemies to use. Note other assets for future goals."*
Husks were primitive **capsules** (`Enemy`/`EnemySwarmer`/`EnemyBrute` prefabs = built-in capsule mesh, no skeleton). A Husk is an **ownerless interpolated ghost** (server-moved by `EnemyAISystem`, position+rotation via stock `LocalTransform` replication, no `KinematicCharacterBody` on the client) — i.e. structurally identical to a **remote player**, which DR-022's `PlayerAnimationDriveSystem.RemoteDriveJob` already animates from `LocalTransform.Position` deltas. So enemy animation is the remote-player path generalized — no new netcode surface.
A read-only research **Workflow** (≈22 agents) inventoried the new Synty packs, mapped the enemy + animation code surface, and confirmed the Rukhanka extension. Clarifying answers (AskUserQuestion): roster = **monster-mash**, scope = **locomotion + attack telegraph** (no death-anim this slice). Full inventory: [[Synty_Asset_Inventory]].
## Decision
1. **Roster (monster-mash), all on the unified Synty Polygon Generic skeleton (drop-in for the DR-022 recipe):**
- **Grunt** = PolygonWerewolf `SM_Werewolf_01` (scale 0.8).
- **Swarmer** = the same werewolf, **undead skin** (a green-tinted `_BaseColor` over the werewolf atlas), scale 0.6.
- **Brute** = PolygonKaiju `SM_Chr_Kaiju_01` (scale 1.3). Kaiju share the Generic CORE skeleton; their extra bones (Jaw/Eyes/Belly) just idle at bind pose; the tail is a separate attachment, omitted.
2. **Client-derived, netcode replication OFF — same doctrine as DR-022.** A new client-only `EnemyAnimationDriveSystem` (`[WorldSystemFilter(LocalSimulation|ClientSimulation)]`, `[UpdateBefore(RukhankaAnimationSystemGroup)]`, `SystemBase`) runs one Bursted `IJobEntity` `[WithAll(EnemyTag)]`: velocity from `LocalTransform.Position` frame-delta (per-`Entity` `prevPos` cache + per-frame prune), facing from `LocalTransform.Rotation` (`AnimParamMath.PlanarForward`, the server faces the target), maxSpeed from the baked-on-both-worlds `EnemyStats.MoveSpeed`, `IsAttacking = AttackWindup.WindUpUntilTick != 0`. Reuses `AnimParamMath.LocomotionParams` unchanged. **No new `[GhostField]`s, no `DefaultVariant` strip, no ghost-hash change, no asmdef change** (`ProjectM.Client` already refs Rukhanka + Unity.Physics); the server-side Rukhanka strip ([[DR-022_Animation_Pipeline_Rukhanka_Synty]]'s `ServerStripAnimationSystem`, matched by assembly name) already covers enemy rigs — **zero server change**.
- **Deliberately NOT `[RequireMatchingQueriesForUpdate]`**: Husks despawn far more often than players, so the prune must run every frame (even with zero live Husks) or the cache leaks one entry per kill.
3. **Attack telegraph rides the existing replicated `AttackWindup`** ([GhostField], non-zero for the ~0.3s wind-up, reset to 0 after the strike) → a clean idempotent `IsAttacking` bool, no tick comparison (the same value `CombatFeedbackSystem` already reads). Controller `AC_EnemyTopDown` forks `AC_PlayerTopDown` (identical `MoveX/MoveZ/Speed` 2D-Freeform locomotion tree so the math + job are shared) + an **Attack** state (`AnyState→Attack` on `IsAttacking`, `Attack→Exit` on `!IsAttacking`). The attack clip `EnemyAttackWindup.anim` is authored: a non-looping forward **pitch of the `Root` bone** (whole-body lunge) — pack-agnostic (Root exists on every Synty rig; locomotion clips don't key Root, so no conflict and it returns to its Y-offset on exit).
4. **Death stays as-is (instant despawn + `CombatFeedbackSystem` VFX) — deferred.** A death animation needs the entity to outlive `Health<=0`, i.e. a deliberate server change (a `Dead` enableable derived from replicated Health + a delayed despawn); scheduled separately so this slice keeps the netcode surface unchanged.
5. **A reusable `EnemyRigTools` editor tool** (menu `ProjectM/Animation`, namespace `ProjectM.EditorTools`, like `EnvArtTools`) builds the whole pipeline in three idempotent steps — materials, controller+clip, the 3 rigged prefabs — by mirroring the DR-022 player-rig recipe in real `PrefabUtility` C# (robust vs the enum/Vector-drop MCP prefab traps). `RigDefinitionAuthoring` is added by **reflection** (no Rukhanka asmdef ref — same "by name" tactic as the server strip). This is the "scalable/sustainable for a solo dev" artifact: a future enemy is one variant-table row + a re-run.
## Consequences (validated at runtime, Unity 6.4.7, real Server+Client)
- **EditMode 204 → 208** (+4 `AnimParamMath.PlanarForward` tests). Clean compile, console clear (no NRE, **no "does not support skinning"** → the deformation material skins correctly).
- Spawned one of each variant: all 3 replicate to the client carrying the **Rukhanka rig + param buffer** (5 params = MoveX/MoveZ/Speed + IsDead/IsAttacking), and **the baked variant scales survive** (0.80 / 0.60 / 1.30).
- **Drive proven by sampling the live param buffer:** a moving Husk reads `MoveZ≈Speed≈0.95` (forward run), a Husk stopped at the player reads `Speed=0`, and Husks in wind-up read `IsAttacking=true` — exactly the intended locomotion + telegraph.
- **Feet-on-ground via `WorldRenderBounds`:** werewolf Grunt/Swarmer feetY ≈ 0.05/0.04 (Root-Y 1.25 / 1.67); Kaiju measured 0.32 sunk → Root-Y corrected 0.77 → **0.52** (re-run preserved the prefab GUID, so the subscene refs held).
- **Files.** New: `EnemyAnimationDriveSystem.cs`, `EnemyRigTools.cs` (editor), `AC_EnemyTopDown.controller`, `EnemyAttackWindup.anim`, `M_Enemy_Werewolf_Animated` / `M_Enemy_WerewolfUndead_Animated` / `M_Enemy_Kaiju_Animated.mat`, `EnemyWerewolf` / `EnemyWerewolfUndead` / `EnemyKaiju.prefab`; `AnimParamMath.PlanarForward` (+4 tests). Modified: `WaveSystem.cs` (scale-fix, below), the gameplay subscene `WaveDirector.EnemyPrefabs[]` (→ the 3 new prefabs). Capsule prefabs (`Enemy`/`EnemySwarmer`/`EnemyBrute`) kept as pristine ghost-config templates.
## Findings / gotchas
- **`WaveSystem` Scale-clobber (fixed).** The spawner used `LocalTransform.FromPosition(pos)`, which resets Scale→1 — silently flattening every variant to scale 1 (Scale is a replicated `[GhostField]`). Now reads the prefab's baked `LocalTransform` and `WithPosition(pos)` (preserves Scale + rotation). A bug fix, not a netcode change.
- **GUID-preserving rebuild.** A naive `DeleteAsset + CopyAsset` mints a NEW GUID each run → orphans the subscene's `WaveDirector` refs. `EnemyRigTools.BuildOne` copies the template only when the output is absent, else modifies it **in place** (clear children + rig components, re-add) → stable GUID across re-runs. (The Materials/Controller steps still recreate assets — re-running those means re-pointing their consumers.)
- **`execute_code` reflection traps:** runs as a method body (no `using`/aliases; fully-qualify), and `EntityManager.GetComponentData`/`GetBuffer` are ambiguous via plain `GetMethod` (overloads) — use the concrete generic directly (it can reference project + Rukhanka types). The `Rukhanka.ParameterValue` value is a union — read `floatValue`/`boolValue`.
- **Kaiju Root-Y (0.52)** is a calculated correction from the measured 0.32 sink (werewolves were visually validated on-ground); eyeball on the next natural Play. Kaiju at scale 1.3 reads ~2.8 m tall — a proper Brute.
- Deferred: death anim (R3, needs the server `Dead`/delayed-despawn change); per-SMR eye-glow material (slice 1 sets every SMR slot to the one body deformation material); a second-faction roster (PolygonSciFiCity cyber enemies) is the natural next add on this exact pipeline — see [[Synty_Asset_Inventory]].
Builds on [[DR-022_Animation_Pipeline_Rukhanka_Synty]] (the recipe + server strip), [[DR-016_Stage_G_Combat_Gameplay]] (Husk/AttackWindup), [[DR-017_Persistent_Base_Player_Driven_Pacing]] (the player-driven Siege loop these enemies populate). Serves the "feel alive" goal.