- 将开发过程/归档文档迁至 docs_dev/(development_plan、certification_checklist、 已废弃的 Cocos 架构草案 archived_cocos_architecture_draft),并修正全部跨引用 - 新增根 README.md(项目介绍,暂定名 Spellforge)与 docs_dev/README.md 索引 - 新增 docs_dev/doc_code_audit_2026-07-20.md:文档 vs 代码交叉审计报告(经 6 路对抗性复核,零证伪),含「代码更优 / 文档更优 / 中性」判定汇总 - 在 docs/ 各设计·技术·机制文档就地加「实现现状 (2026-07-20)」callout: 追认代码更优实现(纯 JSON 数据驱动、SpatialGrid-only 碰撞、MultiMesh 单档、 存档选最新槽等),订正陈旧/矛盾内容(.tres→JSON、Boss HP/阈值/波次、EventID、 StatusManager.apply 签名等),标记未实现功能(C# 热路径、Mana、元进展、 Boss 阶段/抗性、Tutorial、轨迹/连锁/催化等)与 latent bug(CoreFeatureTag 位运算、 pierce 空操作、MAX_OPS 不读 cpu_limit) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
222 lines
10 KiB
Markdown
222 lines
10 KiB
Markdown
# Boss 系统架构设计
|
||
|
||
> **关联切片**:S6 P0(Mini Boss Wave 8 / Final Boss Wave 20)
|
||
> **依赖系统**:EnemyManager、StatusManager、EventBus、VFXManager、ZoneManager
|
||
|
||
---
|
||
|
||
> **⚠️ 实现现状 (2026-07-20 审计)**:本文档描述的整套 Boss 架构(`BossManager` Autoload、`BossDef`/`BossPhaseDef`/`.tres` 资源、`BossAttackRegistry` 攻击模式注册表、场地机制如冰墙/场地收缩、多阶段弹幕、状态机 `BossInstance`)**均为设计蓝图,代码 0% 实现**。现状 Boss 只是 `EnemyManager` SoA 中 `type=4`(Mini Boss,Wave 8)/`type=5`(Final Boss,Wave 20)的普通厚血实体;唯一的"阶段行为"是当血量跨越 66% / 33% 阈值时召唤一批 `FAST` 杂兵援军(`wave_manager.gd:94-117`)。以下 `BossManager`/`.tres`/攻击模式注册表/场地机制全部尚未落地,保留作为未来实现的目标设计。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
|
||
|
||
---
|
||
|
||
## 1. 设计原则
|
||
|
||
- **阶段驱动(Phase-Driven)**:Boss 行为由明确的阶段(Phase)枚举驱动,阶段切换在 `_physics_process` 中以血量阈值触发,不使用独立 Timer Node。
|
||
- **与 EnemyManager 解耦**:Boss 不进入 EnemyManager SoA(避免 stride 污染和 AI 逻辑膨胀),由独立的 `BossManager` Autoload 管理;但向 SpatialGrid 注册自身坐标,使子弹归航与 ZoneManager 能命中 Boss。
|
||
- **数据驱动**:每个 Boss 的阶段配置存储于 `.tres` Resource(`BossPhaseDef`),美术/策划可在不修改代码的情况下调整阈值与攻击模式。
|
||
- **EventBus 集成**:阶段切换、死亡均通过 EventBus 广播,HUD/BGM/成就系统订阅。
|
||
|
||
---
|
||
|
||
## 2. BossManager Autoload
|
||
|
||
```gdscript
|
||
# boss_manager.gd (Autoload: BossManager)
|
||
# Boss 实体数量极少(≤ 2 同屏),不使用 SoA,用 Array[BossInstance] 即可。
|
||
|
||
const MAX_BOSSES: int = 2 # 同屏 Boss 上限
|
||
|
||
var _active: Array[BossInstance] = []
|
||
|
||
func spawn_boss(boss_def_id: String, spawn_pos: Vector2) -> int:
|
||
# 从 BossRegistry 加载 BossDef,实例化 BossInstance,注册到 SpatialGrid
|
||
pass
|
||
|
||
func _physics_process(delta: float) -> void:
|
||
for boss in _active:
|
||
boss.tick(delta)
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Phase 状态机模板
|
||
|
||
### 3.1 枚举与切换条件
|
||
|
||
每个 Boss 的阶段定义在 `BossPhaseDef` Resource 中:
|
||
|
||
```gdscript
|
||
# boss_phase_def.gd
|
||
class_name BossPhaseDef
|
||
extends Resource
|
||
|
||
@export var phase_id: int # 阶段编号(0 起始)
|
||
@export var hp_threshold: float # 进入此阶段的血量百分比(0.0~1.0),如 0.5 = 50% HP
|
||
@export var attack_patterns: Array[String] # 此阶段可用的攻击模式 ID 列表
|
||
@export var move_speed_mult: float = 1.0 # 移动速度倍率(狂暴阶段可设 1.5)
|
||
@export var enrage_vfx: String = "" # 阶段切换时触发的 VFX ID(空=无)
|
||
@export var phase_bgm_layer: int = 0 # 动态音乐层级(0=基础,1=紧张,2=狂暴)
|
||
```
|
||
|
||
### 3.2 BossInstance 状态机
|
||
|
||
```gdscript
|
||
# boss_instance.gd
|
||
class_name BossInstance
|
||
extends RefCounted
|
||
|
||
enum BossPhaseState {
|
||
PHASE_0, # 初始阶段(100% HP)
|
||
PHASE_1, # 受伤阶段(阈值由 BossDef 配置,通常 70%)
|
||
PHASE_2, # 危机阶段(通常 40%)
|
||
PHASE_DYING # 死亡动画阶段(HP ≤ 0,播放死亡序列后才真正移除)
|
||
}
|
||
|
||
var boss_def: BossDef
|
||
var hp: float
|
||
var hp_max: float
|
||
var position: Vector2
|
||
var current_phase: BossPhaseState = BossPhaseState.PHASE_0
|
||
var _attack_cooldown: float = 0.0
|
||
var _pattern_index: int = 0 # 当前阶段攻击模式的轮询索引
|
||
var _phase_just_changed: bool = false # 本帧刚切换阶段的标志,用于触发 enrage VFX
|
||
|
||
func tick(delta: float) -> void:
|
||
_check_phase_transition()
|
||
if current_phase == BossPhaseState.PHASE_DYING:
|
||
_tick_dying(delta)
|
||
return
|
||
_tick_movement(delta)
|
||
_tick_attack(delta)
|
||
_sync_render()
|
||
|
||
# 血量阈值检查(每帧)
|
||
func _check_phase_transition() -> void:
|
||
var hp_pct: float = hp / hp_max
|
||
var phases: Array = boss_def.phases # Array[BossPhaseDef],按 hp_threshold 降序排列
|
||
for i in phases.size():
|
||
var phase_def: BossPhaseDef = phases[i]
|
||
if hp_pct <= phase_def.hp_threshold and int(current_phase) < i + 1:
|
||
_enter_phase(i + 1, phase_def)
|
||
break
|
||
|
||
func _enter_phase(new_phase: int, phase_def: BossPhaseDef) -> void:
|
||
current_phase = new_phase as BossPhaseState
|
||
_pattern_index = 0
|
||
_attack_cooldown = 0.0
|
||
# 阶段切换 VFX
|
||
if phase_def.enrage_vfx != "":
|
||
VFXManager.play(phase_def.enrage_vfx, position, 1.0)
|
||
# 通知 EventBus(HUD 高亮、BGM 切换)
|
||
EventBus.emit(EventID.BOSS_PHASE_CHANGED, {
|
||
"boss_id": boss_def.id,
|
||
"phase": new_phase,
|
||
"bgm_layer": phase_def.phase_bgm_layer
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 攻击模式系统 (Attack Pattern)
|
||
|
||
攻击模式以 ID 字符串存储,在 `BossAttackRegistry` 中注册(类比 `SpellRegistry`),运行时由 `_tick_attack` 调用:
|
||
|
||
```gdscript
|
||
# boss_instance.gd(续)
|
||
func _tick_attack(delta: float) -> void:
|
||
_attack_cooldown -= delta
|
||
if _attack_cooldown > 0.0:
|
||
return
|
||
var phase_def: BossPhaseDef = boss_def.phases[int(current_phase)]
|
||
if phase_def.attack_patterns.is_empty():
|
||
return
|
||
# 轮询攻击模式
|
||
var pattern_id: String = phase_def.attack_patterns[
|
||
_pattern_index % phase_def.attack_patterns.size()
|
||
]
|
||
_pattern_index += 1
|
||
var cooldown: float = BossAttackRegistry.execute(pattern_id, self)
|
||
_attack_cooldown = cooldown
|
||
```
|
||
|
||
`BossAttackRegistry.execute(id, boss)` 返回该攻击模式的冷却时间(秒)。每种攻击模式是一个独立的 GDScript 函数或 Resource,通过 SpellEvaluator 发射子弹(走标准 BulletManager 路径)。
|
||
|
||
---
|
||
|
||
## 5. 具体 Boss 设计模板
|
||
|
||
> **⚠️ 实现现状 (2026-07-20 审计)**:下表 HP 与阶段阈值均为**设计目标**,与代码现值不符。① **血量**:代码实取 `balance.json` 的 `boss_hp`(Mini=**450** / Final=**1800**,`balance.json:8-11`,疑为占位/待平衡值),并非本节的 2000 / 10000,也非 `numerical_design §3.2` 的 50000;三处数值互相矛盾。② **阶段阈值**:代码统一用 **66% / 33%** 两段(`wave_manager.gd:99-102`),并非本节 Mini 的 60%/30% 或 Final 的 70%/40%/15%。③ **攻击模式 / 场地机制 / move_speed / enrage_vfx** 字段全部未实现;每次"进阶"只是召唤 `4 + phase*2` 个 FAST 杂兵。详见 [审计报告](../../docs_dev/doc_code_audit_2026-07-20.md)。
|
||
|
||
### 5.1 Mini Boss — Wave 8(待策划填充)
|
||
|
||
| 字段 | 值 |
|
||
| :--- | :--- |
|
||
| `boss_id` | `"mini_boss_w8"` |
|
||
| `hp_max` | 2000(约为普通精英 6.6×) |
|
||
| `move_speed` | 120(比杂鱼慢,靠范围攻击弥补) |
|
||
| **Phase 0**(100%→60% HP)| 攻击模式:`"charge_slam"` + `"spread_shot_3"` |
|
||
| **Phase 1**(60%→30% HP)| 追加:`"summon_minion_x2"`;move_speed_mult=1.2 |
|
||
| **Phase 2**(30%→0% HP)| 追加:`"zone_aoe_fire"`;move_speed_mult=1.5;enrage_vfx="boss_enrage" |
|
||
| 死亡事件 | `BOSS_KILLED`(**未实现**:该常量不存在;现状仅在生成时发 `BOSS_SPAWNED=19`,死亡走通用 `ENEMY_KILLED=7`)|
|
||
| 战场机制 | 无(Mini Boss 不设场地机制,Final Boss 才有)|
|
||
|
||
### 5.2 Final Boss — Wave 20(待策划填充)
|
||
|
||
| 字段 | 值 |
|
||
| :--- | :--- |
|
||
| `boss_id` | `"final_boss_w20"` |
|
||
| `hp_max` | 10000 |
|
||
| `move_speed` | 80(极慢,靠弹幕覆盖)|
|
||
| **Phase 0**(100%→70% HP)| 攻击模式:`"spiral_shot_8"` + `"summon_turrets_x4"` |
|
||
| **Phase 1**(70%→40% HP)| 追加:`"zone_ice_wall"`;场地:随机生成 4 个冰墙障碍(`ZoneManager` 驱动)|
|
||
| **Phase 2**(40%→15% HP)| 追加:`"enrage_laser_sweep"`;场地收缩:边界每 10 秒向中心缩小 50px |
|
||
| **Phase 3**(15%→0% HP)| 全弹幕覆盖;`"phase3_barrage"`;恢复部分 HP 后进入死亡序列(演出用)|
|
||
| 死亡事件 | **未实现**:`BOSS_KILLED` / `GAME_CLEARED` / `BOSS_PHASE_CHANGED` 三个常量均不存在(ID 15/16/17 实为 `SPELL_EQUIPPED`/`LEVEL_UP`/`SHOP_OPENED`)。现状:生成发 `BOSS_SPAWNED=19`,死亡走 `ENEMY_KILLED=7`,阶段切换发通用 `GAME_STATE_CHANGED=20`(payload `to="BOSS_PHASE_N"`)|
|
||
| 战场机制 | 场地收缩(Phase 2+)、冰墙障碍(Phase 1+)——**均未实现** |
|
||
|
||
---
|
||
|
||
## 6. EventBus 扩展(Boss 专用事件)
|
||
|
||
> **权威登记**:以下 ID 已并入 `technical/implementation_plan.md` §2.1 事件目录与 `event_ids.gd`,本节表为语义说明(勿改号)。
|
||
|
||
| 事件 ID | 常量名 | Payload | 订阅方 |
|
||
| :--- | :--- | :--- | :--- |
|
||
| 15 | `BOSS_PHASE_CHANGED` | `{boss_id, phase, bgm_layer}` | HUD(阶段提示)、AudioManager(BGM 切换)|
|
||
| 16 | `BOSS_KILLED` | `{boss_id, wave_num}` | WaveManager(进入结算)、AchievementManager |
|
||
| 17 | `GAME_CLEARED` | `{elapsed_sec, wave_num}` | GameCycleManager(结算屏)、排行榜 |
|
||
|
||
---
|
||
|
||
## 7. 与现有系统的接口约定
|
||
|
||
| 接口 | 调用方向 | 说明 |
|
||
| :--- | :--- | :--- |
|
||
| `SpatialGrid.register_boss(id, pos, radius)` | BossManager → SpatialGrid | Boss 作为可被子弹命中的实体,每帧更新坐标 |
|
||
| `BulletManager._on_bullet_hit(bullet_id, target_id)` | BulletManager → BossManager | `target_id` 前缀区分:`enemy_xxx` vs `boss_xxx`(或通过 faction 位掩码判断)|
|
||
| `ZoneManager.spawn_zone(...)` | BossAttackPattern → ZoneManager | Boss 攻击模式可生成 Zone(如冰墙、火圈)|
|
||
| `VFXManager.play(vfx_id, pos, scale)` | BossInstance → VFXManager | 阶段切换 + 死亡演出 VFX |
|
||
| `SpellEvaluator.execute_boss_pattern(pattern_id, origin)` | BossAttackRegistry → SpellEvaluator | Boss 弹幕走标准 SpellEvaluator 路径,复用子弹生成管线 |
|
||
|
||
---
|
||
|
||
## 8. 目录规范
|
||
|
||
```text
|
||
scripts/autoloads/
|
||
boss_manager.gd # Autoload BossManager
|
||
scripts/domain/boss/
|
||
boss_def.gd # class_name BossDef (Resource)
|
||
boss_phase_def.gd # class_name BossPhaseDef (Resource)
|
||
boss_instance.gd # class_name BossInstance (RefCounted)
|
||
boss_attack_registry.gd # BossAttackRegistry(类比 SpellRegistry)
|
||
resources/bosses/
|
||
mini_boss_w8.tres
|
||
final_boss_w20.tres
|
||
resources/boss_patterns/
|
||
charge_slam.tres
|
||
spread_shot_3.tres
|
||
spiral_shot_8.tres
|
||
# ...
|
||
```
|