Core Game Loop Additions
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
---
|
||||
date: 2026-06-03
|
||||
type: session
|
||||
tags: [session, m6, core-loop, netcode, ghost-relevancy, design]
|
||||
---
|
||||
|
||||
# Session 2026-06-03 — M6 "The Aether Cycle": core-loop research, plan, and Stages 0–1
|
||||
|
||||
## Goal
|
||||
|
||||
Re-align M6 from the narrow "grid build placement via RPC" into **the first vertical slice of the actual core
|
||||
game loop**. Operator brief: a magic + sci-fi premise (start weak/amnesiac, a voice toward "THEM", harvest
|
||||
magical energy to build a base + charge abilities, procedural expeditions, periodic base-defense). Research
|
||||
good directions and **determine the core game loop in this slice**.
|
||||
|
||||
## Done
|
||||
|
||||
**Research + design (`/dots-dev`).** Three-stream research: codebase foundation map (M1–M5.5), co-op
|
||||
roguelite-base-defense loop precedent (Dome Keeper, Deep Rock, Hades, Risk of Rain, Vampire Survivors, Drill
|
||||
Core…), and magic+sci-fi narrative/progression (Warframe/Control/Bastion/Noita/Returnal). Synthesised **"The
|
||||
Aether Cycle"** — a Dome-Keeper two-phase loop: **Expedition** (gather, soft incursion timer) → **Defend** (a
|
||||
wave hits the base) → **Build/Charge** (spend resources on structures *and* ability tiers, one shared economy)
|
||||
→ repeat, escalating, toward a goal meter. Four operator decisions: persistent base + sorties; separate
|
||||
base/expedition scenes; multiple resource types; "The Awakening Engine" narrative lean. Plan approved.
|
||||
|
||||
**Stage 0 — region-relevancy world split (the netcode crux; DONE + validated).**
|
||||
- `RegionTag{byte Region}` + `RegionId`/`RegionMath` (`Simulation/World/RegionComponents.cs`); transit RPC
|
||||
(`RegionTransitRequest.cs`).
|
||||
- `RegionRelevancySystem` (Server, `GhostSimulationSystemGroup`): per-connection `GhostRelevancyMode.SetIsIrrelevant`,
|
||||
hides cross-region ghosts each tick (global/untagged ghosts stay visible for free). API verified on Netcode
|
||||
1.13.2 via `unity_reflect`.
|
||||
- `RegionTransitSystem` (Server): RPC → resolve player via `SourceConnection`→`NetworkId`→`GhostOwner`, flip
|
||||
`RegionTag`, teleport to region origin (expedition = base + (1000,0,0)).
|
||||
- Tagged players (`GoInGameServerSystem`), storage (`SharedStorageSpawnSystem`), Husks (`WaveSystem`) → Base.
|
||||
- **Validated headless:** transit→Expedition teleports the player to X=1000 and **despawns the base storage
|
||||
ghost on the client** (relevancy); transit→Base **re-grants** it; server==client, console clean.
|
||||
|
||||
**Stage 1 — macro phase director + wave gating (DONE + validated).**
|
||||
- `CycleState` ([GhostField] Phase/CycleNumber/PhaseEndTick — pre-annotated for the future CycleDirector ghost)
|
||||
+ `CyclePhase` consts + `CycleRuntime` (server-only) in `Simulation/World/CycleComponents.cs`.
|
||||
- `CyclePhaseSystem` (Server, `[UpdateBefore(WaveSystem)]`): Expedition (timed) → Defend (wave-driven) → Build
|
||||
(timed) → next cycle, all wrap-safe `NetworkTick` math. Gates `WaveSystem` via a one-line `CycleState` check.
|
||||
- **Validated headless:** full **Expedition→Defend→Build→Expedition** auto-cycle; **CycleNumber 1→2**; wave
|
||||
spawns **only** in Defend (husks=0 in Expedition); **escalation across cycles 4→6 Husks**; Husks tagged Base.
|
||||
|
||||
**Stage 2 — resources + harvest + cycle replication/HUD (DONE + validated).** Adversarially design-reviewed
|
||||
first via a 3-critic + synthesis workflow (it caught real bugs pre-code: the base-storage-ledger relevancy
|
||||
trap, a `GetSingleton<StorageEntry>` "multiple instances" collision, the harvest variable-frame dt-trap, a
|
||||
node double-destroy, the CycleState lazy-create hazard). Split into **2a** (CycleDirector global ghost +
|
||||
migrate `CycleState` onto it + HUD phase readout) and **2b** (resources/nodes/harvest/ledger + HUD counts).
|
||||
- **2a:** `CycleDirector.prefab` (dup UpgradePickup, mesh stripped, no RegionTag → global) carries `CycleState`
|
||||
+ a `StorageEntry` ledger buffer + `ResourceLedger` tag; `CycleDirectorSpawnSystem` (one-shot) spawns it;
|
||||
`CyclePhaseSystem` refactored atomically (`RequireForUpdate<CycleState>`, lazy-create deleted). **Validated:**
|
||||
exactly one `CycleState`, replicates to client, HUD shows `"DEFEND CYCLE 1"`; the global director stays
|
||||
relevant to an expedition player while the base storage despawns (the global-ledger thesis proven).
|
||||
- **2b:** `ResourceId` + `ResourceNode` ghost (`ResourceNode.prefab`, RegionTag{Expedition}); `ExpeditionFieldSystem`
|
||||
edge-spawns/despawns a seeded field per cycle; `ResourceHarvestSystem` (plain group after the predicted group)
|
||||
sweeps projectiles via the new `Projectile.LastStep` (written by `ProjectileMoveSystem`) → deposits to the
|
||||
global ledger (`StorageMath` reused). **Validated headless:** 8 nodes seed in expedition (round-robin A/O/B,
|
||||
invisible to the base player by relevancy); a hit deposits 5 Aether + decrements the node; full depletion
|
||||
despawns the node; 5 same-tick hits deposit all + destroy once (double-destroy safe); **tunnelling sweep**
|
||||
catches a node 3u past the projectile (point test would miss); field despawns on leaving Expedition; HUD reads
|
||||
`"AETHER 30 ORE 5 BIO 0"`. Burst ON, console clean.
|
||||
|
||||
**Visibility + playability pass (DONE + validated, operator-requested).** The systems worked but were invisible/
|
||||
unplayable in a real session (no in-world travel, void expedition, timer-only phases). Added: **walk-in gates**
|
||||
(`ExpeditionGate` baked entity + `ExpeditionGateSystem` server proximity transit — a glowing gate at the base
|
||||
deploys you to the expedition, a return gate brings you back) with **timer cap + early return** pacing
|
||||
(Expedition cap lengthened to ~60s; returning to base via the gate expires the timer → Defend); a **visible
|
||||
expedition place** (indigo ground plane + dark pillars at the expedition region in SampleScene); **bright glowing
|
||||
resource nodes** (M_Projectile material); and a **HUD clarity pass** (color-coded phase + countdown, BASE/ON-
|
||||
EXPEDITION location + gate hint, resource counts). **Validated via screenshots + headless:** stepping into the
|
||||
base gate deploys to the expedition (camera follows, 8 glowing nodes become visible by relevancy); stepping into
|
||||
the return gate comes back to base AND starts Defend early; HUD reads phase/countdown/cycle/resources/location.
|
||||
*Tooling gotcha:* `manage_gameobject create` `component_properties` silently dropped the enum/Vector3 fields
|
||||
(both gates baked with authoring defaults — the BaseGate worked only by coincidence); fixed with
|
||||
`manage_components set_property` + verified via the `mcpforunity://scene/gameobject/{id}/component/...` resource.
|
||||
|
||||
See [[DR-013_M6_Aether_Cycle_Region_Split]] for the full architecture + validated evidence.
|
||||
|
||||
## Decisions
|
||||
|
||||
- [[DR-013_M6_Aether_Cycle_Region_Split]] — M6 = "The Aether Cycle"; base/expedition split via coordinate-region
|
||||
+ `GhostRelevancy` (supersedes DR-008's streaming framing); server-authoritative phase director gating the
|
||||
Husk wave. Region-relevancy + phase-machine implemented and runtime-validated.
|
||||
|
||||
## Open / deferred (the Stage 3–4 continuation)
|
||||
|
||||
- ✅ **Stages 2a/2b done** (see above): client HUD + `CycleState` replication on the global CycleDirector ghost;
|
||||
multi-type resources + procedural harvest field + global ledger + HUD resource counts. Optional hardening:
|
||||
extract the harvest sweep into a pure `HarvestMath` + EditMode tunnelling/reproducibility tests (currently
|
||||
runtime-validated, consistent with how `ProjectileDamageSystem`'s sweep is covered).
|
||||
- **Stage 3 — Build placement + turret + ability tiers:** `BuildPlaceRequest` RPC + occupancy + `BuildPlacementMath`
|
||||
(unit-tested) + grid-snap preview; `Turret`+`TurretFireSystem` (auto-defend, reuse projectile path);
|
||||
`AbilityUpgradeRequest` spending the ledger. (The original M6 build slice.)
|
||||
- **Stage 4 — Persistence + goal meter:** host JSON save/restore (replayed through build/upgrade paths — new
|
||||
scope vs. DR-008); `GoalProgress` ghost ticked per cycle.
|
||||
- **Fiction reconciliation:** adopt Aether/Awakening-Engine naming into [[Identity]] (operator sign-off).
|
||||
- **Burst re-enable + editor restart:** Burst is currently **disabled** for the session (workaround for a stale
|
||||
Burst-cache exception when editing `WaveSystem` on an unfocused editor — see DR-013 gotcha). Restart Unity to
|
||||
clear the cache, then re-enable `Jobs ▸ Burst ▸ Enable Compilation` to restore full performance.
|
||||
|
||||
## Next
|
||||
|
||||
Checkpoint for operator feedback on the working core-loop skeleton, then continue Stage 2 (resources + harvest)
|
||||
— the gather half of the economy — followed by build placement (Stage 3) and persistence/goal (Stage 4).
|
||||
Reference in New Issue
Block a user