Docs: DR-052 SoD facing + underwater feel — build spec, session log, gotchas archive 07-16, skinned-kit cookbook recipe, CLAUDE.md facing contract (net-zero condensations)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
---
|
||||
title: 2026-07-16 — SoD facing, underwater feel, Bathynaut kit in-engine
|
||||
date: 2026-07-16
|
||||
links: "[[DR-052_SoD_Facing_Underwater_Feel]], [[Facing_Underwater_Feel_Build_Spec]], [[DR-051_Lantern_Realignment_Purge]], [[Art_Direction_Lantern]]"
|
||||
---
|
||||
|
||||
# 2026-07-16 — SoD facing, underwater feel, Bathynaut kit (movement/animation tuning session 1)
|
||||
|
||||
Operator brief (07-15, /art-dev + /dots-dev): get the lamp/helmet/etc. in-engine on the character; kill rotate-to-cursor — facing "exactly like Shape of Dreams"; heavier underwater movement; heavier animation + footstep sounds; gap-analyze the movement+animation combo.
|
||||
|
||||
Forks locked by operator: facing model (a) move-facing + cast-turn + idle-hold · suit = dome+tank+shoulder lamp, **no chest beacon** · **keep cursor-dash** (stationary dash/blink fallback Move→Aim→facing) · **dome + bare male head** (Synty sci-fi helmet removed).
|
||||
|
||||
## Shipped (working tree, uncommitted — 22 modified + new files)
|
||||
|
||||
### B — Shape-of-Dreams facing (the rework, as corrected by the pre-code review)
|
||||
- **Pre-code design review `wf_a13d4395-1c7`** (3 lenses → 25 findings → 21 confirmed / 3 refuted / 1 critic died on session-limit; the dead critic's ordering claim was independently refuted by a sibling with full code evidence). **Blocking catch: fire directions did NOT read raw Aim — every archetype + the melee cleave aimed from `PlayerFacing`**; under move-facing every skillshot would have fired along the travel direction. The Build Spec ([[Facing_Underwater_Feel_Build_Spec]]) records all verdicts.
|
||||
- `FacingMath` (new, Simulation): `ResolveAim` (THE fire-direction resolver: Aim→facing→+Z), `SelectTarget` (shared Aim→Move→hold cascade; castActive grants Aim *priority* only — a resting gamepad stick mid-cast falls to Move), `RotateToward` (verbatim extraction).
|
||||
- `TickWindowMath` (new, Simulation/Combat): `SwingActive`/`FireActive`/`SocketFireAndCone` moved verbatim from PlayerAnimationDriveSystem (window length stays a caller param) + **Movement-archetype skip** — a blink is a dodge, never a cast; also kills the latent cooldown-tail phantom `IsFiring` pulse. Anim path now shares the same code.
|
||||
- `PlayerAimSystem`: castActive = melee swing ∪ non-Movement socket windows; locomotion rate = stat (360°/s), cast rate = const 1080°/s, both knob-overridable; **no `IsFirstTimeFullyPredictingTick` gate** (incremental integrator over the snapshot-restored [GhostField] — documented in header); `[UpdateAfter(MeleeComboSystem)]` hygiene pin (cycle-free, verified; sorter tie-breaks are deterministic cross-world so this pins semantics, not a divergence fix).
|
||||
- **Fire-direction decoupling**: AbilityFireSystem (cone :174 / aoe :209 / hitscan :242 / projectile+assist-seed :279) + MeleeComboSystem cleave (:163) → `FacingMath.ResolveAim(input.Aim, facing)`. PlayerFacing = body yaw only. Windup keeps current-tick Aim (existing contract).
|
||||
- Presentation coupled to the damage source: AimReticleSystem gamepad ring + lock-on tether, CombatFeedbackSystem local socket cue + melee arc (remotes stay facing-based — Aim isn't replicated to non-owners, documented asymmetry).
|
||||
- DashSystem/BlinkSystem stationary fallback → Move→**Aim**→facing (cursor-dash kept).
|
||||
|
||||
### C — Heavier underwater movement
|
||||
- `DefaultGroundedSharpness` 15→6 — **all coupled sites together** (const + Player.prefab serialized value + 4 DashSystemTests assertions → const refs; the review confirmed DashSystem's every-tick restore stomps a prefab-only edit within one tick).
|
||||
- `Character_Default.asset` TurnRateDegreesPerSec 720→360 (the asset, not just the initializer).
|
||||
- TuningKnob 26/27/28 (`TurnRateDeg`/`CastTurnRateDeg`/`MoveSharpness`), Count=29, **0 = no-override sentinel** (explicit ClampKnob case ≥0 — the default ≥1 branch would force a 1-unit override); all six maps + `DebugTuningReport` fields extended (**dev-protocol bump**: a stale standalone dev build will refuse the handshake vs a fresh peer — expected, rebuild both) + DebugOverlay rows + TuningConfigTests pin. Sharpness knob honored at all 4 restore sites (dash tail/idle, blink, death).
|
||||
|
||||
### D — Heavier animation + footsteps
|
||||
- Stride-distance footsteps (`FootstepStrideMeters` 1.5m; accumulate planar travel → cadence tracks the drifting velocity), 3 deep-thud clip variants (95–120Hz noise sweeps, decay 7) + ±12% volume jitter + a dark silt puff at the feet (new `SiltPuff` pool emitter). Old fixed-interval timer + `FootstepIntervalSec` deleted.
|
||||
- `RetimeLocomotionGait` (PlayerRigTools): Locomotion blend-state speed 0.85, idle⇄locomotion blends 0.25s (AnimatorController API).
|
||||
|
||||
### A — Bathynaut kit in-engine (art track)
|
||||
- Blender: 33 kitbash pieces rigid-skinned to Head/Spine_03/Clavicle_L (vgroup w=1 + armature modifier, saved in the master), joined per shader role → `SM_Suit_KitBrass` (940 tris) + `SM_Suit_KitGlow` (104: porthole glass + lamp lens), FBX to `ArtSource/Blender/`. ChestLamp + LampRim excluded per fork.
|
||||
- Unity: `PlayerRigTools.AttachBathynautKit` — grafts the kit SMRs + the bare male head onto Player.prefab's flattened skeleton by bone name (in place, GUID preserved, ghost surface unchanged); removes the Synty sci-fi helmet. **GraftSmr REBASES**: verts baked to rest-world space, bindposes = inverse of **rigid (scale-stripped)** rest matrices, `RecalculateTangents()` — persisted as `Rebased_*.asset` meshes (GUID-stable Clear+refill).
|
||||
- New `ProjectM/EmissiveGloamSkinned` shader (hand-written HLSL + Rukhanka `ComputeDeformedVertex`, in-place skinning) on `M_EmissiveGloam_WarmSkinned` (steady warm gold — true light): porthole + lamp lens glow and DEFORM.
|
||||
- Player.prefab children now: Root skeleton · body · armour · KitBrass · KitGlow · bare head. Suit tris ~8.9k (body 7.9k + kit 1k) — still over the ≤6k hero budget; decimate pass stays parked (07-14).
|
||||
|
||||
## Validation
|
||||
|
||||
- L1: console clean (only known tick-batching noise from execute_code stalls).
|
||||
- L2: **409/409 EditMode green** (+19: FacingMathTests 11, TickWindowMathTests 7 incl. the Movement-skip + resting-stick-cast cases, TuningConfigTests sentinel pin; SystemOrderingCycleTests now co-registers PlayerAim/PlayerControl/Blink/PlayerDeathState).
|
||||
- Play smoke (DevSandbox, client+server): **server==client facing under injected input**; move-east → facing (1,0) both worlds; **idle + aim-north holds facing** (no passive cursor tracking); cast opens → facing swings fully north at cast rate and returns; blink socket does NOT open a cast window; **projectile fired while moving east flies (0.00, 1.00) = raw Aim** — the SoD manual-aim contract, live. World creation clean (ordering pin acyclic).
|
||||
- L3: screenshots (`Assets/Screenshots/suitkit_front_glow.png`, `suitkit_back_rigid.png`) — brass dome + glowing amber porthole behind the grille + shoulder lamp from the front; dome crown + tank pack silhouette from the back/game angle. Kit deforms with the rig.
|
||||
- Post-impl diff review `wf_9a8d6162-e72` (20/20 agents, no failures): **17 findings → 15 confirmed (0 blocking: 3 should-fix + 12 nits) / 2 refuted.** All triaged and closed same-session:
|
||||
- **Fixed in code**: blink no longer fires the muzzle/fire cue (the third SocketCooldown consumer got the Movement skip); footsteps got a 0.18s cadence floor (a dash could machine-gun 2–4 thuds); `GraftSmr` now validates the bone rebind BEFORE destroying the old child (a mismatch used to silently strip the piece) + a rigid-skinning guard (`weight0<0.999` aborts) + `rebase:false` for the meter-scale blend-skinned head (tool now reproduces the committed prefab); orphaned doc-blocks deleted from PlayerAnimationDriveSystem; `k_AttackAnimTicks` now structurally = `PlayerAimSystem.CastFacingTicks`; dead ResolveAim re-guard removed from AimReticleSystem; remote-arc asymmetry documented at UpdateRemoteSwings; wrap (0-sentinel) + no-blob tests added to TickWindowMathTests. **Suite 411/411 green after fixes.**
|
||||
- **Docs amended**: Build Spec B.6 (local FX = ResolveAim, deliberately better than the draft cascade — cue==damage in every case) + Part D (pitch jitter shipped as 3 clip variants + volume jitter).
|
||||
- **Accepted**: the Move→Aim→facing cascade is duplicated verbatim in DashSystem/BlinkSystem (byte-identical, commented; a `ResolveDashDir` helper is available cleanup if a third site ever appears).
|
||||
|
||||
## Gotchas learned (new, durable)
|
||||
|
||||
1. **Blender skinned-kit export: a HIDDEN armature can't be selected → `use_selection` FBX export silently drops the skeleton + all vgroups** (the mesh imports as static MeshRenderers). `hide_set(False)` before selecting; restore after.
|
||||
2. **Blender→Unity skinned roundtrip imports cm bones under a 0.01 armature** (regardless of FBX_SCALE_UNITS vs FBX_SCALE_ALL) while Synty-native skeletons are meter-scale. **Rebind by bindpose reuse EXPLODES (×100)**. The fix that works: rebase at graft time — bake verts to rest-world, bindposes = inverse of **rigid (scale-stripped)** rest matrices (`Matrix4x4.TRS(pos, m.rotation, one).inverse`).
|
||||
3. **A procedural skinned Mesh asset without TANGENTS fails Rukhanka/BRG registration** (`BatchMeshID not present` + assertion spam, whole rig vanishes) — `RecalculateTangents()` mandatory.
|
||||
4. **Hand-written Rukhanka deformation shaders**: the `UNITY_DOTS_INSTANCING_START` block with `_DeformedMeshIndex` must be declared **BEFORE** including `ComputeDeformedVertex.hlsl` (macro expands at include time → else "undeclared identifier ..._DOTSInstancingOverrideMode" only in the DOTS_INSTANCING_ON variant = magenta), AND `_DeformedMeshIndex` must ALSO be a Properties-block entry — Rukhanka's SkinnedMeshBaker validates `material.HasProperty`.
|
||||
5. Synty variant containers hold BOTH genders' heads — match grafts by exact name (`Contains("Head")` grabbed the female head first).
|
||||
6. `manage_asset rename` can half-fail (moved the asset to `Assets/` root, extensionless, while reporting an error) — verify on disk; a plain file copy + `refresh_unity force` is the reliable fallback.
|
||||
|
||||
## Follow-ups (surfaced, not silent)
|
||||
|
||||
- `Ability_Blink.asset` CooldownTicks=1 vs BlinkSystem's 150-tick stamp (HUD bar + window-math hygiene) — align to 150 later.
|
||||
- Bathynaut game-ready decimate pass (~8.9k tris vs ≤6k hero budget) — parked since 07-14.
|
||||
- EmissiveGloamSkinned is BRG-only by design (invisible in plain classic scenes — same class as all EG-skinned materials).
|
||||
- Operator eyes-on tuning pass: knobs live in the DevSandbox overlay (Turn rate deg / Cast turn deg / Move sharp + the melee/dash rows); FeelConfig footstep values compile-time for now.
|
||||
|
||||
## Part E — gap analysis (the operator deliverable)
|
||||
|
||||
What the movement+animation combo still lacks for the underwater-heavy feel, in recommended order:
|
||||
1. **Idle sway/breathing** — the suit stands statue-still; a subtle full-body idle drift (Blender clip on the humanoid pipeline) would sell suspension-in-water more than any tuning knob.
|
||||
2. **Start/stop weight reads** — sharpness 6 gives the velocity drift, but there's no lean-in/lean-back pose; a 2-frame lean overlay driven off acceleration (AnimParamMath already computes the basis) is the cheap version.
|
||||
3. **Bubble exhaust** — periodic bubble trickle from the dome (pool emitter, warm-neutral, rises) synced loosely to the footstep cadence; strongest cheap underwater cue after the silt puffs.
|
||||
4. **Turn lean/banking** — body roll proportional to the facing turn rate (client-only, presentation quaternion tweak).
|
||||
5. **Camera weight** — PrototypeCameraRig positional lag/damping scaled up slightly so the camera "drags" through water with the player.
|
||||
6. **Underwater ambience loop** — procedural low rumble + occasional distant groans; the soundscape is currently just SFX.
|
||||
7. **Walk-cycle authoring** — the retimed Synty walk still reads "land walk slowed down"; a Blender heavy-trudge clip (lead with the chest, delayed foot plants) is the real fix per the humanoid clip pipeline.
|
||||
|
||||
## Next session
|
||||
|
||||
Operator eyes-on feel pass with the live knobs (turn rates, sharpness, footstep values), then pick from the gap list (recommend 1+3 first). Post-impl review findings (wf_9a8d6162-e72) to triage if any confirmed.
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
title: DR-052 — Shape-of-Dreams facing model + underwater movement feel
|
||||
date: 2026-07-16
|
||||
status: locked
|
||||
links: "[[2026-07-16_Facing_Underwater_Feel_Suit_Kit]], [[Facing_Underwater_Feel_Build_Spec]], [[DR-048_Lantern_Adoption_Full_Pivot]], [[DR-051_Lantern_Realignment_Purge]]"
|
||||
---
|
||||
|
||||
# DR-052 — Shape-of-Dreams facing model + underwater movement feel
|
||||
|
||||
## Decisions (operator-locked 07-15/16)
|
||||
|
||||
1. **The facing model is SoD option (a)**: the body turns toward the MOVEMENT direction while moving; turns toward the AIM only during a cast window (melee swing / non-Movement socket fire, `CastFacingTicks`=13 ≈ the anim pulse); holds the last facing when idle. **The cursor is never passively tracked.** Twin-stick cursor-facing is dead.
|
||||
2. **`PlayerFacing` is body-yaw/presentation ONLY.** Every gameplay direction (all four AbilityFireSystem archetypes, the auto-target seed, the melee cleave) reads `FacingMath.ResolveAim(PlayerInput.Aim, facing)` — raw replicated Aim, facing fallback for a resting gamepad stick, +Z last. Aim-consuming presentation (reticle ring, tether axis, local slash/cone cues) reads the SAME resolver so sim and FX cannot diverge; remote players' cues stay facing-based (Aim isn't replicated to non-owners).
|
||||
3. **A Movement-archetype socket (blink) never counts as a cast** — `TickWindowMath.SocketFireAndCone` skips it outright (facing AND animation paths; this also killed the latent cooldown-tail phantom IsFiring).
|
||||
4. **Stationary dash/blink keep going toward the cursor** (fallback Move→Aim→facing) — cursor-dodge control kept deliberately over SoD purity.
|
||||
5. **Underwater weight defaults**: `GroundedMovementSharpness` 6 (was 15), locomotion turn 360°/s (was 720), cast turn 1080°/s const; MoveSpeed unchanged (6). Locomotion gait ×0.85, idle⇄locomotion blends 0.25s. Footsteps are stride-distance (1.5m) deep thuds + silt puffs.
|
||||
6. **Live tuning rides TuningKnob 26–28 with a 0 = NO-OVERRIDE sentinel** (0 falls back to stat/const/authored; explicit ClampKnob ≥0 case). Extending `DebugTuningReport` is an accepted **dev-protocol bump** (stale standalone dev build ⇒ handshake refusal ⇒ rebuild both).
|
||||
|
||||
## Determinism/netcode contract (from review wf_a13d4395-1c7, confirmed)
|
||||
|
||||
- Wire-neutral apart from the dev report bump: no `[GhostField]`/RPC/input-struct changes; PlayerFacing stays the only replicated facing state.
|
||||
- PlayerAimSystem stays **un-gated** (no `IsFirstTimeFullyPredictingTick`) — the rate-limited turn is an incremental integrator over the snapshot-restored ghost field and must re-integrate every predicted pass.
|
||||
- `[UpdateAfter(MeleeComboSystem)]` on PlayerAimSystem is a **hygiene pin**, not a divergence fix — the sorter's tie-break is deterministic and cross-world-identical (confirmed by critic evidence; the "cross-world sort divergence" claim was refuted). Socket windows open 1 tick late by construction — cosmetic-only since damage is Aim-sourced.
|
||||
|
||||
## Suit attachment pipeline (the reusable recipe)
|
||||
|
||||
Blender kitbash attachments → rigid-skin (vgroup w=1 + armature modifier) → join per shader role → FBX (UNHIDE the armature first — hidden = silently skinless) → `PlayerRigTools.AttachBathynautKit`: bone-name rebind + **rebase** (verts to rest-world; bindposes = inverse of RIGID scale-stripped rest matrices; `RecalculateTangents`) → `Rebased_*.asset` meshes. Glow pieces ride `ProjectM/EmissiveGloamSkinned` (Rukhanka ComputeDeformedVertex; DOTS-instanced block BEFORE the include; `_DeformedMeshIndex` in Properties for the baker's validation).
|
||||
Reference in New Issue
Block a user