Files
spellforge/docs_dev/specs/2026-07-22-boss-phases-design.md
T
joywayerandClaude Opus 4.8 7989d8d0fd docs(spec): Boss 阶段+弹幕/位移攻击模式系统设计
阶段状态机 + 攻击模式注册表(环/扇/瞄准/旋转 + 冲刺/蓄力)+ 新建
EnemyBulletManager 敌方子弹 + 保留召唤援军 + 事件 29/30 + bosses.json
与设计器 Boss 分页。范围外:场地机制/通关演出/无敌帧/.tres。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 09:17:40 +08:00

14 KiB
Raw Blame History

Boss 阶段 + 弹幕/位移攻击模式系统 — 设计

日期2026-07-22 切片S6Boss 深度) 前置状态:Mana 系统 + 元进展已完成;Boss 目前只是 EnemyManager SoA 中 type=4Mini/W8/type=5(Final/W20)的厚血实体,唯一"阶段行为"是 66%/33% 跨阈值召唤 FAST 援军(wave_manager.gd)。本设计将其升级为数据驱动的多阶段状态机 + 攻击模式调度 + 敌方子弹子系统。 依赖EnemyManager、PlayerManager、PlayerStats、EventBus、VFXManager、SettingsManager、SpatialGrid、game_designer 编辑器插件。


0. 范围

范围内

  • 数据驱动的 Boss 多阶段状态机(血量阈值切换)。
  • 攻击模式注册表:瞬时远程弹幕(环/扇/瞄准/旋转)+ 位移/蓄力招式(冲刺/蓄力重击),带三段式生命周期与前摇预警。
  • 新增 EnemyBulletManager 敌方子弹子系统(伤害玩家)。
  • 保留现有"进阶召唤援军"手感(作为每阶段可选字段)。
  • 补齐专用事件 BOSS_PHASE_CHANGED / BOSS_KILLED
  • data/bosses.json + 设计器新增「Boss」分页(字段化、双语、行编辑器)。
  • 敌方子弹独立 MultiMesh 渲染层。

范围外(延后)

  • 场地机制(冰墙障碍、场地收缩)。
  • 通关演出 / GAME_CLEARED 事件 / Boss 死亡序列演出。
  • 玩家无敌帧系统(本轮敌方子弹单次命中即销毁,靠数值调低保证公平)。
  • .tres 资源(本工程纯 JSON,不引入)。
  • C# 热路径(沿用 GDScript,参照 R-08)。

1. 新增自动加载 EnemyBulletManager(敌方子弹)

平行于 BulletManager独立 SoA 池,不污染玩家子弹热路径(玩家子弹检测 vs 敌人 SpatialGrid;敌方子弹检测 vs 玩家)。

  • 文件:scripts/autoloads/enemy_bullet_manager.gd+ .uid),注册进 project.godot autoload。
  • SoA PackedFloat32Array,命名常量 ENEMY_BULLET_STRIDE = 7 [ x, y, vx, vy, lifetime, radius, damage ](索引 0..6)。
  • const MAX_ENEMY_BULLETS: int = 1024_readyresize(MAX_ENEMY_BULLETS * ENEMY_BULLET_STRIDE) + fill(0.0)P6-N3:禁止热循环内 new())。
  • PLAYER_HIT_RADIUS:玩家碰撞半径常量(估算 12px,与玩家标记视觉一致)。
  • _physics_process(delta)
    • 逐弹积分:x += vx*dty += vy*dtlifetime -= dt
    • lifetime <= 0_swap_and_pop
    • 否则对玩家碰撞:d² ≤ (radius + PLAYER_HIT_RADIUS)²(读 PlayerManager.get_position())→ PlayerStats.take_damage(damage)(走难度减伤)+ VFXManager.play("hit_spark", pos) + _swap_and_pop(单次命中即销毁)。
  • 接口:
    • spawn_bullet(pos: Vector2, vel: Vector2, lifetime: float, radius: float, damage: float) -> int(满容返回 -1)。
    • sync_multimesh(mm: MultiMesh)SoA 位置批量写入,visible_instance_count = active,与 BulletManager 同法)。
    • get_active_count() -> intreset()
  • combat_managerstart_game / close_shop_and_next_wave / _on_player_diedEnemyBulletManager.reset()(与 bullet/zone/minion 重置一致)。

2. 新增自动加载 BossManager(阶段 + 招式侧挂控制器)

Boss 实体仍留在 EnemyManager SoA(渲染/受击/HP 不变);BossManager 用侧 Dictionaryboss_id 存运行态,并接管 Boss 的移动

  • 文件:scripts/autoloads/boss_manager.gd+ .uid),注册进 project.godot autoload。
  • 运行态(每个活跃 Boss 一条):
    {
      boss_type: int,
      max_hp: float,
      phase: int,                 # 当前阶段(0 起)
      phase_defs: Array,          # 该 boss_type 的阶段配置(来自 bosses.json
      pattern_index: int,         # 当前阶段招式轮询索引
      cooldown: float,            # 距下次起手的冷却
      action: Dictionary,         # 当前进行中的招式状态(见 §3),空 = 无
    }
    
  • register_boss(boss_id, boss_type, max_hp)wave_manager._spawn_boss 生成 SoA Boss 后调用;初始化运行态、置 phase=0 并触发 phase 0 进入。
  • _ready 载入 data/bosses.json(缺失/格式错 push_error,纯数据驱动无回退);订阅 EventID.ENEMY_KILLED
  • _physics_process(delta)(有活跃 Boss 才进入循环体):对每个 Boss —
    1. _check_phase_transition:读 EnemyManager.get_hp_percent(id),按 phase_defshp_threshold 降序)检查是否跨入更深阶段;跨入 → 进阶(重置 pattern_index/cooldown、可选召唤援军、发 BOSS_PHASE_CHANGED)。
    2. _tick_movement:无进行中位移招式时,Boss 朝玩家缓慢漂移(move_speed * phase.move_speed_mult),经 EnemyManager.set_entity_pos 写回 SoA。
    3. _tick_action:推进当前招式(§3);无招式且 cooldown <= 0 → 选下一招式并启动。
  • _on_enemy_killedkilled_id 是注册 Boss → 发 BOSS_KILLED {boss_id, boss_type, wave} 并注销运行态。
  • reset():清空运行态;由 combat_manager 在 run 重置点调用。

EnemyManager 改动

  • 新增 set_entity_pos(id, pos)(写 SoA x/y)。
  • _gd_update_movement 跳过已注册 Boss 的 slotBossManager 全权驱动其移动)——通过 EnemyManager 侧的一个「外部驱动」id 集合(register_external_mover(id) / unregister_external_mover(id),类比精英寻路跳过),零额外开销(无 Boss 时集合空)。

wave_manager 改动

  • _spawn_boss 末尾调用 BossManager.register_boss(...) + EnemyManager.register_external_mover(_boss_id)
  • 移除 _update_boss_phase / _on_boss_phase_enter(阶段与召唤逻辑迁入 BossManager)。
  • 保留 _boss_id / _boss_wavekilled_id == _boss_id波次完成判定(不变)。

3. 招式模型:带生命周期的动作

每阶段 attack_patterns 是招式规格列表,BossManager 轮询启动。招式分两类,均含通用字段 kindcooldown(招式结束后进入的冷却秒数)。

3.A 瞬时远程弹幕(发一波即结束,Boss 期间继续漂移)

EnemyBulletManager.spawn_bullet 从 Boss 位置发射:

kind 行为 主要参数
ring 绕 360° 均匀 N 弹(开花/环) count, speed, bullet_damage, bullet_radius
spread 朝玩家方向 ±half_angle 的 N 弹扇形 count, half_angle, speed, bullet_damage, bullet_radius
aimed 瞄准玩家的单发/连发 count, speed, bullet_damage, bullet_radius(连发用 burst 间隔可后续扩展)
spiral 每次起手角度递增的旋转弹 count, speed, angle_step, bullet_damage, bullet_radius

瞬时招式的 action 生命周期只占一帧(发射后立即进冷却)。

3.B 位移 / 蓄力招式(跨多帧,三段式,接管 Boss 移动)

三段式:windup 前摇 → active 生效 → recover 后摇action 状态含 { kind, stage, timer, ... },逐帧推进。

kind 行为 主要参数
dash(冲刺) 前摇:静立 + 播预警 VFX(给玩家闪避窗口);生效:以 dash_speed 冲向前摇锁定的玩家位置,冲刺中与玩家重叠 → PlayerStats.take_damage(contact_damage)(带内部命中冷却 contact_hit_cd 防每帧多段);后摇:静立恢复。 windup, dash_speed, dash_time, contact_damage, contact_hit_cd, recover, telegraph_vfx
charge_up(蓄力) 前摇:静立蓄力 + 播预警 VFX;生效:释放一次重击(大 ring 或多重 spread,由 release_kind + 弹幕参数决定);后摇:静立恢复。 windup, release_kind, count, speed, half_angle, bullet_damage, bullet_radius, recover, telegraph_vfx

公平性约束:所有位移/蓄力招式必须windup 前摇与 telegraph_vfx 预警,否则冲刺不可闪避。

招式注册表实现

BossManager 内置 pattern_id/kind → 处理逻辑(行为在代码,调参在 JSON,类比 status/spell)。瞬时招式为「发射函数」;位移/蓄力招式为「阶段推进器」。不引入独立 Resource / 注册 autoload,逻辑内聚在 BossManager(招式数量少,MVP 阶段)。


4. 数据与设计器

4.1 data/bosses.json

{
  "4": {
    "move_speed": 120.0,
    "phases": [
      {
        "hp_threshold": 1.0,
        "move_speed_mult": 1.0,
        "summon_adds": 0,
        "attack_patterns": [
          { "kind": "spread", "count": 5, "half_angle": 40.0, "speed": 220.0, "bullet_damage": 8.0, "bullet_radius": 8.0, "cooldown": 2.5 },
          { "kind": "dash", "windup": 0.8, "dash_speed": 600.0, "dash_time": 0.5, "contact_damage": 15.0, "contact_hit_cd": 0.4, "recover": 0.8, "telegraph_vfx": "boss_telegraph", "cooldown": 3.0 }
        ]
      },
      {
        "hp_threshold": 0.66,
        "move_speed_mult": 1.2,
        "summon_adds": 6,
        "attack_patterns": [ ... ]
      },
      {
        "hp_threshold": 0.33,
        "move_speed_mult": 1.5,
        "summon_adds": 8,
        "attack_patterns": [ ... ]
      }
    ]
  },
  "5": { "move_speed": 80.0, "phases": [ ... ] }
}
  • 顶层键为 boss_type"4"=Mini、"5"=Final),与 waves.jsonboss 字段、balance.jsonboss_hp 键一致。
  • phaseshp_threshold 降序(1.0 → …)。
  • Boss 基础血量仍取 balance.jsonboss_hp(不迁移,避免破坏现有平衡数据);bosses.json 只负责阶段/招式/移动。
  • SettingsManager 加 getter(如 get_boss_phase_config(boss_type) -> Array);BossManager._ready 经其读取。缺失/格式错 push_error,无静默回退。

4.2 设计器「Boss」分页

按 CLAUDE.md「新增分页 3 步」:

  1. addons/game_designer/boss_tab.gd@tool extends VBoxContainer,复用 designer_ui.gdspin/line/opt/btn/header/set_status/load_json/save_json;在 _ready() 构建 UI,非 _init())。
  2. designer_panel.gd_init()_add_tab("Boss", ...) 注册。
  3. Manager _load_json_bosses()_ready() 载入。

字段化要求(遵守插件规范):

  • 所有枚举/选项中英双语、中文在前kind环 ring / 扇形 spread / 瞄准 aimed / 旋转 spiral / 冲刺 dash / 蓄力 charge_up)。
  • OptionButton 存 key(如 ring),把双语显示串写进 JSON(显示标签数组与存储值数组分离,index 对齐)。
  • 数值用 SpinBox,撑满宽度(SIZE_EXPAND_FILL,工厂已内置)。
  • 可变长的阶段列表 / 招式列表用增删行 UI(加 / 🗑删);如结构过深,MVP 可先做「按 boss_type 选择 → 阶段列表 → 每阶段招式行编辑」的分层表单。禁止手写 JSON 文本框
  • 数据往返一致、防丢键;可选字段为默认值时可省略。

5. 事件

event_ids.gd 从 29 起新增(同步更新 docs/technical/implementation_plan.md §2.1 事件目录):

ID 常量 Payload 订阅方
29 BOSS_PHASE_CHANGED { boss_id, boss_type, phase } HUD(阶段提示)、AudioManager(可选 BGM
30 BOSS_KILLED { boss_id, boss_type, wave } WaveManager/成就(可选)

GAME_CLEARED 延后(范围外)。沿用现有 BOSS_SPAWNED = 19


6. 渲染

combat_s2.gd

  • _setup_world_view 增第三个 MultiMeshInstance2D + _enemy_bullet_mminstance_count = EnemyBulletManager.MAX_ENEMY_BULLETS),modulate 用红/紫区分玩家子弹(黄)。
  • _processEnemyBulletManager.sync_multimesh(_enemy_bullet_mm)

7. 跨切片约束(P6-N)遵守

  • ENEMY_BULLET_STRIDE 命名常量,禁裸整数。
  • 热循环(integrate/collision)不 new()SoA 预分配;_swap_and_pop 就地搬运。
  • VFX one_shotP6-N72)。
  • EventID 从常量取,禁硬编码整数;顺序递增(29/30)。
  • CoreFeatureTag / StatusID 等仍用枚举,不引入裸串。
  • 纯数据驱动:bosses.json 缺失/格式错 push_error,无硬编码回退副本。

8. 验证

  • godot-mcp-pro validate_script 校验新脚本语法。
  • play_scene 跳到 W8/W20(或直接 WaveManager 起 boss 波),execute_game_script 断言:
    • 敌方子弹生成并使 PlayerStats.hp 下降(ring/spread/aimed/spiral 各验一次)。
    • dash 前摇静立→冲刺位移→接触扣血(带命中冷却,不每帧多段);charge_up 前摇→释放弹幕。
    • HP 跨 66%/33%(或配置阈值)发 BOSS_PHASE_CHANGED;进阶召唤援军数正确。
    • Boss 死亡发 BOSS_KILLED,波次照常完成。
  • 设计器 Boss 分页对真实 bosses.json 跑回读往返(execute_editor_script 断言 FAILS=0);改 dock GDScript 后需 EditorInterface.restart_editor(true) 清缓存再测。

9. 目录 / 文件清单

scripts/autoloads/
  enemy_bullet_manager.gd (+ .uid)   # 新增 autoload
  boss_manager.gd         (+ .uid)   # 新增 autoload
scripts/autoloads/enemy_manager.gd   # +set_entity_pos / +register_external_mover / _gd_update_movement 跳过
scripts/autoloads/wave_manager.gd    # _spawn_boss 注册 BossManager;移除旧 _update_boss_phase/_on_boss_phase_enter
scripts/autoloads/event_ids.gd       # +BOSS_PHASE_CHANGED=29 / +BOSS_KILLED=30
scripts/autoloads/settings_manager.gd# +get_boss_phase_config 等 getter
scripts/domain/combat/combat_manager.gd # reset EnemyBulletManager/BossManager
scenes/main/combat_s2.gd             # 敌方子弹 MMI + sync
data/bosses.json                     # 新增数据
addons/game_designer/boss_tab.gd (+ .uid)  # 新增设计器分页
addons/game_designer/designer_panel.gd     # _add_tab 注册
project.godot                        # 注册两个 autoload + 分页无关
docs/technical/implementation_plan.md# §2.1 事件目录 +29/+30
docs/technical/boss_design.md        # 更新实现现状注记(改文档追认)