Import art/VFX asset packs + game-feel systems; normalize texture extensions to lowercase for LFS
Add BefourStudios SciFi environment packs, Gabriel Aguiar VFX, and the ShaderCrew Toon Shader embedded packages, plus combat/enemy/wave/death gameplay systems and supporting vault docs/screenshots. Rename 11 vendor textures from uppercase .PNG/.HDR to lowercase so the case-sensitive Git LFS filters (*.png/*.hdr) match on case-sensitive filesystems (Linux CI, case-sensitive macOS), not just locally where core.ignorecase=true masks the gap. Each .meta moved with its asset so GUID references are preserved. All ~1000 binaries tracked via LFS. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ Multiplayer game on **Unity DOTS** (Entities) + **Netcode for Entities** (server
|
||||
|
||||
## Map of Content
|
||||
|
||||
- **Vision** → [[Pillars]] — design pillars & locked decisions
|
||||
- **Vision** → [[Pillars]] — design pillars & locked decisions · [[Identity]] — the fiction (sci-fi frontier colony)
|
||||
- **Game Design** → [[Systems_Index]] — per-system design docs
|
||||
- **Roadmap** → [[Milestones]] · [[Backlog]]
|
||||
- **Sessions** → `07_Sessions/2026/` — dated work logs (latest: [[2026-05-29_Project_Setup]])
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
---
|
||||
tags:
|
||||
- vision
|
||||
- identity
|
||||
status: draft
|
||||
updated: 2026-06-02
|
||||
permalink: gamevault/01-vision/identity
|
||||
---
|
||||
|
||||
# Game Identity — Frontier colony (sci-fi)
|
||||
|
||||
> The fiction that turns the [[Pillars|mechanical pillars]] into a *place*. Decided 2026-06-02 to bridge the "tech-demo → game" gap. It is a **skin over the locked pillars — no mechanical rework**: every pillar already built (M1–M5) keeps its systems and gains a fictional role below.
|
||||
|
||||
## One-sentence pitch
|
||||
|
||||
> **A 2–4 player co-op action-RPG about holding a frontier outpost on a hostile world: twin-stick combat against the Husk swarm, a shared base you build and automate, and production chains that keep running while you fight.**
|
||||
|
||||
## Setting
|
||||
|
||||
An off-world **colony foothold** — a small crew anchoring a survivable outpost on a frontier world that does not want them there. A spreading corruption — **the Blight** — turns drones, machinery, and fauna into aggressive **Husks** that probe and swarm the perimeter. The outpost is the safe anchor you grow and defend; beyond it lies the blighted frontier you push into for resources.
|
||||
|
||||
## Player fantasy
|
||||
|
||||
A **frontier operator**: mobile, lethal twin-stick gunplay; you and your friends raise an outpost, wire up automation so the colony *compounds* even while you're out fighting, and push the perimeter back against the Husks. Setup is rewarded over grind (per the automation pillar).
|
||||
|
||||
## Tone
|
||||
|
||||
Grounded near-future **industrial sci-fi** with an edge of cosmic/biotech corruption. Not cute, not horror — **tense frontier survival with a power-fantasy payoff**. Reference feel: *Helldivers* frontier grit × *Factorio* logistics satisfaction × *V Rising* base-growth loop × *Diablo / PoE* combat readability.
|
||||
|
||||
## Art north-star
|
||||
|
||||
**Dark, readable, high-contrast.** A desaturated industrial world (metal, regolith, dim ambient) so that **bright emissive gameplay elements pop and bloom**:
|
||||
|
||||
- **Cyan / teal** → the player crew, friendly energy, owned structures.
|
||||
- **Hostile orange / red** → Husks and Blight corruption.
|
||||
- **White-hot** → impacts, muzzle flashes, deaths.
|
||||
|
||||
Silhouette-first readability (ARPG legibility from the top-down frame), bloom on emissives, vignette to focus the frame, ACES tonemapping. **Primitives + emissive + post-processing now**; stylized low-poly hard-surface (crew/structures) and corrupted biomech (Husks) later. The palette is the identity even before real models.
|
||||
|
||||
## The enemy — Husks
|
||||
|
||||
Corrupted rogue drones/lifeforms driven to swarm the colony. They are the **threat that makes combat *combat*** and gives the base a reason to exist.
|
||||
|
||||
- **First Husk (this slice):** a melee charger — seeks the nearest operator, closes, strikes. Server-authoritative interpolated ghost.
|
||||
- **Later:** ranged "spitters", armored "brutes", a "broodmaker" spawner, and eventually a corrupted-fabricator boss. Wave/threat director over time.
|
||||
|
||||
## Automation flavor
|
||||
|
||||
Colony **fabricators, conveyors, assembler drones, and power** — the self-running production chains of the locked automation pillar, fictionalized as the outpost's industry that keeps growing while the crew fights and expeditions.
|
||||
|
||||
## How it maps to the locked pillars (no rework)
|
||||
|
||||
| Pillar ([[Pillars]]) | Fictional skin |
|
||||
|---|---|
|
||||
| Action-ARPG combat (twin-stick) | Frontier operator gunplay vs. the Husk swarm |
|
||||
| Co-op base power fantasy (V Rising feel) | The crew's outpost — built, defended, grown together |
|
||||
| Automation as progression | Colony fabricators/drones/conveyors compounding output |
|
||||
| Persistent base + instanced expeditions | The safe **outpost anchor** (`BaseAnchor`) vs. the blighted **frontier** beyond |
|
||||
|
||||
## Audio / UI identity (direction)
|
||||
|
||||
Industrial synth, weighty impacts, a low hostile drone for Husk proximity. UI reads as a **clean diegetic colony-terminal** — functional HUD (health, ability charge, threat count), neon-on-dark.
|
||||
|
||||
## Related
|
||||
|
||||
- [[Pillars]] — locked mechanical decisions this fiction skins.
|
||||
- Established in the "First Blood" game-feel slice (2026-06-02) — see the session log + decision record for the presentation/enemy architecture that first expresses this identity.
|
||||
@@ -28,4 +28,5 @@ permalink: gamevault/01-vision/pillars
|
||||
|
||||
## Related
|
||||
|
||||
- [[Identity]] — the **fiction** that skins these pillars (sci-fi frontier colony), decided 2026-06-02. No mechanical rework; adds setting, tone, art north-star, and the Husk enemy.
|
||||
- Decision records: `07_Sessions/_Decisions/`
|
||||
@@ -36,6 +36,12 @@ One design doc per gameplay system, linked here. Each should state: purpose, com
|
||||
- **Systems:** `SharedStorageSpawnSystem` (server one-shot — instantiate the container ghost at `CellToWorld(cell)`, destroy spawner); `StorageOpReceiveSystem` (server `SimulationSystemGroup`, NOT predicted — apply the RPC to the singleton container's buffer via `StorageMath`); `StorageOpSendSystem` (client managed `SystemBase` — E/Q keyboard + editor-only `Deposit`/`Withdraw` statics → `StorageOpRequest` RPC). `GoInGameServerSystem` re-rooted onto `BaseGridMath.PlotCenter(BaseAnchor)` (with a `TryGetSingleton` fallback).
|
||||
- **Netcode shape:** base config = **baked, ghost-free, identical both worlds** (not replicated). Storage container = **ownerless interpolated** server-spawned ghost; its `StorageEntry` buffer is a `[GhostField]` (no `OwnerSendType`/`GhostOwner`) so server mutations replicate to all clients. Deposit/withdraw = **server-authoritative `IRpcCommand`** resolved against the single container singleton, applied outside the predicted loop (no rollback double-apply). Status: **built + runtime-validated** (server == client buffer; EditMode 62/62). Decisions: [[DR-008_M5_HomeBase_BaseLayer_Storage]]. M6 (grid placement) + M7 (production) build on `BaseGridMath` + the runtime-ghost-into-cell spawn path.
|
||||
|
||||
### M5.5 — Game feel & identity ("First Blood") · [[2026-06-02_GameFeel_Identity]]
|
||||
|
||||
- **Components** (`ProjectM.Simulation`): `EnemyTag`/`EnemyStats`/`EnemyAttackCooldown`/`EnemySpawner` + pure `EnemyAIMath`; `Dead` (enableable, **derived**) + `RespawnState` + pure `RespawnMath`; `TickUtil.NonZero` (the cooldown 0-sentinel guard).
|
||||
- **Systems:** `EnemyAISystem` (server-only, **plain `SimulationSystemGroup`**, `[UpdateAfter(PredictedSimulationSystemGroup)]` — the interpolated Husk ghost seeks the nearest living player, deals contact `DamageEvent`) + the `WaveSystem` threat director (escalating waves of Husk variants Grunt/Swarmer/Brute — replaced the flat sustain; see [[2026-06-02_GameFeel_Deepening]]); `HealthApplyDamageSystem` +`EnemyTag` death; `PlayerDeathStateSystem` (both worlds, predicted — derives `Dead` from `Health<=0`, gates movement/aim/fire via `.WithDisabled<Dead>()`); `PlayerRespawnSystem` (server-only — schedule + refill + reposition). Client presentation (managed, `PresentationSystemGroup`): `CombatFeedbackSystem` (damage numbers / VFX / procedural SFX / camera shake by edge-detecting replicated Health) + `HudSystem` (code-built uGUI health / cooldown / threat / DOWNED) + `PrototypeCameraRig.AddShake`.
|
||||
- **Netcode shape:** Husk = **ownerless interpolated** server-driven ghost (stock `LocalTransform` replication; `Health` `[GhostField]`); `Dead` = **local derived** enableable (NOT replicated — pure function of replicated Health); juice/HUD **observe** replicated state only (client world, never the sim). Identity: [[Identity]] (sci-fi frontier colony). Status: **built + runtime-validated** (Husks spawn(6)/replicate/chase/strike; death→respawn loop; HUD; emissive dark-sci-fi look); EditMode **74/74**. Decisions: [[DR-009_GameFeel_Identity_FirstBlood]].
|
||||
|
||||
## Conventions
|
||||
|
||||
DOTS/ECS conventions live in repo `CLAUDE.md` and the `dots-dev` skill's `dots-conventions.md`. Don't duplicate volatile API details here — link to context7-derived notes instead.
|
||||
@@ -21,7 +21,7 @@ Unordered pool of candidate work. Promote to a [[Milestones|milestone]] when com
|
||||
- [ ] **M2 follow-up — restart the editor to clear the corrupted Burst cache**, then confirm the console is clean on a warm play (no "not a known Burst entry point"). See [[2026-05-31_M2_Combat]] / [[DR-003_M2_Combat_Netcode_Architecture]].
|
||||
- [ ] **M2 follow-up — live interactive fire test** (focused Play Mode: press Space / LMB / RT → predicted projectile + dummy HP drop). The server combat loop + replication are validated; the input→`AbilityFireSystem`→predicted-spawn→classification path is only validated structurally.
|
||||
- [ ] **M2 follow-up — mouse-cursor aim for KBM** (needs a camera ground-ray rig); currently aim = gamepad right-stick + movement-heading fallback.
|
||||
- [ ] **M2 follow-up — player death/respawn** (M2 only clamps player HP ≥ 0; dummies despawn on death).
|
||||
- [x] **M2 follow-up — player death/respawn** — done in **M5.5**: derived `Dead` enableable gate (from replicated Health) + server `PlayerRespawnSystem` (full HP + reposition to base after a delay). [[DR-009_GameFeel_Identity_FirstBlood]]
|
||||
- [ ] M2 polish — projectile/dummy visuals (primitive meshes/materials currently); optional predicted client-side auto-target if the soft server reconcile feels off.
|
||||
- [ ] **M3 follow-up — UI/icon/description pipeline** for abilities (managed lookup keyed by `AbilityId`, off the blob). Deferred from M3 ([[2026-05-31_M3_Data_Driven_Abilities]]).
|
||||
- [ ] **M3 follow-up — timed / removable modifiers** (expiry on `NetworkTick`, `ClearByType` via `StatModifier.SourceId`). M3 modifiers are permanent-once-granted.
|
||||
@@ -42,4 +42,16 @@ Unordered pool of candidate work. Promote to a [[Milestones|milestone]] when com
|
||||
- [ ] **M5b follow-up — CC package version**: 1.4.2 declares `entities/physics@1.3.x` (resolves via SemVer floor on our 6.4.0/1.4.6). Move to a CC build explicitly targeting Entities 6.x when published.
|
||||
- [ ] **M5 follow-up — physics lag compensation** (`NetCodePhysicsConfig.EnableLagCompensation` + `PhysicsWorldHistorySingleton`) if/when physics-based hit detection replaces the swept-segment server check.
|
||||
- [ ] **M5 follow-up — projectiles as physics bodies** (currently kinematic + swept hit); convert for projectile-vs-world collision.
|
||||
- [ ] **M5 follow-up — hard rotation lock** (`PhysicsMass.InverseInertia=0`); `Rigidbody.FreezeRotation` is not honored by the DOTS baker, so rotation is held by per-tick angular-zero + aim write.
|
||||
- [ ] **M5 follow-up — hard rotation lock** (`PhysicsMass.InverseInertia=0`); `Rigidbody.FreezeRotation` is not honored by the DOTS baker, so rotation is held by per-tick angular-zero + aim write.
|
||||
- [x] **Game identity defined** — sci-fi **frontier colony** fiction ([[Identity]]), layered over [[Pillars]] with no mechanical rework. Resolves the "mechanics without a fiction" gap. Delivered with M5.5 ([[DR-009_GameFeel_Identity_FirstBlood]]).
|
||||
- [x] **M2 polish — projectile/dummy visuals** — addressed by the M5.5 emissive palette + URP post-processing (cyan crew, cyan projectiles, orange Husks, dark walls/ground). Real models/animation still deferred.
|
||||
- [x] **M5.5 follow-up — auto-target on Husks** — done (deepening pass): `AbilityFireSystem` candidate query `.WithAny<TrainingDummyTag, EnemyTag>()`. [[2026-06-02_GameFeel_Deepening]]
|
||||
- [x] **M5.5 follow-up — respawn safety** — done (deepening pass): replicated `RespawnInvuln` window (server-enforced damage immunity + HUD SHIELDED cue). [[2026-06-02_GameFeel_Deepening]]
|
||||
- [x] **M5.5 follow-up — enemy variety + wave/threat director** — done (deepening pass): `WaveSystem` escalating waves (lull → spawn → clear → bigger) + 3 melee Husk variants (Grunt/Swarmer/Brute) round-robin from a baked pool. Ranged spitter + broodmaker/boss still open. [[2026-06-02_GameFeel_Deepening]]
|
||||
- [ ] **Deepening follow-up — ranged Husk (Spitter)**: a server-spawned enemy-projectile subsystem for dodge depth (the next variety type; melee-only today).
|
||||
- [ ] **Deepening follow-up — wave number on HUD**: needs a replicated game-state ghost (`WaveState` is server-only); the live "HUSKS N" count conveys threat for now.
|
||||
- [ ] **Deepening follow-up — boss / per-wave composition weighting** (brutes later, swarms early) instead of flat round-robin; a "broodmaker" that spawns.
|
||||
- [ ] **Deepening follow-up — server perf under wave load**: investigate the in-editor tick-batching (Burst cache health, multi-world cost); validate in a real build.
|
||||
- [ ] **M5.5 follow-up — real assets**: authored audio (replace procedural SFX), stylized hard-surface crew/structures + corrupted-biomech Husks (replace primitives), TextMeshPro HUD; add the URP **SSAO** renderer feature for more depth.
|
||||
- [ ] **M5.5 follow-up — multi-client juice/HUD**: validate remote-player death VFX (interpolation-timeline `Health==0`) + per-client HUD under two clients (pairs with the deferred M4/M5b two-build tests).
|
||||
- [ ] **M5.5 follow-up — difficulty balance**: Husks tuned for a real moving/shooting player (dmg 5, strike 48 ticks, speed 3, `MaxAlive` 6); revisit once movement/fire feel is tuned and enemy variety lands.
|
||||
@@ -16,6 +16,7 @@ permalink: gamevault/06-roadmap/milestones
|
||||
| **M3 — Data-driven abilities & modifiers** | Ability **and** character stats authored in ScriptableObjects, baked to DOTS **blob assets**; runtime **flat + % modifier** stacks (upgrades/buffs) → effective stats, server-authoritative + prediction-correct. Pattern slice: refactor the current projectile ability + 1–2 sample abilities onto the data model. | ✅ Done 2026-05-31 — runtime-validated on 6.4.7: blob DB baked into both worlds; data-driven base + replicated `StatModifier` ghost buffer → **identical effective stats on server & owner-predicted client** (held under tick-batching); data-only ability swap; real pickup grant; EditMode 38/38. Blob DB + replicated modifier buffer + every-tick effective recompute — [[DR-004_M3_DataDriven_Abilities_Modifiers]], [[2026-05-31_M3_Data_Driven_Abilities]]. |
|
||||
| **M4 — Co-op** | 2–4 players; client-hosted listen-server (Direct IP/LAN now, Unity Relay later) | ✅ Done 2026-06-02 — **LAN slice + multi-client validated**: no-auto-connect `ConnectionConfig` + request-component host/join, editor auto-host + thin clients, deterministic ring spawn; 3 clients (1 real + 2 thin) connect→spawn (distinct slots)→replicate→clean disconnect; `ConnectionUI` for builds; EditMode 45/45. **Two controllable characters in-game confirmed 2026-06-02 via Unity Multiplayer Play Mode** (extra virtual player; full connection handshake not exercised end-to-end, but in-scene co-op looks good). **Unity Relay + real two-build LAN join deferred** — [[DR-005_M4_Connection_Model_Direct_IP]], [[2026-06-01_M4_LAN_CoOp_And_Classification_Fix]]. |
|
||||
| **M5 — Home base + physics** | Persistent base subscene streaming + Unity Physics in the predicted loop | 🚧 In progress 2026-06-01 — **physics-in-prediction slice done + runtime-validated** on 6.4.7: player is a velocity-driven dynamic Unity Physics body in the predicted loop (built-in `CapsuleCollider`+`Rigidbody` bake; `PhysicsVelocity` auto-replicated), collides with baked static walls (stops at the surface, no tunnel/climb-over), planar-pinned, **server == client** with no desync; EditMode 51/51. **Base subscene streaming deferred** to a later pass — [[DR-006_M5_Physics_In_Prediction]], [[2026-06-01_M5_Physics_In_Prediction]]. **M5b (same day): player movement re-founded on the Unity Character Controller package** (`com.unity.charactercontroller` 1.4.2) — kinematic collide-and-slide, owner-predicted, data-driven speed; replaces the dynamic-Rigidbody mover (keeps the DR-006 predicted-physics infra). Runtime-validated (collide-and-slide, planar, server==client, CharacterInterpolation predicted-only); EditMode 47/47 — [[DR-007_M5b_Character_Controller_Package]], [[2026-06-01_M5b_Character_Controller]]. **Base-layer done 2026-06-02 + runtime-validated:** home base = baked ghost-free `BaseAnchor` + locked deterministic planar build-grid (`BaseGridMath`, 1.0u × 32²; M6 builds on it) + player spawn re-rooted onto the anchor + one **shared-storage ghost** (ownerless interpolated; deposit/withdraw via server-authoritative `IRpcCommand`; **server == client** buffer). EditMode 62/62. **Subscene split (base/expedition) + disk persistence still deferred** — [[DR-008_M5_HomeBase_BaseLayer_Storage]], [[2026-06-02_M5_HomeBase_BaseLayer]]. |
|
||||
| **M5.5 — Game feel & identity** | Bridge "tech-demo → game": the **Husk** enemy (server AI, interpolated ghost), player death/respawn, combat juice (damage numbers/VFX/SFX/camera shake), a core HUD, and a sci-fi look pass — under the new fiction ([[Identity]], sci-fi frontier colony) | ✅ Done 2026-06-02 — runtime-validated on 6.4.7: Husks spawn(6)+replicate+chase+strike; death→respawn loop; HUD (health/cooldown/threat/downed); emissive dark-sci-fi look. EditMode **74/74**. ctx7-verified APIs. **Deepened same day:** auto-target on Husks, replicated respawn-invulnerability, and a `WaveSystem` threat director (escalating waves of 3 Husk variants — Grunt/Swarmer/Brute) replacing the flat sustain — runtime-validated (wave 1→2 escalation 4→6, distinct maxHP 30/15/80). [[DR-009_GameFeel_Identity_FirstBlood]], [[2026-06-02_GameFeel_Identity]], [[2026-06-02_GameFeel_Deepening]] |
|
||||
| **M6 — Build/placement** | Server-authoritative grid build placement via RPC | ⬜ |
|
||||
| **M7 — Automation** | Self-running tick-based production chains (deterministic offline catch-up) | ⬜ |
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
date: 2026-06-02
|
||||
type: session
|
||||
tags: [session, dots, netcode, enemy-ai, wave-director, game-feel, m5]
|
||||
permalink: gamevault/07-sessions/2026/2026-06-02-gamefeel-deepening
|
||||
---
|
||||
|
||||
# Session 2026-06-02 — Game-feel deepening (waves, variety, invuln, auto-target)
|
||||
|
||||
## Goal
|
||||
|
||||
Continue the [[2026-06-02_GameFeel_Identity|First Blood]] slice's suggested next steps — turn the flat Husk swarm into a **sustaining combat loop**: (1) **auto-target on Husks**, (2) **respawn safety (invulnerability)**, (3) **enemy variety + a wave/threat director**. Same constraints (server-authoritative, deterministic, ctx7-verified, adversarially reviewed). Decisions extend [[DR-009_GameFeel_Identity_FirstBlood]].
|
||||
|
||||
## Done
|
||||
|
||||
- **Auto-target on Husks:** `AbilityFireSystem` soft auto-target candidate query `.WithAll<TrainingDummyTag>()` → `.WithAny<TrainingDummyTag, EnemyTag>()` — Husks are now aim-assist targets (they were already raw-aim hittable).
|
||||
- **Respawn invulnerability:** new replicated `RespawnInvuln { [GhostField] uint UntilTick }` + `RespawnState.InvulnTicks`. `PlayerRespawnSystem` (server) sets the window on recovery; `HealthApplyDamageSystem` (server, predicted group) nullifies player damage while active; `HudSystem` (client) shows a cyan health bar + "SHIELDED". Stops instant re-death into the swarm.
|
||||
- **Wave/threat director:** `WaveDirector` (baked config) + `WaveEnemyPrefab` (buffer pool) + `WaveState` (runtime singleton) + `WaveSystem` (server state machine): **Lull → Spawning `BaseCount + (wave-1)*CountPerWave` Husks, one every `SpawnIntervalTicks` at a deterministic ring, round-robin over the prefab pool → wait for the field clear (no `EnemyTag`) → Lull(`LullTicks`)**. Replaced the flat `EnemySpawner` sustain (deleted `EnemySpawner`/`EnemySpawnSystem`/`EnemySpawnerAuthoring`). Tick gating uses `TickUtil.NonZero` + wrap-safe `NetworkTick`.
|
||||
- **Husk variety:** 3 interpolated-ghost variants spawned round-robin — **Grunt** (`Enemy`, 30 HP), **Swarmer** (`EnemySwarmer`, 15 HP, fast/weak, small), **Brute** (`EnemyBrute`, 80 HP, slow/tanky/hard-hitting, large) — each = different baked `EnemyStats` + its own emissive material (`M_HuskSwarmer` yellow, `M_HuskBrute` deep-red) + scale. Subscene `EnemySpawner` GameObject swapped to `WaveDirector` (wired to all three).
|
||||
- **Cleanup (from runtime):** `EnemyAISystem` `[UpdateBefore(PredictedSimulationSystemGroup)]` → `[UpdateAfter(...)]` — the predicted group is **OrderFirst**, so `UpdateBefore` was silently **ignored** (console warning); contact damage now intentionally drains the **following** tick (~16ms melee latency, accepted). Removed a spurious `ParticleSystem.duration` set (warning).
|
||||
|
||||
## Validation
|
||||
|
||||
- **EditMode 74/74 green** (no new pure-math helpers; wave logic is stateful/integration-validated at runtime). Console clean of compile errors.
|
||||
- **Runtime (single in-editor client, 6.4.7):**
|
||||
- **Wave director:** wave 1 spawned **4 Husks** (BaseCount), replicated server↔client (4==4); cleared the field → **Lull** → **wave 2 escalated to 6 Husks** (BaseCount + CountPerWave). Full spawn→clear→lull→bigger-wave cycle confirmed.
|
||||
- **Variety:** distinct baked `maxHP` **30 / 15 / 80** among live Husks (Grunt/Swarmer/Brute), round-robin from the pool; distinct materials + scales confirmed by screenshot (yellow Swarmers, orange Grunts, a large red Brute).
|
||||
- **Invulnerability:** `RespawnInvuln.UntilTick` set on the player's respawn (server-authoritative, replicated for the HUD cue).
|
||||
- **Adversarial review (7 agents):** verified the wave director, variety, invuln replication, and auto-target **correct** — the "wave deadlock", "escalation overflow", and "0-sentinel conflation" suspicions all came back **not-a-bug** (the empty-server stall self-heals on respawn + Husks teleport-seek so can't wall-stick; overflow needs wave ~1 billion; writes are `TickUtil.NonZero`-guarded). **1 confirmed low-severity finding:** `RespawnMath.IsDue` used a raw `now >= respawnTick` compare — the lone wrap-safety holdout (the slice's tick *writes* were guarded by `TickUtil.NonZero`, but this read-side *compare* was missed). **Fixed** with a wrap-safe signed-delta `(int)(now - respawnTick) >= 0` (kept pure-uint, so the 5 `RespawnMath` unit tests stay valid). EditMode 74/74 after the fix.
|
||||
|
||||
## Diagnosis notes (for future me)
|
||||
|
||||
- **`[UpdateBefore(PredictedSimulationSystemGroup)]` is IGNORED** — the predicted group is **OrderFirst** in `SimulationSystemGroup`, and OrderFirst/OrderLast outrank `UpdateBefore`/`After` (Unity logs "Ignoring invalid UpdateBefore… because OrderFirst/OrderLast has higher precedence"). A plain-`SimulationSystemGroup` server system therefore always runs **after** the predicted group; to read this tick's post-predicted state use `[UpdateAfter(PredictedSimulationSystemGroup)]` (and accept that anything it appends for the predicted group drains next tick). This corrects the "same-tick" claim in [[DR-009_GameFeel_Identity_FirstBlood]] — `EnemyAISystem` contact damage is a 1-tick-late (~16ms, imperceptible for melee) event, NOT same-tick. The runtime warning caught what the first review missed.
|
||||
- **Wave state machine — avoid the deadlock:** only re-enter Lull when `RemainingToSpawn==0` AND the field is clear (`EnemyTag` count 0). The headless (input-less) player can't kill Husks, so the field never clears and the cycle stalls in Spawning — that's a test-harness artifact, not a bug (a real player clears it; validated by killing Husks via `execute_code` → the director advanced to wave 2).
|
||||
- **Editing a subscene authoring component in code:** open the subscene additively, `GameObject.GetComponent`/`DestroyImmediate` the old authoring + `AddComponent` the new + set fields (incl. `GameObject[]` prefab arrays, which the MCP property setter handles awkwardly) → `EditorSceneManager.MarkSceneDirty`+`SaveScene` → close. `AssetDatabase.DeleteAsset` is blocked by `execute_code` safety checks — guard duplicate-creates with a `LoadAssetAtPath != null` check instead.
|
||||
- **Server tick-batching warnings in-editor** are the known perf artifact (multiple worlds in one process + presentation + earlier unfocused throttle), not a code bug — reliable server state (wave director, RPCs) is robust to it; only one-shot predicted `InputEvent`s drop under batching.
|
||||
|
||||
## Open / deferred
|
||||
|
||||
- **Ranged Husk (Spitter):** a server-spawned enemy projectile subsystem for tactical (dodge) depth — the next variety step (melee-only today).
|
||||
- **Wave number on the HUD:** needs a replicated game-state ghost (`WaveState` is server-only); the live "HUSKS N" count conveys threat for now.
|
||||
- **Boss / mini-boss + per-wave composition weighting** (brutes later, swarms early) instead of flat round-robin.
|
||||
- **Server performance:** investigate the in-editor tick-batching under the wave load (Burst cache health, world count); validate in a real build.
|
||||
|
||||
## Next
|
||||
|
||||
Add the **ranged Spitter** (enemy projectile) for combat depth, or resume the milestone track at **M6 — server-authoritative grid build placement** ([[DR-008_M5_HomeBase_BaseLayer_Storage]]). The wave director + variety pool make adding new enemy types data-cheap.
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
date: 2026-06-02
|
||||
type: session
|
||||
tags: [session, dots, netcode, game-feel, identity, enemy-ai, presentation, hud, urp, m5]
|
||||
permalink: gamevault/07-sessions/2026/2026-06-02-gamefeel-identity
|
||||
---
|
||||
|
||||
# Session 2026-06-02 — "First Blood": game-feel & identity slice
|
||||
|
||||
## Goal
|
||||
|
||||
Before more systems work, **make the project feel like a game — in feel, look, and play — and give it an identity.** Diagnosis: M1–M5 are systems-complete but identity-less — pillars are mechanical (no fiction), and there is **no feedback layer** (grep confirmed zero audio/particles/VFX/shake/UI), the only enemy is an inert dummy, and there's no death/respawn or HUD. Operator chose: identity = **sci-fi frontier colony**, first slice = the **full game-feel pass** ("First Blood"), proceed plan→approve→build. Locked in [[DR-009_GameFeel_Identity_FirstBlood]]; fiction in [[Identity]].
|
||||
|
||||
## Process
|
||||
|
||||
- **Explore + propose** (read-only): diagnosed the look/feel/play gap against [[Pillars]] + the M1–M5 vault, surfaced the "mechanics without a fiction or a feedback layer" core, and proposed a spine (Identity) + 3 axes (Feel/Look/Play) with a recommended first slice. Operator picked the fiction + full slice via a 3-question gate.
|
||||
- **context7 WAS reachable this session** (vs [[DR-008_M5_HomeBase_BaseLayer_Storage]]). A **4-agent ctx7 verification workflow** (Netcode / Entities-enableable / URP / DOTS→GameObject presentation) returned high-confidence verified API shapes + a key correction: **an interpolated enemy ghost must be moved server-only in the plain `SimulationSystemGroup`, NOT in the predicted loop**, and `Dead` is cleanest **derived** from replicated Health (no replicated enabled bit).
|
||||
- **Implementation** (sequential via MCP `create_script`/`script_apply_edits` — single editor, domain-reload-ordered; NOT a parallel swarm) in 5 stages, each compiled + console-checked + (where applicable) EditMode-tested + runtime-validated before the next.
|
||||
- **Adversarial review workflow** over the diff (12 agents: 4 dimensions → per-finding adversarial verification). It **verified the architecture correct** — the Husk is a properly interpolated/ownerless ghost (`HasOwner=0`, `DefaultGhostMode=Interpolated`), the derived `Dead` is rollback-safe (no GhostField), the local player's Health is never client-predicted, and presentation randomness is outside the sim — every netcode-desync suspicion came back **not-a-bug**. **3 confirmed low-severity findings**, all the same root: the `uint` cooldown "0 = ready" sentinel can collide at tick wraparound (`RespawnMath` already guarded it; the three cooldown writers + the HUD didn't). **Fixed** via a shared `TickUtil.NonZero` guard at the three writes (`EnemyAISystem`/`EnemySpawnSystem`/`AbilityFireSystem`) and a wrap-safe `NetworkTick` compare in the HUD cooldown bar.
|
||||
|
||||
## Done
|
||||
|
||||
**Stage 1 — Husk enemy** (`ProjectM.Simulation/Combat`, `ProjectM.Server/Combat`, `ProjectM.Authoring/Combat`): `EnemyTag`/`EnemyStats`/`EnemyAttackCooldown`/`EnemySpawner` + pure `EnemyAIMath` (SeekVelocity/InAttackRange/RingPosition); `EnemyAISystem` (server-only, plain `SimulationSystemGroup` `[UpdateBefore(PredictedSimulationSystemGroup)]` — seeks nearest living player, writes `LocalTransform`, appends `DamageEvent` on a `NetworkTick`-gated strike); `EnemySpawnSystem` (persistent server spawner, sustains `MaxAlive` on a deterministic ring around `BaseAnchor`); `HealthApplyDamageSystem` +`EnemyTag` death; `EnemyAuthoring` + `EnemySpawnerAuthoring`. `Enemy.prefab` duplicated from `UpgradePickup.prefab` (interpolated ownerless ghost) → `EnemyAuthoring`; `EnemySpawner` GameObject added to `Gameplay.unity`.
|
||||
|
||||
**Stage 2 — Player death/respawn** (`ProjectM.Simulation/Player`, `ProjectM.Server/Combat`): `Dead` (enableable, **derived** from Health) + `RespawnState` + pure `RespawnMath`; `PlayerDeathStateSystem` (both worlds, predicted, before movement/aim/fire — sets `Dead` + zeroes velocity); `PlayerRespawnSystem` (server-only, after the predicted group — schedule on death, refill+reposition on due). `PlayerControlSystem`/`PlayerAimSystem`/`AbilityFireSystem` gained `.WithDisabled<Dead>()`; `PlayerAuthoring` bakes `Dead` (disabled) + `RespawnState`.
|
||||
|
||||
**Stage 3 — Combat juice** (`ProjectM.Client/Presentation`): `CombatFeedbackSystem` (client-only managed `SystemBase`, `PresentationSystemGroup`) — edge-detects replicated `Health` → pooled billboard `TextMesh` damage numbers + runtime `ParticleSystem` hit/death/muzzle bursts + **procedural** `AudioClip` SFX + camera shake; Husk death via despawn-prune; local-fire muzzle via `AbilityCooldown` edge. `PrototypeCameraRig` +`AddShake` (presentation-only decayed shake).
|
||||
|
||||
**Stage 4 — HUD** (`ProjectM.Client/Presentation`): `HudSystem` (code-built uGUI overlay — `RawImage` health + cooldown bars over `Texture2D.whiteTexture`, legacy `Text` threat count + DOWNED/RESPAWNING overlay) driven from the local player ghost. `ProjectM.Client.asmdef` +`UnityEngine.UI`.
|
||||
|
||||
**Stage 5 — Look** (assets/editor): URP global `Volume` (Bloom/ACES Tonemapping/ColorAdjustments/Vignette) + dark flat ambient + exp fog + cool key light + URP HDR color grading; emissive re-tint (teal crew, cyan projectiles, orange Husks via new `M_Husk`, dark-metal walls/ground). Husk balance tuned (dmg 8→5, strike 36→48 ticks, speed 3.5→3.0).
|
||||
|
||||
**Docs:** [[Identity]] (the fiction) + linked from [[Pillars]]/Home; [[DR-009_GameFeel_Identity_FirstBlood]].
|
||||
|
||||
## Validation
|
||||
|
||||
- **EditMode 74/74 green** (+12 vs M5's 62 — 7 `EnemyAIMath`, 5 `RespawnMath`). Console clean of code/Burst/ghost errors after every stage.
|
||||
- **Runtime (single in-editor client, 6.4.7):** Husks **spawn (6) + replicate server↔client (6==6)**, **chase** from the ring to the player, **deal contact damage** (player → 0 HP). **Death→respawn loop verified** by polling: `Health=0`/`Dead=True`/`RespawnTick` scheduled → recovered at base full-HP/`Dead=False`. **HUD renders** (health bar, "HUSKS N", DOWNED overlay). **Look confirmed** via screenshots: dark metal world + cyan crew + orange Husks + bloom (after fixing the `M_Dummy`-as-wall-material orange-walls bug).
|
||||
- **Adversarial review (12 agents):** architecture verified correct; 3 confirmed low-severity findings (the `uint` cooldown 0-sentinel wraparound), all fixed (`TickUtil.NonZero` + HUD `NetworkTick` compare). **EditMode 74/74 after fixes**, console clean.
|
||||
|
||||
## Diagnosis notes (for future me)
|
||||
|
||||
- **Interpolated ghost ≠ predicted.** Move an ownerless interpolated enemy ghost **server-only in plain `SimulationSystemGroup`** (`[WorldSystemFilter(ServerSimulation)]`), NOT in `PredictedSimulationSystemGroup`. `[UpdateAfter(PredictedSimulationSystemGroup)]` (NOT `[UpdateBefore]` — the predicted group is OrderFirst, so UpdateBefore is silently ignored; corrected in the deepening pass) so a contact `DamageEvent` drains the following tick (~16ms, fine for melee). Stock `LocalTransform` replication carries its position — no hand-written `[GhostField]`.
|
||||
- **Derive enableable state, don't replicate it, when it's a pure function of already-replicated data.** `Dead = Health<=0` derived every predicted tick in both worlds is rollback-correct and dodges the `[GhostEnabledBit]` surface — same idiom as `EffectiveCharacterStats`. To toggle a disabled enableable component you must still visit the entity: `.WithPresent<Dead>()` (write) vs `.WithDisabled<Dead>()` (alive-only run). `Simulate` (also enableable) ANDs independently — keep `.WithAll<Simulate>()`.
|
||||
- **A Baker can bake an enableable component DISABLED** (`AddComponent<Dead>(e); SetComponentEnabled<Dead>(e, false);`) → instances spawn alive with zero first-tick work (instantiated entities inherit the prefab's enabled state).
|
||||
- **Presentation belongs in a client-only managed `SystemBase` in `PresentationSystemGroup`** (once per frame, no rollback double-fire). Read ECS state via `SystemAPI.Query` INSIDE `OnUpdate` (registers job deps) + `EntityManager.CompleteDependencyBeforeRO<T>()` — NOT from a MonoBehaviour `LateUpdate` (the job-safety exception the camera rig hit). `Entity` is a stable client dict key for a ghost's lifetime; **prune the cache each frame** (a pruned enemy = a kill → death VFX at its last position; never `DestroyEntity` a ghost from the client — `GhostDespawnSystem` owns that).
|
||||
- **Netcode-safe "hit-stop" = a camera punch, never `Time.timeScale`** (which would corrupt the deterministic sim).
|
||||
- **Code-built uGUI with no assets:** `RawImage` over `Texture2D.whiteTexture` (anchor-driven fill, no sprite) + legacy `Text` with `Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf")`. Procedural `AudioClip.Create` + runtime `ParticleSystem` (Sprites/Default material, HDR start color to bloom) keep the slice asset-free.
|
||||
- **Watch shared-material bleed when re-tinting:** the walls glowed orange because `M_Dummy` doubled as the wall material — gave Husks their own `M_Husk` instead. Editing a prefab asset's component in code needs `PrefabUtility.LoadPrefabContents` → modify → **`SaveAsPrefabAsset`** (NOT `SavePrefabAsset`, which rejects the contents root) → `UnloadPrefabContents`.
|
||||
- **ACES tonemapping needs the URP asset's color grading mode = HDR** (`m_ColorGradingMode = 1`), else HDR bloom/emission won't tonemap right.
|
||||
- **Editor `is_focused` matters again:** play-enter domain reload + boot took ~10–15s while unfocused; entity inspection via `execute_code` had to be retried until worlds booted. (`Application.runInBackground` keeps Play ticking but Edit mode still throttles unfocused.)
|
||||
|
||||
## Open / deferred
|
||||
|
||||
- Generalize `AbilityFireSystem` auto-target from `TrainingDummyTag` to `EnemyTag` (Husks are hittable via raw-aim swept hit; only the aim-assist snap is missing).
|
||||
- Respawn safety (brief invuln / Husk push-back) so respawn isn't instant re-death.
|
||||
- Real assets (audio, stylized crew/structure models, biomech Husks); SSAO renderer feature.
|
||||
- Enemy variety + a wave/threat director (ranged Husk, brute, broodmaker).
|
||||
- Multi-client validation of remote-player death VFX + per-client HUD.
|
||||
|
||||
## Next
|
||||
|
||||
Either **deepen the loop** (enemy variety + wave director + respawn safety + auto-target on Husks) to make the combat sustain, or resume the milestone track at **M6 — server-authoritative grid build placement** ([[DR-008_M5_HomeBase_BaseLayer_Storage]]). The presentation + server-AI patterns from this slice are the reusable foundation for the former.
|
||||
@@ -0,0 +1,53 @@
|
||||
---
|
||||
id: DR-009
|
||||
title: First Blood — game-feel & identity slice (Husk enemy + death/respawn + combat juice + HUD + sci-fi look)
|
||||
status: accepted
|
||||
date: 2026-06-02
|
||||
tags:
|
||||
- decision
|
||||
- netcode
|
||||
- presentation
|
||||
- enemy-ai
|
||||
- game-feel
|
||||
- identity
|
||||
- m5
|
||||
permalink: gamevault/07-sessions/decisions/dr-009-gamefeel-identity-firstblood
|
||||
---
|
||||
|
||||
# DR-009 — First Blood: game-feel & identity slice
|
||||
|
||||
## Context
|
||||
|
||||
The project was a **systems-complete, identity-less vertical slice**: M1–M5 solved every hard DOTS/Netcode problem (predicted player, data-driven abilities/modifiers, co-op, predicted physics + Character Controller, home base + shared storage) but it **looked like a debug scene, had zero feedback, and didn't play like a game** — the only "enemy" was an inert training dummy, there was no player death/respawn, no HUD, and a grep confirmed **no audio / particles / VFX / camera shake / UI** anywhere. The operator asked to "make this feel more like a game — in feel, look, and play — and give it an identity." A diagnosis established the gap was twofold: **(1) the pillars are mechanical, not fictional** (no setting/tone/art-direction) and **(2) there is no feedback layer** translating the (correct, replicated) simulation into things a player sees/hears/feels.
|
||||
|
||||
The operator chose: identity = **sci-fi frontier colony** ([[Identity]]); first slice = the **full game-feel pass** ("First Blood"); proceed by plan → approve → build. **context7 was reachable this session** (unlike [[DR-008_M5_HomeBase_BaseLayer_Storage]]) and was used to verify every volatile Netcode/Entities/URP API before writing (a 4-agent ctx7 verification workflow).
|
||||
|
||||
## Decision
|
||||
|
||||
1. **Identity = sci-fi frontier colony, layered over the locked [[Pillars]] with NO mechanical rework.** Captured in [[Identity]]: an off-world outpost crew holding the line against the **Husk** swarm; cyan crew / orange Husks / dark industrial world; automation = colony fabricators/drones. Only flavor, art-direction, and enemy fiction are added.
|
||||
|
||||
2. **Husk enemy = an OWNERLESS INTERPOLATED ghost, moved SERVER-ONLY.** Unlike the (non-replicated, server-only) training dummy, the Husk is a real ghost so all clients see it — built by **duplicating `UpgradePickup.prefab`** (an existing interpolated ownerless ghost) and swapping its authoring to `EnemyAuthoring` (keeps the `GhostAuthoringComponent`). `EnemyAISystem` runs **server-only in the plain `SimulationSystemGroup`** (interpolated ghosts are NOT predicted — must not run in `PredictedSimulationSystemGroup`), `[UpdateAfter(PredictedSimulationSystemGroup)]` (corrected from `[UpdateBefore]` in the same-day deepening pass — the predicted group is **OrderFirst**, so `UpdateBefore` is silently ignored), so a contact `DamageEvent` it appends drains the **following** tick (~16ms, imperceptible for melee) in `HealthApplyDamageSystem` (server, inside the predicted group). _(The flat `EnemySpawnSystem` mentioned below was superseded the same day by the `WaveSystem` threat director — see [[2026-06-02_GameFeel_Deepening]].)_ It seeks the nearest **living** player (writes `LocalTransform` directly — stock LocalTransform replication carries position, **no hand-written `[GhostField]`**) and strikes on a `NetworkTick`-gated cooldown. `EnemySpawnSystem` is a **persistent** server spawner that sustains `MaxAlive` Husks on a deterministic ring around the `BaseAnchor` (mirrors the one-shot `TrainingDummySpawnSystem` but refilling). Husk death reuses the existing channel: `HealthApplyDamageSystem` destroys `EnemyTag` at 0 HP (one-line addition next to the dummy hook).
|
||||
|
||||
3. **Player `Dead` = a LOCAL enableable component DERIVED from replicated Health — not replicated.** `PlayerDeathStateSystem` (runs in **both** worlds in `PredictedSimulationSystemGroup`, before movement/aim/fire) sets `Dead.enabled = Health.Current <= 0` and zeroes `CharacterControl.MoveVelocity` while dead. Because it is a pure function of the already-replicated, reconciled `Health` (the **same derive-don't-replicate idiom as `StatRecomputeSystem`/`EffectiveCharacterStats`**), the gate is identical on server + owner-predicted client and rollback-correct **with no replicated enabled bit** (avoids the volatile `[GhostEnabledBit]` surface). Movement/aim/fire (`PlayerControlSystem`/`PlayerAimSystem`/`AbilityFireSystem`) gain `.WithDisabled<Dead>()` (alive) composed with the existing `.WithAll<Simulate>()`. `Dead` is **baked DISABLED** on the player prefab (spawns alive, zero first-tick work). Respawn **timing is server-only** (`PlayerRespawnSystem`, `SimulationSystemGroup` `[UpdateAfter(PredictedSimulationSystemGroup)]`): schedule on first `Health<=0`, and on the due tick refill `Health` to the effective max + reposition to the deterministic base spawn slot (`PlayerSpawnMath`). `Health` (GhostField) + `LocalTransform` replicate, so the recovery reaches clients and the derived `Dead` clears.
|
||||
|
||||
4. **All juice + HUD = CLIENT-ONLY managed `SystemBase` in `PresentationSystemGroup` — observe, never simulate.** `CombatFeedbackSystem` edge-detects every damageable ghost's replicated `Health` (managed `Dictionary<Entity,FxCache>`, pruned each frame): a decrease → floating damage number (pooled billboard `TextMesh`) + hit-spark burst + hit SFX + camera shake; a Husk **despawn** (pruned-while-enemy = a kill) → death burst + SFX at its last position; the local player crossing to 0 → death feedback; local fire (`AbilityCooldown` advancing) → muzzle + zap. SFX are **generated procedurally** (`AudioClip.Create`), VFX use a **runtime `ParticleSystem` pool** (Sprites/Default material + HDR start colors so they bloom) — the slice ships **no binary audio/particle assets**. `HudSystem` builds a uGUI overlay **in code** (`RawImage` bars over `Texture2D.whiteTexture` + legacy `Text` with a resolved builtin font — no sprite/prefab assets) driven from the local player ghost: health bar, ability-cooldown bar, live Husk threat count, DOWNED/RESPAWNING overlay. Camera shake is a static impulse on `PrototypeCameraRig` decayed in `LateUpdate` — **presentation-only, never the sim** (no `Time.timeScale` hit-stop, which would corrupt netcode). `ProjectM.Client` gains a `UnityEngine.UI` asmdef reference.
|
||||
|
||||
5. **Look = a URP post-processing volume + an emissive palette, on a budget.** A global `Volume` (`Bloom` threshold 1.1 / intensity 0.75, `Tonemapping` ACES, `ColorAdjustments` cool/contrasty, `Vignette`) + dark cool flat ambient + exponential fog + a cool directional key light + URP asset set to **HDR color grading** (required for ACES). Materials re-tinted with HDR emission so they bloom: crew **teal** (`M_Player`), energy-**cyan** projectiles (`M_Projectile`), hostile **orange** Husks (new `M_Husk`, isolated so re-tints don't bleed), dark-metal walls (`M_Dummy` doubles as the wall material) + ground (`M_Ground`). No real models/animation yet — palette + post-processing carry the identity.
|
||||
|
||||
## Consequences
|
||||
|
||||
- **Validated at runtime on 6.4.7 (single in-editor client).** Husks **spawn (6) and replicate server↔client (6==6)**, chase the player from the spawn ring to point-blank (seek works), and **deal contact damage** (player HP driven to 0). The **death→respawn loop works**: player `Health=0` → `Dead=True` → respawn scheduled (`RespawnTick`) → on the due tick **recovered at base with full HP**, `Dead=False`. The **HUD renders** (health bar, "HUSKS N" threat count, "DOWNED — RESPAWNING" overlay). The **look reads as intended**: dark metal environment, glowing cyan crew, hostile orange Husks, bloom on emissives (after fixing an initial shared-material bug where the walls glowed orange because `M_Dummy` doubled as the wall material). **EditMode 74/74** (+12: 7 `EnemyAIMath`, 5 `RespawnMath`).
|
||||
- **Reused every validated pattern** — one-shot/persistent server spawner, interpolated server-spawned ghost (duplicate-an-existing-ghost), `DamageEvent`→`HealthApplyDamageSystem`, stock `LocalTransform` replication, server-only systems, derive-don't-replicate, `NetworkTick` cooldown gating, pure unit-tested math helpers. **No new asmdefs** beyond the `UnityEngine.UI` reference.
|
||||
- **Difficulty tuned** after observing the swarm one-shot the (idle, input-less) player: Husk damage 8→5, strike cadence 36→48 ticks, speed 3.5→3.0. A real moving/shooting player can fight 6 Husks; the headless validation can't (it just stands there) — repeated death there is a test-harness artifact, not a gameplay bug.
|
||||
- **Foundation for the next feel work** — the presentation pattern (client-world edge-detection on replicated state → pooled GameObject FX) and the server-AI pattern (interpolated ghost + seek/attack) generalize to more enemy types, a wave/threat director, ranged Husks, and real art/audio.
|
||||
|
||||
## Open / deferred
|
||||
|
||||
- **Auto-target on Husks** — `AbilityFireSystem`'s soft auto-target still only considers `TrainingDummyTag`; generalize the candidate set to `EnemyTag` (projectiles already hit Husks via raw-aim swept hit, so combat works; only the aim-assist snap is missing).
|
||||
- **Respawn safety** — the player respawns into the swarm; add brief post-respawn invulnerability or a Husk push-back so respawn isn't instant re-death under a real player.
|
||||
- **Real assets** — procedural SFX + primitive meshes + code-built FX are prototype-grade; swap for authored audio, stylized hard-surface crew/structures, and corrupted-biomech Husks once the loop is proven fun.
|
||||
- **SSAO** — deferred (the URP SSAO renderer feature would deepen the dark scene); bloom + grading + emissive already deliver the read.
|
||||
- **Enemy variety + threat director** — one melee Husk today; add ranged/brute/broodmaker + a wave/escalation system (the [[Identity]] roadmap).
|
||||
- **Multi-client juice/HUD** — validated single-client; confirm remote-player death VFX + per-client HUD under two clients.
|
||||
|
||||
Expresses [[Identity]]; builds on the server-authoritative + deterministic + co-op pillars from [[Pillars]]; reuses patterns from [[DR-003_M2_Combat_Netcode_Architecture]] (DamageEvent/Health), [[DR-004_M3_DataDriven_Abilities_Modifiers]] (derive-don't-replicate, effective stats), [[DR-007_M5b_Character_Controller_Package]] (CharacterControl), [[DR-008_M5_HomeBase_BaseLayer_Storage]] (BaseAnchor, duplicate-an-existing-ghost).
|
||||
Reference in New Issue
Block a user