Vampire-Act-Base/design/gdd/wave-manager-logic.md

20 KiB
Raw Blame History

Wave Manager Logic

Status: In Design Author: SepComet + Claude Last Updated: 2026-04-27 Implements Pillar: 战场即信息 (The Field is Information), 形态即战术 (Form is Tactics)

Overview

Wave Manager Logic 是夜裔的遭遇战编排系统——控制每一波敌人的生成时机、类型构成、数量和波次推进节奏。它不是一个简单的"定时刷怪"计时器,而是战斗节奏的作曲家:它决定什么时候让玩家感到压力(混合编制波次)、什么时候给玩家喘息(波次间 10-20s 间隔)、以及什么时候考验玩家的形态切换决策(同一波内投放多种需要不同形态克制的敌人)。

玩家直接感受 Wave Manager 的输出——但不是通过 UI 数字,而是通过战场节奏。一波 Swarm 海从四面八方涌来就是"切雾形"的信号。一波 Brute+Stalker 混合编制就是在问玩家:"你先处理哪个Brute 的弹反窗口还是 Stalker 的冲刺?"波次清空后的短暂沉默不是空白——那是玩家重新评估血能、调整位置、预判下一波构成的战略时刻。Wave Manager Logic 是"战场即信息"和"形态即战术"的编排引擎——它通过敌人构成设计来创造有意义的形态切换压力,而不是靠随机数。

Player Fantasy

Wave Manager Logic 服务于一个任何动作游戏玩家都熟悉但很少被命名的幻想:掌控节奏。当你清空一波敌人、战场突然安静的那 10 秒钟,你的心跳从"战斗模式"的高频切回战术思考——"我还有多少血能?下一波会是什么?我该站在哪里?"——这就是 Wave Manager 在发挥作用。它不是制造敌人,而是制造期待

好的波次编排像一场 DJ 演出它知道什么时候给你密集的低音Swarm 海、什么时候切入一个重拍让你展示技巧Brute 弹反)、什么时候让所有声音同时响起制造混乱(混合编制)、以及什么时候全部静默给你喘息(波次间隔)。玩家不会说"这波次设计得真好"——他们会说"再来一波,我能打"。

参考 Vampire Survivors 的波次节奏——敌人密度波浪式增长,偶尔给一个"安全窗口"让你有空间操作——和 Hades 的遭遇战房间——每个房间是一场独立的微型战斗拼图。夜裔的 Wave Manager 将两者融合:波浪式密度 + 拼图式构成。每一波不只是"更多敌人",而是"不同的问题"。第一波是热身(纯 Swarm教你切雾形第三波是期中考试Brute + Swarm 混合,考验你什么时候放弃 AOE 去做弹反),最后一波是终极考验(三种类型同时在场,要求完整的形态切换循环)。

Detailed Design

Core Rules

1. Wave Lifecycle

InterWave ──(delay elapsed)──→ WaveActive ──(all enemies dead)──→ InterWave
    ↑                                                                    │
    └──────────────────── (next wave exists) ────────────────────────────┘
                                              │
                                              └── (no more waves) → LevelComplete

2. Spawning Rules

  • 敌人从竞技场边界perimeter生成以竞技场中心为圆心、固定半径 R 的圆周上随机取点
  • 生成时敌人面向竞技场中心(朝向玩家方向)
  • 同一 SpawnGroup 内的敌人同时生成(同一帧),不同 SpawnGroup 之间有 SpawnDelay 间隔
  • 生成后立即进入 Enemy AI 的 Idle 状态(玩家进入 DetectionRange 时进入 Chase

3. Wave Tracking

  • Wave Manager 订阅 EnemyAI.OnEnemyDied(enemyId, enemyType, killerForm) 事件
  • 维护 aliveEnemyCount — 当前波次中存活敌人数量
  • aliveEnemyCount == 0 且所有 SpawnGroup 已生成完毕 → 波次清空
  • 仅追踪由当前波次生成的敌人。不在波次中的敌人不计入

4. Wave Advancement

  • 波次清空后立即进入 InterWave 状态,启动 InterWaveDelay 计时器10-20s由 LevelData 配置)
  • 计时器结束 → 自动推进到下一波
  • 无需玩家交互 — 节奏完全由设计者控制
  • 如果是最后一波 → 发射 OnLevelComplete,不再进入 InterWave

5. Level Completion

  • 最后一波的所有敌人死亡 → OnLevelComplete 发射
  • 此时所有敌人已清除,关卡结束
  • 后续流程(结算、菜单、下一关)由 L1/L2 处理

Wave Structure

WaveData 定义:

WaveData {
    int WaveIndex;                    // 0-based wave number
    string WaveName;                  // Designer label (e.g. "Swarm Intro")
    List<WaveSpawnGroup> SpawnGroups; // Groups spawned this wave
    float PreWaveDelay;               // Additional delay before first spawn (default 0)
    bool IsFinalWave;                 // Marks level completion
}

WaveSpawnGroup 定义:

WaveSpawnGroup {
    EnemyType Type;                   // Swarm(1) / Brute(2) / Stalker(3)
    int Count;                        // How many enemies of this type
    float SpawnDelay;                 // Seconds after wave start to spawn this group
    SpawnPattern Pattern;             // Perimeter / Clustered / Opposite / Random
}

SpawnPattern 枚举:

Pattern Behavior When to Use
Perimeter 随机分布在竞技场边界圆周上 标准包围感,适合 Swarm
Clustered 集中在圆周的一个 60° 弧段内 紧凑编队,适合 Brute 群
Opposite 分布在两个相对的 60° 弧段 夹击压力,适合 Stalker 编队
Random 完全随机分布在整个边界上 混合编制默认

MVP Level: "First Night" 波次设计

7 波,教学曲线从单一类型引入到全类型混合:

Wave Name Composition Design Intent
1 Awakening 8× Swarm (Perimeter) 雾形教学 — 圆形 AOE 清屏。玩家学会"看到很多小敌人→切雾形"。
2 The Pack 12× Swarm (Perimeter, 2 groups staggered 1s) 雾形巩固 — 更多数量,分两批生成创造持续压力。
3 Heavy 2× Brute (Clustered) 弹反教学 — 两个 Brute 先后攻击,玩家学会"看到大块头→切人形弹反"。SpawnDelay=2s 确保不会同时攻击。
4 Swarm + Brute 8× Swarm (Perimeter) + 2× Brute (Opposite) 首次混合 — 玩家被迫在 AOE 清小怪和弹反大怪之间切换。Swarm 先刷0sBrute 延迟 3s 刷。
5 Hunters 3× Stalker (Opposite, 2 groups staggered 0.5s) 冲刺教学 — 首次引入 Stalker玩家学会"看到高速冲刺→切狼形对撞"。
6 Full House 6× Swarm (Perimeter) + 1× Brute (Clustered) + 2× Stalker (Opposite) 全类型混合 — 三种形态循环的完整考验。生成顺序Swarm(0s) → Stalker(2s) → Brute(4s)。
7 Final Stand 10× Swarm (Perimeter) + 3× Brute (Random) + 3× Stalker (Random) 最终波 — 高密度全类型。所有 SpawnGroup 同时生成SpawnDelay=0制造最大混乱和最大切换压力。

设计原则

  • 每种敌人类型的前两次出现都是"纯编制"(只有该类型),给予玩家学习空间
  • 混合编制在玩家掌握基础后引入Wave 4+
  • 生成顺序创造"先处理A再处理B"的引导:先刷的敌人先到达玩家
  • SpawnDelay 控制敌人到达玩家的时间差,防止所有敌人同时压上

Interactions with Other Systems

System Direction Interface
Enemy AI Logic Downstream 生成敌人实例并注入 EnemyTypeConfig。调用 Enemy AI 的 SpawnEnemy(typeConfig, position, facingAngle) 工厂方法。
Enemy AI Logic Upstream 订阅 OnEnemyDied(enemyId, enemyType, killerForm) — 追踪存活敌人数量,判断波次是否结束。
Combat Logic 无直接依赖。Wave Manager 不参与伤害判定。通过 Enemy AI 间接关联。
UI/HUD (L2) Downstream 发射 OnWaveChanged(waveIndex, waveName) — HUD 显示当前波次信息。发射 OnInterWaveStarted(duration) — HUD 显示倒计时或"准备"提示。
VFX Spawner (L1) Downstream 发射 OnWaveStarted(波次开始 VFXOnWaveCleared(波次清空 VFXOnEnemySpawned(敌人出现 VFX
Audio Player (L1) Downstream 发射 OnWaveStarted(波次音效)、OnInterWaveStarted(喘气/心跳音效)
Level Loader (L1) Upstream Level Loader 加载关卡时提供 LevelData(包含所有 WaveData。可选提供竞技场边界参数中心点、半径用于生成位置计算。
Score Calculator Downstream 发射 OnWaveCleared + 波次数据 — Score Calculator 可根据波次清空速度计算评分。

Formulas

Spawn Position Calculation

The spawn position formula is defined as:

spawnPos = arenaCenter + (cos(angle), 0, sin(angle)) × arenaRadius

Variables:

Variable Symbol Type Range Description
Arena center arenaCenter Vector3 any Arena center point (from Level Loader)
Arena radius arenaRadius float 2040 Spawn perimeter radius (LevelData config)
Angle angle float 0360° Random angle for perimeter spawns. Clustered: 60° range from base angle. Opposite: two 60° ranges 180° apart.

Output Range: Any point on the perimeter circle at arenaRadius from center.

For Clustered pattern: angle = baseAngle + random(-30°, +30°) where baseAngle is randomized per wave. For Opposite pattern: half at baseAngle ± 30°, half at baseAngle + 180° ± 30°. For Random pattern: angle = random(0, 360°).


Alive Enemy Count

The wave tracking formula is defined as:

aliveCount = spawnedThisWave - killedThisWave

Variables:

Variable Symbol Type Range Description
Spawned this wave spawnedThisWave int 0totalWaveEnemies Cumulative count of enemies spawned in current wave
Killed this wave killedThisWave int 0spawnedThisWave Incremented on each OnEnemyDied for enemies belonging to this wave

Output: 0 to totalWaveEnemies. Wave clears when aliveCount == 0 && allSpawnGroupsCompleted.


Wave Completion

The wave completion formula is defined as:

isWaveCleared = (aliveEnemyCount == 0) AND (allSpawnGroups have been spawned)

Output: Boolean. Once true, triggers transition to InterWave or LevelComplete.

Edge Cases

  • If all enemies in a wave are killed before all SpawnGroups have spawned: Wave remains active. isWaveCleared requires both conditions — all enemies dead AND all groups spawned. Remaining SpawnGroups continue spawning on schedule.
  • If player dies during WaveActive: Wave state freezes. Death/Respawn Rules manages respawn. On respawn, Wave Manager resets the current wave to its initial state (all enemies despawned, wave restarts from InterWave → WaveActive).
  • If deltaTime = 0 (game paused): All Wave Manager timers pause (InterWaveDelay, SpawnDelay). No spawning occurs. No state transitions.
  • If a wave has 0 enemies (all SpawnGroups have Count=0): Wave immediately completes (aliveCount=0, all groups "spawned"). Transition directly to InterWave. Logged as a warning — likely a data error.
  • If a wave has only one SpawnGroup: SpawnDelay is irrelevant — all enemies spawn simultaneously at wave start. No timer needed between groups.
  • If OnEnemyDied fires for an enemy not spawned by this wave: Ignore the event. Wave Manager maintains a set of tracked enemy IDs. Unknown IDs are silently skipped.
  • If arenaRadius <= 0 (invalid LevelData): Log error. Use fallback radius = 20.0. Spawning continues with fallback.
  • If LevelData has an empty Waves list: Log error, emit OnLevelComplete immediately. No enemies, no gameplay — treat as degenerate level.
  • If InterWaveDelay = 0: Wave advancement is instant — no breather between waves. Valid but not recommended for MVP. Minimum enforced by design: 5s (clamped at runtime).
  • If PreWaveDelay + SpawnDelay exceeds practical limit (>30s): Log warning. Enemies not spawning for too long creates dead air. Designer should review timing.
  • If spawned position would place enemy inside arena geometry (e.g., pillar): MVP does not handle this — arenas are assumed to be clear circles. Enemy collision with geometry deferred to vertical slice.
  • If OnLevelComplete fires but game state is already transitioning: Guard with _levelCompleteFired flag. Emit event exactly once. Duplicate calls are ignored.

Dependencies

System Layer Type Hard/Soft Interface
Enemy AI Logic L0 Upstream/Downstream Hard Downstream: calls SpawnEnemy(typeConfig, position, facingAngle) to create enemies. Upstream: subscribes to OnEnemyDied(enemyId, enemyType, killerForm) to track alive count. Cannot function without Enemy AI.
Level Loader (L1) L1 Upstream Hard Provides LevelData (wave list, arena params, InterWaveDelay) on level start. Without it, Wave Manager has nothing to manage.
UI/HUD (L2) L2 Downstream Soft Emits OnWaveChanged, OnInterWaveStarted for HUD display. Gameplay works without HUD.
VFX Spawner (L1) L1 Downstream Soft Emits OnWaveStarted, OnWaveCleared, OnEnemySpawned for visual feedback.
Audio Player (L1) L1 Downstream Soft Emits OnWaveStarted, OnInterWaveStarted for audio cues.
Score Calculator L0 Downstream Soft Emits OnWaveCleared with wave index and clear time for scoring.
Death/Respawn Rules L0 Upstream Soft Receives OnPlayerDied to freeze/reset wave state. Soft because wave system could function without death handling (just loses state on death).

Hard dependencies (system cannot function without): Enemy AI Logic, Level Loader.

Soft dependencies (enhanced by but works without): UI/HUD, VFX Spawner, Audio Player, Score Calculator, Death/Respawn Rules.

Tuning Knobs

Parameter Default Safe Range Too Low Too High
InterWaveDelay 15s 825s No time to reassess — waves blur together Dead air — player gets bored waiting
ArenaRadius 30.0 2040 Enemies spawn on top of player — no reaction time Enemies take too long to reach player — no pressure
PreWaveDelay (per wave) 0s 03s Delays wave start, creates dead air before fights
SpawnDelay (between groups) 2s 0.55s All enemies arrive simultaneously — no staggered pressure Groups arrive so far apart wave loses cohesion
Swarm count per wave 812 420 Not enough to justify AOE form switch Frame rate and readability suffer
Brute count per wave 13 15 Brute not threatening — can be ignored Multiple simultaneous heavy attacks unreadable
Stalker count per wave 23 15 Stalker not creating urgency Stalker rush overlaps unreactable
Total enemies per wave 816 430 Wave feels empty — no density 200+ total active enemies — performance risk

Key tuning groups:

  • Pacing rhythm: InterWaveDelay + PreWaveDelay + SpawnDelay — the sum of delays determines the feel of "combat density vs breathing room"
  • Form pressure balance: Swarm count vs Brute count vs Stalker count per wave — if any type dominates, the wave only tests one form
  • Performance ceiling: Total enemies alive simultaneously — Combat Logic budget is 200 enemies at <1ms. Total active across all waves must stay under this limit. At default counts (Wave 7: 16 total), performance is not a concern. Future tuning should profile before increasing.

Visual/Audio Requirements

Combat is a visual system category — Visual/Audio is REQUIRED.

Event VFX Audio
OnWaveStarted Arena perimeter pulse — brief geometric ring flash at spawn boundary. Color: white → fade. Rising tone / alarm — pitch increases with wave number
OnWaveCleared Brief screen flash + all remaining enemy fragments dissolve simultaneously Bass drop + short silence before InterWave heartbeat
OnEnemySpawned Small geometric "materialize" effect at spawn point — shape matches enemy type Subtle whoosh — pitch varies by enemy size
OnInterWaveStarted Screen edges dim slightly (vignette increase) — signals "safe moment" Heartbeat / breathing SFX — tempo slows as InterWaveDelay nears end
OnLevelComplete Full screen geometric pattern (like shattered glass reforming) — gold/white Victory fanfare — short, geometric, not orchestral

UI Requirements

Element Content Trigger
Wave Banner "Wave [N]: [WaveName]" — appears at wave start, fades after 2s OnWaveChanged
InterWave Countdown "[N]s until next wave" — centered, subtle OnInterWaveStarted(duration)
Enemy Remaining Counter "[N] enemies remaining" — small HUD element Alive count update each time OnEnemyDied fires
Wave Clear Text "Cleared!" — brief centered text OnWaveCleared

Acceptance Criteria

# GIVEN WHEN THEN
1 LevelData with 7 waves loaded. Wave 1 has 8× Swarm (Perimeter). InterWaveDelay=15s. Level starts Wave 1 spawns 8 Swarm at arena perimeter. OnWaveChanged fires with waveIndex=0.
2 Wave 1 active. 8 enemies alive. Enemy AI emits OnEnemyDied for one Swarm aliveCount=7. Wave remains active.
3 Wave 1 active. 8 enemies alive. All 8 OnEnemyDied events fire. Last enemy dies Wave clears. OnWaveCleared fires. Transition to InterWave. InterWaveDelay timer starts.
4 InterWave state. 15s elapsed. Next wave exists (Wave 2). Timer expires Transition to WaveActive. Wave 2 begins spawning. OnWaveChanged fires with waveIndex=1.
5 Wave 7 (IsFinalWave=true) active. All enemies dead. Last enemy dies OnWaveCleared fires. OnLevelComplete fires. No InterWave transition.
6 Wave with 2 SpawnGroups: Group1(Delay=0s), Group2(Delay=3s). Wave starts. Wave becomes active Group1 spawns immediately. 3s later, Group2 spawns. Both before any died events.
7 SpawnPattern=Clustered, baseAngle=90°. Count=4. ArenaRadius=30. ArenaCenter=(0,0,0). Spawn executed 4 enemies spawned within 60° arc centered on 90° (±30°). All at distance 30 from center.
8 deltaTime=0.0. InterWave state with 10s remaining on timer. Update called Timer does not advance. Still 10s remaining.
9 Player dies during WaveActive (Wave 3). Death/Respawn emits OnPlayerRespawned. OnPlayerRespawned received Wave 3 resets: all enemies despawned, wave restarts from InterWave → re-spawn all Wave 3 enemies.
10 OnEnemyDied fires for enemy ID not in tracked set (not spawned by this wave). Event received Ignored. aliveCount unchanged.
11 LevelData.Waves is empty list. Level starts Log error. OnLevelComplete fires immediately.
12 Wave 3 "Heavy": 2× Brute (Clustered, SpawnDelay=2s between them). Wave becomes active First Brute spawns at 0s. Second Brute spawns at 2s. Two separate spawn events.

Open Questions

  • Player death wave reset: Should the player restart the entire level, the current wave, or from a checkpoint (e.g., every 3 waves)? Defer to Death/Respawn Rules GDD.
  • Wave difficulty scaling: Should enemy stats increase per wave (health/damage scaling) or is composition change alone sufficient for difficulty? MVP assumes composition-only scaling.
  • Boss wave integration: How does Boss AI Logic GDD's boss encounter integrate into the wave system? Is the boss a special "Wave 8" or a separate encounter after Wave 7?
  • Wave order randomization: Should the 7-wave structure be fixed (same every playthrough) or allow designer to tag waves as "random pool" for replayability? MVP uses fixed order.
  • Spawn visual transition: Should enemies "phase in" with a brief materialize animation, or pop into existence? Enemy AI GDD says Idle state has no wake-up animation — spawning FX is a Wave Manager concern.