58 KiB
魔法工匠:骨架垂直切片开发计划
Arcane Artificer — Skeleton Vertical Slice Development Plan
策略定义:骨架垂直切片 (Skeleton Vertical Slice) 不是"先把所有系统建完再组合"(横向分层),而是先用最薄的一刀从输入到渲染切穿所有层,证明核心体验可运行;再每轮迭代让这刀"更宽"——加入更多内容与深度。
- 骨架:最小可运行的端到端技术脚手架,不含游戏内容
- 垂直切片:每个里程碑均可独立游玩与测试,交付"小但可玩的游戏"
- 顺序原则:①风险最高的系统最先实现;②每个切片回答一个明确的"能做到吗"问题;③下一个切片依赖上一个切片的通过
一、技术风险登记表 (Risk Register)
驱动切片顺序的核心不确定性:
| 风险 ID | 风险描述 | 影响系统 | 严重度 | 解决切片 |
|---|---|---|---|---|
| R-01 | PackedFloat32Array SoA 在 GDScript 下 2000 弹幕能否达到 60fps |
BulletManager | 🟢 已缓解 | S0 ✅ |
| R-02 | SpellEvaluator 的 while 循环与 SubPayloadRegistry 在高频施法下无 GC 抖动 | SpellEvaluator | 🟢 已缓解 | S0~S1 ✅ |
| R-03 | 1000 敌人的 Boid AI + SpatialGrid 查询帧时间是否可控 | EnemyManager | 🟢 已缓解 | S0 ✅ |
| R-04 | Area2D 信号回调是否足够处理 500-2000 弹幕碰撞(切换阈值验证) | BulletManager/碰撞 | 🟡 部分 | S1 ✅(当前用 SpatialGrid;完整阈值验收留 S6) |
| R-05 | compile_wand 预编译后 CompiledDeck 与 SubPayloadRegistry 竞争条件实测 |
SpellEvaluator | 🟢 已缓解 | S2~S3 ✅ |
| R-06 | CIRCUIT 拓扑 _flatten_circuit + 嵌套 LOGIC_FORK 实际运行正确性 |
SpellEvaluator | 🟡 中 | S5 |
| R-07 | GPUParticles2D 池化(VFXManager)与 MultiMesh 渲染切换性能实测 |
VFXManager/渲染 | 🟡 部分 | S4 ✅(色块占位池化;粒子素材与 MultiMesh 留 S6) |
| R-08 | GDScript ↔ C# 跨语言边界开销实测:PackedFloat32Array.AsSpan() 零拷贝是否生效、批量通知 vs 逐条回调性能差异;从 S0 建立 C# 项目基线(ADR-L1) |
BulletManager/EnemyManager/SpatialGrid | 🟠 高 | S0 |
| R-09 | W15+ Elite 需 NavigationAgent2D 或 FlowField 与 Boid SoA 共存,帧时间增量是否可控(ADR-A4) |
EnemyManager / 关卡设计 | 🟡 中 | S5 |
二、切片总览 (Slice Overview)
S0 技术骨架 → 证明:ECS 架构能运行 ✅ 已完成 (2026-06)
S1 最小战斗 → 证明:子弹打到敌人,敌人死亡 ✅ 已完成 (2026-06)
S2 核心游戏循环 → 证明:波次→商店→升级→下一波 完整闭合 ✅ 已完成 (2026-06)
S3 法术管道 → 证明:ACTION+MODIFIER+TRIGGER 链可运行 ✅ 已完成 (2026-06)
S4 战斗深度 → 证明:状态效果、VFX、DPS 系统协同 ✅ 已完成 (2026-06)
S5 高级构建 → 证明:MATRIX/CIRCUIT Core、LOGIC、召唤、地面效果 ✅ 功能完成 (P-S5-01~07+AI-01 实测通过;唯 C# 压力 P-S5-ZM-01 顺延)
S6 润色发布 → 证明:60fps 稳定、内容充足、手感好 ⬜ 未开始
当前主场景:res://scenes/main/combat_s2.tscn(S2 起)
每个切片结束前,下列问题必须回答"是"才可进入下一个切片(出口检查 Exit Gate)。
实施进度摘要
| 切片 | 状态 | 主场景 / 入口 | 备注 |
|---|---|---|---|
| S0 | ✅ | combat_test.tscn |
GDScript SoA 回退路径实测通过;architecture_design.md §4.5 已填 S0 实测值(Bullet 0.37ms / Enemy 0.23ms) |
| S1 | ✅ | combat_test.tscn |
碰撞为 SpatialGrid 数据驱动(非 Area2D);spark_bolt + 对象池验收通过 |
| S2 | ✅ | combat_s2.tscn |
CombatManager FSM、Wave/Shop/Profile A/B 双槽;MODIFIER 分支 |
| S3 | ✅ | combat_s2.tscn |
SubPayloadRegistry、execute_sub、multicast;子弹冷数据 on_hit_payload_id |
| S4 | ✅ | combat_s2.tscn |
StatusManager / VFXManager / DpsTracker;fire_bolt/poison_dart;蓄力边沿 |
| S5 | ✅* | combat_s2.tscn |
①LOGIC ②CIRCUIT ③Zone+Minion ④MATRIX+共鸣 ⑤精英寻路 全部完成;P-S5-01~07 + P-S5-AI-01 实测通过(2026-06-05)。*唯 P-S5-ZM-01(C# 压力)按 R-08 顺延 |
| S6 | ⬜ | — | — |
⚠️ 实现方式偏差备忘(代码核对 2026-06-05,全切片适用)
- 纯数据驱动架构全面落地 ✅(2026-06-05):游戏设计器 EditorPlugin(
addons/game_designer/,编辑器底部面板,7 个可视化分页:法术/敌人/波次/Core/共鸣/状态/平衡)+res://data/*.json(7 个数据文件),策划/美术/开发零代码配置全部游戏内容。已移除所有硬编码内容副本与回退——JSON 是唯一权威源,缺失/格式错误时push_error明确报错(不静默)。各 Manager 纯加载:SpellRegistry←spells.json(20法术)、EnemyManager←enemies.json(6原型)、WaveManager←waves.json(20波)、WandPreset←cores.json(Core)、SpellEvaluator←resonance.json(共鸣)、StatusRegistry←status_effects.json(状态)、SettingsManager←balance.json(乘子+Boss血)。WandPreset删除全部make_spell_*/硬编码 Core;SpellRegistry删.tres扫描+builtins;ShopManager删 fallback+过滤 shop_cost>0。实测:游戏完整运行(法术施放/商店/状态/战斗)0 报错,所有内容来自 JSON(_SPEED/WAVE_CONFIG初始空,证明无硬编码副本)。文档handbook/07_game_designer.md。余项:运行时热重载(目前需 F5 重启)、玩法常量配置(玩家速度/商店价/掉落等仍为代码常量,属可调参数非内容)。- UI 全程序化构建:
shop.tscn/hud.tscn/inventory.tscn/hud_dps.tscn均不存在;HUD 与商店在scenes/main/combat_s2.gd内用Label.new()/Button.new()动态生成。- S3 拖拽背包 UI(
inventory.tscn)缺失:法术目前仅能经商店点击购买,无拖拽装配。- C# 热路径仍为骨架:
csharp/三文件(Bullet/Enemy/SpatialGrid Cs)未承载实际热循环,运行走 GDScript 回退路径(与 R-08 标注一致)。
S0 — 技术骨架 (Technical Skeleton) ✅ 已完成
目标
"空荡荡的竞技场里,一颗子弹能在 2000 颗同屏的情况下飞出去,并在 Profiler 中证明帧时间 < 16ms。"
技术上没有游戏性,只有数据流动与性能验证。
范围
| 要实现的系统 | 最小实现内容 | 暂不实现 |
|---|---|---|
EventBus / EventID |
整数 ID 常量 + 简单 emit/subscribe | 事件类型校验、跨语言边界 |
BulletManager |
PackedFloat32Array SoA (STRIDE=12)、spawn/despawn、匀速位移积分、swap-and-pop 删除 | 碰撞、归航、弹射、VFX |
EnemyManager |
SoA (STRIDE=8)、spawn/despawn、直线追玩家移动、_visible_flags LOD;屏外低频 Boid 分离力(_update_separation_only,OFFSCREEN_SEPARATION_INTERVAL=10,P6-N1) |
AI 行为状态机、攻击、死亡动画 |
SpatialGrid |
固定 Cell 网格、每帧 dirty-list 方案重建、query_circle |
超大弹体豁免路径(留 stub) |
PlayerManager |
WASD 移动(MOVE_THRESHOLD_NORM)、位置查询接口 |
法杖施法、蓄力、冲刺 |
ObjectPool (通用) |
Array 栈 + reset() 接口约定 |
无 |
TimeManager |
时间缩放接口、GameTick 基础计数(implementation_plan.md §2.1 核心库 Layer 0 表);Autoload 注册 |
帧率节流、RenderFrame 分离 |
| C# 项目初始化 | .csproj 配置;BulletManagerCs / EnemyManagerCs / SpatialGridCs 空骨架挂为对应 GDScript Autoload 子节点;PackedFloat32Array.AsSpan() 零拷贝读写基准验证(ADR-L1) |
完整热路径逻辑(S1 起逐步填充) |
| 渲染同步 | N 个敌人 Node2D.position 帧末同步(_visible_flags 筛选) |
动画、特效 |
关键文件
scripts/autoloads/event_id.gd # EventID 常量 (参考 implementation_plan.md §2.1)
scripts/autoloads/event_bus.gd # EventBus Autoload
scripts/autoloads/bullet_manager.gd # SoA BulletManager GDScript 接口层 (architecture_design.md §4.2)
scripts/autoloads/enemy_manager.gd # SoA EnemyManager GDScript 接口层 (implementation_plan.md §2.3.C)
scripts/autoloads/time_manager.gd # TimeManager Autoload (implementation_plan.md §2.1 Layer 0)
scripts/core/object_pool.gd # 通用对象池 (implementation_plan.md §2.1 Layer-0 ObjectPool)
scripts/domain/player_manager.gd # 移动 + 位置接口
csharp/autoloads/BulletManagerCs.cs # C# 热路径内核:SoA 位移积分(S0 骨架,S1 起填充)
csharp/autoloads/EnemyManagerCs.cs # C# 热路径内核:Boid 分离力(S0 骨架)
csharp/systems/SpatialGridCs.cs # C# SpatialGrid:重建 + query_circle(S0 建立)
scenes/main/combat_test.tscn # 调试场景(无 UI)
验收标准
- P-S0-01:同屏 2000 颗子弹(
BulletManager._active_count = 2000),Godot Profiler_physics_process帧时间 < 8ms(留余量给 EnemyManager) - P-S0-02:同屏 1000 敌人(含 Boid 分离力低频更新),EnemyManager 帧时间 < 6ms
- P-S0-03:子弹 swap-and-pop 删除不产生位置跳变(视觉无闪烁)
- P-S0-04:
SpatialGrid.query_circle1000 敌人下单次查询 < 0.1ms - P-S0-05:GDScript Profiler 中无每帧
new()GC 分配(验证 SoA 不产生 RefCounted 临时对象) - P-S0-06:C# 基线验证:
PackedFloat32Array.AsSpan()在 C# 侧读写数据与 GDScript 侧值一致;1000 次空跨语言Call()基准总耗时记录在案(确认批量通知策略 ADR-L1 规则 3 的必要性)— 骨架已建,S0 性能以 GDScript 回退路径验收 - P-S0-07:Godot Profiler 截图存档并回填帧预算表:
BulletManagerCs/EnemyManagerCs/SpatialGridCs三系统实测帧时间写入architecture_design.md §4.5的"S0 实测值"列;git commit 须包含 Profiler 截图(或等效文字记录)。此项为 S0 出口硬性要求——表格中"—"未替换为实测值则不得进入 S1。 — 已填 GDScript 回退实测:Bullet 0.37ms、Enemy 0.23ms、SpatialGrid <0.001ms
技术参考
architecture_design.md §4.1~4.3(Low-GC 原则、BulletManager SoA、SpatialGrid)implementation_plan.md §2.3.C(EnemyManager SoA、ENEMY_STRIDE=8、dirty-list)implementation_plan.md §4(Q1~Q4 技术难点预案)
出口检查
✅ 已通过(2026-06):R-01/R-03 在 GDScript 回退路径下达标;
architecture_design.md §4.5S0 实测值已回填。 R-04 说明:S0/S1 以 SoA 与低弹数验证为主;Area2D 与 500–2000 弹幕阈值、SpatialGrid 主力路径及渐进切换的 完整压力验收 放在 S6 P0(与architecture_design.md §4.3~§4.4一致),避免与 S1 最小战斗范围混淆。
S1 — 最小战斗 (Minimum Combat) ✅ 已完成
目标
"玩家站在场景里,自动发射魔法飞弹,子弹命中敌人造成伤害,敌人 HP 归零后死亡,屏幕显示击杀数。"
范围
| 要实现的系统 | 最小实现内容 | 暂不实现 |
|---|---|---|
SpellEvaluator (阶段一:编译) |
compile_wand(core, raw_deck) → LINEAR 拓扑直接包装 CompiledDeck |
MATRIX/CIRCUIT 拓扑、共鸣 |
SpellEvaluator (阶段二:执行) |
execute_compiled 主 while 循环、SpellType.ACTION 分支、_push_projectile |
MODIFIER/TRIGGER/LOGIC 节点 |
SpellContext / CastStats |
完整字段 + reset();对象池 32 个(SPELL_CONTEXT_POOL_SIZE=32) |
寄存器持久化 |
ProjectileDef |
完整字段 + reset();对象池 |
穿透、弹射、归航 |
DamageContext / DamageContextPool |
独立文件 + acquire()/release()/reset()(含 ctx.reset() 前置调用) |
source_tags 细分 |
| 碰撞检测 | Area2D 方案:子弹 CircleShape2D + body_entered 信号 → APPLY_DAMAGE 事件 |
SpatialGrid 回退路径 |
EnemyManager.apply_damage |
接收 damage_context_id、从池取 context、计算 ((Base+Add)×Mult)×(1-Res)-Armor、HP 扣减 |
暴击特效、元素反应 |
| 敌人死亡 | HP ≤ 0 时 swap-and-pop 移除;ENEMY_KILLED 事件 |
死亡动画、掉落物 |
CoreDefinition / SpellNode (Tier 1) |
spark_bolt ACTION 节点;wand_basic LINEAR Core 5 槽 |
MODIFIER/TRIGGER/LOGIC 节点 |
ConfigMgr |
FileAccess JSON 读取、键值查询接口;Autoload(architecture_design.md §2 数据层) |
热重载、分包配置 |
| HUD(极简) | 击杀计数 Label | 血条、法力条、波次计时器 |
关键文件
scripts/domain/spell_system/
spell_context.gd # class_name SpellContext
cast_stats.gd # class_name CastStats
spell_node.gd # SpellType enum + class_name SpellNode
projectile_def.gd # class_name ProjectileDef
spell_evaluator.gd # compile_wand() + execute_compiled()
compiled_deck.gd # class_name CompiledDeck
scripts/autoloads/
damage_context.gd # class_name DamageContext (独立文件!)
damage_context_pool.gd # Autoload DamageContextPool (不声明 class_name)
spell_context_pool.gd # Autoload SpellContextPool
spell_registry.gd # Autoload SpellRegistry
config_mgr.gd # Autoload ConfigMgr - JSON 配置加载器 (architecture_design.md §2)
resources/
cores/wand_basic.tres # LINEAR 5槽
spells/action_spark_bolt.tres # Tier 1 ACTION
验收标准
- P-S1-01:
spark_bolt子弹飞出,命中 50 HP 敌人造成 3 点伤害,HP 变为 47 - P-S1-02:敌人 HP ≤ 0 时消失,击杀计数 +1
- P-S1-03:
DamageContextPool无内存泄漏(acquire 后必 release,pool size 稳定不增长) - P-S1-04:
SpellContext池化正常,pool 大小不超过 32 个分配(Profiler 无每帧 new) - P-S1-05:Area2D 方案下 500 颗子弹 + 100 敌人无帧率下降(保 S0 基线)— 实测 SpatialGrid 碰撞,~145 FPS
技术参考
architecture_design.md §3.2(SpellNode、SpellContext、ProjectileDef、DamageType 枚举)implementation_plan.md §2.1(DamageContext P6-N49 独立文件要求、DamageContextPool P6-N67)combat_mechanics_depth.md §4(伤害公式 FinalDamage)implementation_plan.md §2.2(compile_wand P6-N65 参数类型)
出口检查
✅ 已通过(2026-06):
wand_basic+spark_bolt闭环;对象池无泄漏。 R-04:本切片 P-S1-05 仅验证中低密度(如 500 弹 + 100 敌);高密度碰撞与渲染分档在 S6 与架构 §4.3~4.4 一并验收。
S2 — 核心游戏循环 (Core Game Loop) ✅ 已完成
目标
"能完整体验:战斗(一波敌人)→ 波次结算(金币+XP)→ 商店(买一张法术卡并装备)→ 下一波;循环可无限重复。"
范围
| 要实现的系统 | 最小实现内容 | 暂不实现 |
|---|---|---|
WaveManager |
按波次配置生成敌人、倒计时、WAVE_COMPLETE 事件 |
Boss 波、精英变体 |
ShopManager |
每波结算后弹出商店(3 选 1 法术)、金币扣减、刷新价格公式(P6-N23)、WAVE_COMPLETE 后重置刷新次数 |
多 Core 商店、道具 |
PlayerStats |
hp_max、mana_max、cpu_limit、XP/金币计数;升级曲线 ⌊10×1.4^(level-1)⌋(P6-N16) |
attunement 属性、luck |
| 掉落系统 | 击杀时在原位生成金币/XP 拾取物;5 秒未拾取消失 | 特殊掉落池 |
| Core 装备管理 | 玩家背包持有 1 个 Core;商店购买法术后写入 Core 插槽并触发 compile_wand 重编译 |
多 Core 切换(最多 3 个) |
| HUD(基础) | HP 条、法力条、XP 条、金币、当前波次 | 蓄力进度环、DPS 面板 |
ProfileManager / GameCycleManager |
局内 Run 存档(ADR-A2):user://run_a.json / user://run_b.json A/B 双槽交替写入;字段含 schema_version、wave_num、shop_seed、cores、active_core_idx 等(见 architecture_design.md ADR-A2);load_run() 解析后 必经 _migrate_run();NOTIFICATION_WM_CLOSE_REQUEST 尽力写最后一笔;波次边界 / 商店关闭与架构文档存档时机对齐 |
Run 截图、HMAC 排行榜(属 Endless,见 S6) |
| Tier 1-2 法术 | spark_bolt、energy_orb、double_cast、spread_mod、damage_plus(≥5 张可购买) |
Trigger/LOGIC 类 |
关键文件
scripts/autoloads/
wave_manager.gd
shop_manager.gd
player_stats.gd
drop_manager.gd
profile_manager.gd
scripts/domain/combat/
combat_manager.gd # 游戏循环状态机 (战斗→结算→商店)
# game_cycle_manager.gd # 若与 combat_manager 分文件:负责 save_run/load_run A/B 槽与 WM_CLOSE(ADR-A2)
scenes/
main/combat.tscn # 正式战斗场景
ui/shop.tscn
ui/hud.tscn
resources/spells/
action_energy_orb.tres
modifier_double_cast.tres
modifier_spread_mod.tres
modifier_damage_plus.tres
resources/enemies/
enemy_basic.tres # Wave 1 杂鱼(HP=15, Speed=100, Dmg=5)
enemy_charger.tres # Wave 5 冲锋甲虫
验收标准
- P-S2-01:Wave 1 生成 20 只杂鱼,全部击杀后触发
WAVE_COMPLETE,金币/XP 正确发放 - P-S2-02:商店显示 3 张随机法术卡,购买
damage_plus后装入 Core 槽位,下一波spark_bolt伤害提升(3 + 10 = 13) - P-S2-03:XP 曲线正确:Level 1→2 需 10 XP;Level 5→6 需 54 XP(P6-N16 公式)— 实测 Lv5→6 需 38 XP(公式
roundi(10×1.4^(lv-1))) - P-S2-04:商店第 1 次刷新 20G,第 2 次 30G,Wave 结算后重置(P6-N23)
- P-S2-05:3 波循环完整无崩溃;SpellDeck JSON 存档后重载数据一致 —
ProfileManagerA/B 双槽run_a.json/run_b.json - P-S2-06(ADR-A2):模拟写入
run_a.json过程中杀进程,重进游戏应能从未损坏槽恢复上一完整波次/商店状态;schema_version变更时旧档经_migrate_run可读 — 双槽交替写入已验证 - P-S2-07(ADR-A2):
shop_seed存盘后重载,商店随机展示与刷新序列与断线前一致(防刷种作弊链与架构一致)
技术参考
architecture_design.mdADR-A2(局内存档 A/B、迁移、WM_CLOSE)implementation_plan.md §2.2(SubPayloadRegistry 竞争条件保证 P6-N10,禁止战斗中热换牌)numerical_design.md §1.2(XP 曲线公式 P6-N16、商店刷新公式 P6-N23)game_design.md §3.1.A(多 Core 规则、战斗中不可切换)
出口检查
✅ 已通过(2026-06):3 波循环无崩溃;A/B 存档与
shop_seed验证通过。
S3 — 法术管道 (Spell Pipeline) ✅ 已完成
目标
"能构建 'damage_plus × 2 → trigger_hit → spark_bolt' 的子母弹链,子弹命中后在命中点再发射一颗 spark_bolt。"
范围
| 要实现的系统 | 最小实现内容 | 暂不实现 |
|---|---|---|
SpellEvaluator — MODIFIER |
SpellType.MODIFIER 分支:node.apply(context) 修改 CastStats |
隐式 MODIFIER(矩阵邻接) |
SpellEvaluator — TRIGGER |
SpellType.TRIGGER 分支:deck.consume_until_scope_end() → SubPayloadRegistry.register() |
多层 TRIGGER 嵌套 |
SubPayloadRegistry |
register/lookup(只读,预编译阶段构建;P6-N10 竞争条件保证) | 无 |
BulletContext (冷数据) |
_bullet_contexts: Dictionary;存储 on_hit_payload_id、pierce_remaining、bounce_remaining |
归航、正弦弹道 |
BulletManager._on_bullet_hit |
命中后查 on_hit_payload_id → 调用 SpellEvaluator.execute_sub(payload_id, hit_pos, owner_id) |
击杀触发、超时触发 |
execute_sub |
深度检查(MAX_TRIGGER_DEPTH=3);超限时发 SPELL_CAST_BEGIN{depth_exceeded=true} |
无 |
Multicast (double_cast) |
MODIFIER 语义:从 deck 多弹 N 个 ACTION;尽力策略(不足时不崩溃) | 无 |
| 穿透 / 弹射 | pierce_remaining、bounce_remaining 冷数据管理;弹射衰减在 apply_damage(P6-N34) |
连锁排斥列表 |
| Tier 2 Trigger 法术 | trigger_hit(击中触发);trigger_on_kill(击杀触发) |
Timer 触发 |
| 背包 UI(功能版) | 拖拽法术卡入 Core 插槽;实时调用 compile_wand 重编译 |
共鸣预览、CIRCUIT 电路图 |
关键文件
scripts/domain/spell_system/
sub_payload_registry.gd # Autoload
spell_deck.gd # class_name SpellDeck(含 _consumed 掩码权威实现)
scripts/autoloads/
bullet_context_pool.gd # BulletContext 对象池
resources/spells/
trigger_on_hit.tres
trigger_on_kill.tres
modifier_bounce.tres
modifier_pierce.tres
modifier_homing.tres
action_shotgun_blast.tres
scenes/ui/
inventory.tscn # 背包拖拽 UI
验收标准
- P-S3-01:
[damage_plus] → [trigger_on_hit] → [spark_bolt]配置:命中敌人后在命中点额外产生一颗 spark_bolt - P-S3-02:
[double_cast] → [spark_bolt] → [energy_orb]配置:单次施法产生 spark_bolt + energy_orb 两颗弹 - P-S3-03:TRIGGER 嵌套深度 > 3 时,HUD 显示"法术链太深"黄色提示,不崩溃
- P-S3-04:
execute_sub期间不产生新SpellContext分配(从池取用) - P-S3-05:
BulletContext在子弹死亡时正确归还对象池(无内存泄漏,Profiler 验证)— 冷数据存_bullet_contextsDictionary,swap-and-pop 时同步清理 - P-S3-06:
SubPayloadRegistry在关卡进行中只读(背包关闭后才重编译),无竞争条件
技术参考
architecture_design.md §3.4.A(compile_wand 权威流程、SubPayloadRegistry 生命周期)architecture_design.md §3.2.B(SpellEvaluator 执行流程、MAX_OPS/MAX_TRIGGER_DEPTH)implementation_plan.md §2.2(SubPayloadRegistry P6-N10、SpellDeck.pop() P6-N56)combat_mechanics_depth.md §5(BulletContext 职责:SRP,不含 calc_damage,P6-N34)
出口检查
✅ 已通过(2026-06):子母弹链与 multicast 实测通过;
lock_for_battle()防竞争。 背包 UI(2026-06-05 完成):计划的拖拽背包以点选式插槽编辑器实现(程序化构建于combat_s2.gd,非独立inventory.tscn)——商店内「🎒 背包」按钮打开;拓扑感知插槽网格(LINEAR 单行 / MATRIX 行A·行B 网格 / CIRCUIT 按槽)+ 备牌区;点选源再点目标完成 插槽↔插槽交换 / 插槽→备牌 / 备牌→插槽,每次操作即时重编译装备。实测:交换、移入/移出备牌、LINEAR 修正器序、MATRIX 行布局均正确。
S4 — 战斗深度 (Combat Depth) ✅ 已完成
目标
"给敌人施加燃烧(DoT),VFX 有命中火花;HUD 实时显示 DPS;
charge_triggered停步蓄力正确只触发一次。"
范围
| 要实现的系统 | 最小实现内容 | 暂不实现 |
|---|---|---|
StatusManager |
平铺数组 _active_statuses;逆序 while 循环 + swap-back-pop 移除;while + -= tick_interval DoT 跳字(P5-N1);分帧批处理(STATUS_BATCH_LIMIT=500) |
状态催化反应、元素弱点 |
StatusTypeDef |
.tres 数据驱动(id/stack_mode/max_stacks/tick_interval/can_catalyze=[]);_ready() 自动扫描加载 |
元素免疫 faction 掩码 |
StatusID Autoload |
BURN=1/FREEZE=2/POISON=3/WET=4/OILY=5/STUN=6/COMBO_MARK=7/VULNERABILITY=8 | 新增 ID 从 9 起递增 |
| BURN / POISON 法术 | action_fire_bolt(施加 BURN)、action_poison_dart(施加 POISON) |
FREEZE、WET、STUN |
VFXManager |
_vfx_scenes 预加载表、_instantiate_vfx(one_shot=true,P6-N72)、_active_count O(1) 计数(P6-N60);play("hit_spark"/...)" |
Godot 粒子素材(用临时色块代替) |
PlayerManager 输入状态 |
_charge_triggered: bool;CHARGE_FIRED(ID=14)一次性发出(P6-N69);CHARGE_STATE_CHANGED(ID=9)仅供 UI;DASH_TRIGGERED(ID=13)冲刺边沿检测(含 stationary_time 字段,P6-N36) |
冲刺波 Core 具体法术(S5/S6) |
| DPS 面板 | _RB_SIZE=256 环形缓冲区;懒加载 _recalc_window;UI 每 0.5s 轮询(P6-N38、P6-N47) |
Run 截图 |
连击叠加 (COMBO_MARK) |
激光武器命中时施加 COMBO_MARK(stack_mode=1, tick_interval=999.0);apply_damage 中读取层数乘加成 |
VULNERABILITY 叠层 |
| 玩家受击 | ON_PLAYER_HURT(ID=10)事件;PLAYER_DAMAGED(ID=5);HP 扣减 |
无敌帧、特殊受击法术 |
关键文件
scripts/autoloads/
status_manager.gd
status_id.gd # Autoload StatusID
vfx_manager.gd # 含 _instantiate_vfx 完整实现
scripts/domain/
status_type_def.gd # class_name StatusTypeDef
status_instance.gd # class_name StatusInstance
resources/
status_types/
burn.tres
poison.tres
combo_mark.tres
spells/
action_fire_bolt.tres
action_poison_dart.tres
scenes/ui/
hud_dps.tscn # DPS 标签 + 蓄力进度环
验收标准
- P-S4-01:
fire_bolt命中敌人 → 敌人获得 BURN → 每秒跳字一次(tick_interval=1.0)直到 duration 耗尽 - P-S4-02:同屏 200 个 BURN 实例,StatusManager
_physics_process帧时间 < 1.5ms(architecture_design.md §4.5+implementation_plan.md §2.4统一预算)— 实测 ~0.153ms/帧 - P-S4-03:VFXManager
play("hit_spark", ...)正常触发;MAX_ACTIVE_VFX=200超出时静默丢弃(无崩溃) - P-S4-04:玩家静止 1.5s →
CHARGE_FIRED恰好发送一次;继续静止不再发送;移动后再次静止 1.5s 可再次触发(P6-N69) - P-S4-05:DPS 面板数值与实际持续输出相符(±10% 误差),
get_dps()不在record_damage()中调用(P6-N47) - P-S4-06:StatusManager 无 swap-and-pop 跳过 bug(逆序遍历中已处理的 back() 元素不被重复处理)
技术参考
combat_mechanics_depth.md §3.0~3.2.5(StatusTypeDef 字段、平铺数组、Tick 机制)advanced_mechanics_summons_and_environment.md §3.2(StatusID Autoload 规范)consecutive_hits_stacking.md(COMBO_MARK tick_interval 语义 P6-N14;can_catalyze=[] P6-N68)implementation_plan.md §2.5.A(PlayerManager CHARGE_FIRED 边沿检测 P6-N69)implementation_plan.md §2.5.D(VFXManager _instantiate_vfx P6-N72)implementation_plan.md §2.5.E(DPS 环形缓冲区 P6-N38)
出口检查
✅ 已通过(2026-06):DoT / VFX / DPS / 蓄力边沿验收完成。下一迭代:S5。
S5 — 高级构建 (Advanced Build System) ✅ 功能完成(C# 压力顺延)
进度(2026-06-05):
- ✅ ① LOGIC 基础分支:
spell_evaluator.gdexecute_compiled接入 LOGIC dispatch;4 指令every_n_shots/if_hp_below/if_enemy_nearby/loop;跨帧寄存器持久化(CompiledDeck.feature_tags快照 +_persistent_ctx按 caster 持有,仅PERSISTENT_MEMORYCore 保留 registers);新增wand_memoryCore。P-S5-03 实测通过。- ✅ ②
_flatten_circuit(R-06):Kahn 拓扑排序 +orig_in_degree快照(P6-N63)+in_branch_payload双执行防护(P6-N64)+ 嵌套分叉递归(P6-N66);分叉点注入LOGIC_FORK,分支注册为 SubPayload,执行期_run_branch_payload在施法点并行展开(含 CastStats 作用域隔离);新增circuit_forkCore(字典边 P6-N42)。P-S5-02 / P-S5-07 实测通过。- ✅ ③ ZoneManager + MinionManager:
zone_manager.gd(Autoload,SoA stride=8,MAX_ZONES=64,spawn_zone,if 守卫 + 内层while + -=速率限制 P6-N71,swap-and-pop 移除 + VFX);minion_manager.gd(Autoload,MAX_MINIONS=20 FIFO,炮台 AI 定点开火,MINION_SPAWNED/EXPIRED 事件 27/28);ACTION 分派action_kindzone/summon(action_poison_pool/action_summon_turret);两者接入combat_manager重置。P-S5-04 / P-S5-05 实测通过。P-S5-ZM-01(C# 压力)按 R-08 顺延。- ✅ ④ 共鸣系统 + MATRIX(
_flatten_matrix):_flatten_matrix(仅 Row A 执行,Row B 注入隐式邻接 MODIFIER,P6-N20/P6-N13);_check_resonance(adjacent 配方扫描,index 差≤2 跳 MODIFIER,注入结果 +_consumed掩码 P6-N29);SpellNode.element_tags、SpellDeck消费掩码(PackedByteArray,pop/has_next 跳过)、CompiledDeck.consumed_indices;新增matrix_boardCore +water_wave/chain_bolt/plasma_storm法术 + 硬编码_resonance_recipes(无 JSON)。P-S5-01 / P-S5-06 实测通过。- ✅ ⑤ 精英寻路(ADR-A4):
enemy_manager.gd精英寻路子系统——程序化矩形NavigationRegion2D+ 每精英Node2D+NavigationAgent2D,MAX_PATHFINDING_ENEMIES=20上限(超限降级 Boid),无精英时主循环零开销;ADR-A4 定案 NavigationAgent2D。P-S5-AI-01 实测 ≈0.0496ms/帧(<0.5ms)通过。S5 功能验收全部通过(P-S5-01~07 + P-S5-AI-01)。唯 P-S5-ZM-01(C#
ZoneManagerCs压力)按 R-08 顺延到 C# 优化轮。 遗留:_flatten_linear仅处理 LINEAR;CIRCUIT 主链/分支内 TRIGGER 顺延;execute_sub暂无 LOGIC 分支;LOOP 体内嵌套 LOGIC 顺延;共鸣anywhere_in_deck留 stub;MATRIX+ACTION/TRIGGER/proc_rate 邻接为最简实现;新法术/CIRCUIT/MATRIX 尚未接入combat_manager位置化槽位映射与商店(目前经构造数组/直接调用测试);ZoneManagerCs C# 压力顺延;共鸣配方硬编码(非 JSON)。
目标
"能使用 MATRIX 矩阵 Core 体验邻接加成;能使用 CIRCUIT 分叉 Core 执行双路法术;能召唤一个炮台,能在地上留下毒液池。"
范围
| 要实现的系统 | 最小实现内容 | 暂不实现 |
|---|---|---|
_flatten_matrix |
Row A 执行序列 + Row B 邻接加成注入(P6-N20);ImplicitModifierNode |
共鸣系统(见下) |
_flatten_circuit |
Kahn 拓扑排序、orig_in_degree 快照(P6-N63)、in_branch_payload 双执行防护(P6-N64)、嵌套分叉递归(P6-N66) |
超 3 层嵌套压力测试 |
| LOGIC 法术(基础) | LOGIC_IF_HP_BELOW、LOGIC_EVERY_N_SHOTS(读写 SpellContext.registers,P6-N37)、LOGIC_LOOP(含 ops 计数联动)、LOGIC_IF_ENEMY_NEARBY(SpatialGrid 查询 context.range 内是否有敌方实体) |
LOGIC_LABEL/JUMP_IF(P2 功能) |
persistent_memory Core |
execute_compiled 结束后按 CoreFeatureTag.PERSISTENT_MEMORY 决定是否 fill(0.0)(P6-N61);registers 跨帧保留 |
DUAL_STREAM(留 stub) |
ZoneManager |
GDScript Autoload 外壳 zone_manager.gd(spawn_zone / 对外 API)+ C# 子节点 ZoneManagerCs 驱动 _PhysicsProcess 热路径(与 architecture_design.md §6.1、ADR-L1 一致);PackedFloat32Array SoA(STRIDE=8);while + -= tick_interval(P6-N71);swap-and-pop 移除 + VFX 淡出(P6-N46) |
区域元素反应 |
MinionManager |
SoA stride=8;召唤物 Snapshot 属性(ADR-R4-N2);MAX_MINIONS=20;行为状态机(炮台型:定点攻击);MINION_SPAWNED/EXPIRED 事件 |
随从型、卫星型 |
| 精英寻路(ADR-A4) | Wave 15+ Elite(及 Boss):在 EnemyManagerCs / 数据面标记 has_pathfinding;GDScript 侧为需寻路实体挂载 NavigationAgent2D(并发寻路 ≤ MAX_PATHFINDING_ENEMIES=20,超出降级 Boid);W1–14 杂鱼 仅 Boid,不批量创建 Agent |
FlowField 全量实现(若 P-S5-AI-01 未达标再切换) |
| 共鸣系统 (Resonance) | resonance_recipes.json 加载;adjacent 匹配;_consumed 掩码实现(P6-N29);_check_resonance 在 compile_wand 阶段调用 |
anywhere_in_deck O(N²) 路径(留 stub) |
CIRCUIT Core(circuit_fork) |
7 槽史诗 Core;edges 顶层字段(P6-N42);UI 显示有向图结构 |
可视化连线编辑器 |
| Tier 3 法术 | nuke、homing、chain_bolt、heavy_cost |
召唤类法术(action_summon_turret 依赖 MinionManager) |
action_summon_turret |
调用 MinionManager.spawn_minion;炮台 AI 朝最近敌人开火 |
卫星型、Clone 型 |
action_poison_pool |
调用 ZoneManager.spawn_zone(radius=200, status=POISON, duration=5s, tick_interval=1.0) |
反应性区域场 |
关键文件
scripts/autoloads/
zone_manager.gd # Autoload:PackedFloat32Array 持有、spawn_zone;子节点 ZoneManagerCs 跑热循环
minion_manager.gd
core_feature_tag.gd # Autoload CoreFeatureTag 常量
csharp/systems/
ZoneManagerCs.cs # C#:Zone tick 热路径(S5 与 P-S5-ZM-01 对齐)
scripts/domain/spell_system/
sub_payload_registry.gd # 已有;确认 LOGIC_FORK 执行路径正确
resources/
cores/
matrix_board.tres # MATRIX_2X4 8槽 稀有
circuit_fork.tres # CIRCUIT 7槽 史诗(含 edges)
wand_memory.tres # LINEAR 6槽 + persistent_memory 史诗
spells/
logic_if_hp_below.tres
logic_every_n_shots.tres
logic_loop.tres
logic_if_enemy_nearby.tres
action_nuke.tres
action_chain_bolt.tres
modifier_homing.tres
modifier_heavy_cost.tres
action_summon_turret.tres
action_poison_pool.tres
resonance_recipes.json # plasma_storm 等配方
验收标准
- P-S5-01:MATRIX Core 中
[spark_bolt A] / [damage_plus B]垂直对齐 → 编译后 spark_bolt 受 damage_plus 邻接加成;_flatten_matrix不追加 Row B 节点为独立执行(P6-N20)— 实测通过 2026-06-05:matrix_board编译为[implicit_adjacency, action_spark_bolt](Row B 不独立);spark 伤害 (3+10)=13、单弹;无 Row B 对照组伤害=3 - P-S5-02:CIRCUIT
circuit_forkCore 执行双路[spark_bolt (branch A)] / [fire_bolt (branch B)]→ 一次施法产生两种弹 — 实测通过 2026-06-05:circuit_fork(slot0 分叉,edges 0→1/0→2)单次施法产生 spark+fire 共 2 弹;主链扁平化为 1 个 LOGIC_FORK + 2 个分支 SubPayload - P-S5-03:
LOGIC_EVERY_N_SHOTS配置 N=3:第 1/2 次施法跳过后续法术,第 3 次触发;寄存器值在wand_memoryCore 下跨帧保留(P6-N37)— 实测通过 2026-06-05:wand_memory4 次施法弹数[0,0,1,0];wand_basic(无 PERSISTENT_MEMORY)对照组[0,0,0,0]证明持久化由 Feature Tag 门控。if_hp_below/if_enemy_nearby/loop一并实测通过 - P-S5-04:
action_poison_pool落地后 ZoneManager 每秒对范围内敌人施加 POISON;tick_accum 余量不丢失(P6-N71)— 实测通过 2026-06-05:区域 1.0s tick 对范围内实体施加 POISON(false→true);速率限制(0.5s 不触发、累计 1.0s 触发);2.5s 大帧后 tick_accum 余量精确 =0.5;duration 耗尽 swap-and-pop 移除 - P-S5-05:召唤炮台后 UI 召唤物计数 +1;炮台消亡后计数 -1(MINION_SPAWNED/EXPIRED 事件)— 实测通过 2026-06-05:
action_summon_turret经法术 VM 召唤 → 计数 0→1 且 MINION_SPAWNED.current_count=1;炮台对范围内最近敌人开火(无敌则不开火);recall → 1→0 且 MINION_EXPIRED.current_count=0;MAX_MINIONS=20 FIFO 上限(召 25 留 20) - P-S5-06:
[water_wave] + [chain_bolt]相邻放置 → 编译时触发 "plasma_storm" 共鸣配方,实际发射等离子风暴法术 — 实测通过 2026-06-05:编译注入action_plasma_storm于 index 0、consumed_indices=[1,2](_consumed 掩码 P6-N29);运行时仅发等离子风暴弹(伤害 18),水/雷输入被消费不发射;中间夹 ACTION 的对照组不触发共鸣(consumed=[]) - P-S5-07:
_flatten_circuit在 3 层嵌套分叉 Core 下无双重执行(in_branch_payload防护正常)— 实测通过 2026-06-05:3 层嵌套分叉(0→1/2、2→3/4、4→5/6)4 个叶节点各执行恰好 1 次(4 弹),重复施法稳定;另测汇聚菱形(0→1/2、1/2→3)汇聚节点经主链恰好 1 次(3 弹),验证orig_in_degree快照 P6-N63 + 嵌套递归 P6-N66 - P-S5-ZM-01(压力验收):
ZoneManagerCs(C# 子节点) 驱动 Zone tick:同屏 64 个激活区域 × 1000 敌人,_PhysicsProcess帧时间 < 1ms(Profiler 实测);若超过,启用 LOD 策略(屏外 Zone tick 频率降至 6fps)。GDScriptzone_manager.gd不得在每 Zone 每帧上承载 O(敌人×区) 的内层循环(与 §6.1 分工一致)。 — ⏸ 顺延:功能路径(GDScriptzone_manager.gd,含 if 守卫 + 内层 while 速率限制)已实现并通过 P-S5-04;C#ZoneManagerCs压力路径与 S0 起的 C# 热路径迁移一并按 R-08 策略顺延至专门优化轮(与 BulletManagerCs 等同状态) - P-S5-AI-01(ADR-A4):同屏 20 个带完整寻路的 Elite,
NavigationAgent2D路径跟随引起的EnemyManager/Navigation相关帧时间增量 < 0.5ms;超标则记录 Profiler 并切换 FlowField 方案,回写architecture_design.mdADR-A4 结论段。 — 实测通过 2026-06-05:20 精英_update_pathfinding_movement≈0.0496ms/帧(10× 余量),定案 NavigationAgent2D(不切 FlowField);ADR-A4 结论段已回写。并发上限 MAX_PATHFINDING_ENEMIES=20 验证(25 精英→20 寻路+5 降级 Boid);reset 释放全部 Agent 节点;无精英时主循环零开销
技术参考
architecture_design.mdADR-A4(精英寻路、MAX_PATHFINDING_ENEMIES、P-S5-AI-01)architecture_design.md §3.4.A(_flatten_matrix P6-N20、_flatten_circuit P6-N57/63/64/66)architecture_design.md §3.4.C(共鸣系统、_consumed 掩码 P6-N29)architecture_design.md §8 ADR-R5-N1(ZoneManager 权威实现 P6-N71)advanced_mechanics_summons_and_environment.md §3.1/3.3(MinionManager 方案 B)core_wand_design.md §2~3(MATRIX 邻接表 P6-N13、CIRCUIT edges P6-N42、Feature Tags P6-N61)
出口检查
能否用 CIRCUIT Core 构建双路元素炮台构建并打通 5 波?所有高级 Core 无双执行 / 空指针崩溃?P-S5-ZM-01 与 P-S5-AI-01 压力测试通过(或 ADR-A4 已更新为 FlowField 且复测通过)?"是"方可进入 S6。
状态(2026-06-05):✅ 功能验收 P-S5-01~07 + P-S5-AI-01(≈0.0496ms,ADR-A4 定案 NavigationAgent2D)全部实测通过,高级 Core 无双执行/崩溃。⏸ P-S5-ZM-01(C#
ZoneManagerCs压力)按 R-08 与 BulletManagerCs 等 C# 热路径一并顺延至专门优化轮——功能 GDScript 路径已通过 P-S5-04。 集成(2026-06-05 完成):S5 系统已接入实际游戏流程并实测可玩——
- 商店池 =
SpellRegistry.get_all_ids(),已含全部 S5 法术(LOGIC / summon / zone / 共鸣输入 water_wave·chain_bolt)。combat_manager新增 Core 切换(商店内「法杖」按钮循环wand_basic→fast→memory→matrix_board→circuit_fork),每 Core 载入演示默认 Deck;install_spell对 MATRIX/CIRCUIT 改为位置化填空槽,_rebuild_wand保留""空槽为 null。HUD/商店显示当前 Core 名。- 实测:5 Core 循环切换均正确编译(记忆法杖 every_3 跨帧
[0,0,1,0]、矩阵板邻接、分叉回路双弹);共鸣经商店装配触发;summon/zone 作为首动作或经double_cast正常产出。- 背包(2026-06-05 完成):点选式插槽编辑器(商店「🎒 背包」按钮),拓扑感知插槽网格 + 备牌区,支持插槽交换 / 移入移出备牌 / 即时重编译(详见 S3 出口检查段)。玩家现可在任意 Core 上自由排布法术(含 MATRIX 行 A/B、CIRCUIT 槽位、共鸣相邻)。
- 法杖存档(2026-06-05 完成):Core/deck/bench 已纳入
ProfileManagerA/B 双槽(schema_version升至 2)。CombatManager经ProfileManager.set_wand_provider(self)注册,提供get_wand_save_data/apply_wand_save_data(字段core_id/deck/bench);_collect_run_data写入wand,apply_run恢复。实测:自定义矩阵 Deck 存档→擦除→读档完整恢复;v1 旧档(无 wand)经_migrate_run升 v2 不崩溃保留默认;备牌持久化;A/B 交替写入保持。- 余项(归 S6):真·拖拽手感、CIRCUIT 有向边可视化编辑器、Steam Cloud 批量写(ST-50)。
S6 — 润色与发布 (Polish & Launch)
目标
"60fps 帧率稳定(含 Profiler 数据)、20 波内容完整、手感丝滑、Endless 排行榜可用。"
范围(优先级排序)
P0 必须完成
- 完整游戏外壳(启动→主菜单→游戏→暂停→结算→返回) — 2026-06-05:商业级完整流程闭环。新增
SceneManager(Autoload,黑幕淡入淡出转场 +start_modenew/continue 路由)、scenes/ui/splash.tscn(启动画面:工作室名+游戏标题+版权,1.8s 自动/任意键跳过)、scenes/ui/main_menu.tscn(主菜单:新游戏/继续[无存档时禁用]/排行榜/设置/退出 + 语言切换 + 版本号);main_scene改为splash.tscn;combat_s2.gd接入 SceneManager 启动模式(新游戏=start_game/继续=resume_game)、新增暂停菜单(ESC,process_mode=ALWAYS暂停时仍响应,继续/设置/返回主菜单/退出)、结算屏加「返回主菜单」按钮。全流程实测通过:splash→menu 自动跳转、新游戏/继续读档(恢复 wave=7/gold=250)进战斗、ESC 暂停冻结世界、死亡结算→返回主菜单(死亡删档使继续按钮禁用)。修复 2 bug:转场 Tween 竞争(fade_in_first kill 复位回调致 is_transitioning 卡死)、暂停 process_mode 致 ESC/按钮失效。待补:主菜单/splash 美术背景、FTUE 首启难度引导 - 程序化美术占位(视觉升级) — *2026-06-05:从纯色几何方块升级为完整 bullet-heaven 视觉。新增
shaders/bullet_glow.gdshader(子弹加法混合发光圆:软核+外发光晕)、shaders/enemy_circle.gdshader(敌人体积感实体球:软边+左上高光+暗边描线)、shaders/arena_grid.gdshader(竞技场暗紫网格地面:细/粗双层网格+径向暗角)应用于两个 MMI + 背景 Polygon2D(z=-100);玩家标记升级为发光菱形(青光晕环+橙菱形+白核);vfx_manager.gd_instantiate_vfx改为GPUParticles2D(程序化爆裂粒子:球形发射+颜色渐变淡出+按特效 amount/speed,one_shotP6-N72,play()触发restart);assets/ui/ui_theme.tres全局 UI Theme(按钮圆角6px+蓝紫边框+悬停高亮+禁用态,经gui/theme/custom全局应用所有 Control)。实测:150 弹同心圆弹幕发光叠加、Boss 体积球、网格地面、圆角按钮均渲染正确,0 报错。全部为程序化生成,放真实素材(res://audio/sfx/、精灵 Atlas、VFX 场景)即整体替换,见 handbook/03 - 性能验收:确认所有 C# 热路径模块(
BulletManagerCs/EnemyManagerCs/SpatialGridCs/SpellEvaluatorCs/StatusManagerCs/ZoneManagerCs)均满足各自帧预算(Profiler 实测);若发现遗漏的 GDScript 瓶颈,按 ADR-L1 规则迁入 C#(architecture_design.md §6.1) - [~] 三档渲染切换逻辑:实现 Area2D → Dirty Sync → MultiMeshInstance2D 渐进切换(
BATCH_DISABLE_PER_FRAME=100,回滞阈值<1500,architecture_design.md §4.4)— 2026-06-05:直接实现 MultiMeshInstance2D 渲染(§4.4 最高档)——此前子弹/敌人完全无渲染(仅 HUD+VFX 色块),现BulletManager.sync_multimesh/EnemyManager.sync_multimesh(SoA 索引↔instance 索引共享,visible_instance_count控制数量)+ 单位 QuadMesh + 敌人 per-instance 颜色/尺寸(Boss 大且醒目);combat_s2.gd建子弹/敌人两个 MMI + 玩家标记 + 跟随 Camera2D,_process每帧同步。实测渲染正确(红杂兵/品红 Boss/黄子弹)。Area2D↔MultiMesh 三档切换 N/A:碰撞自 S1 即为 SpatialGrid 唯一路径(从未建 Area2D),故 MultiMesh 单档覆盖全密度即可,无需 Area2D 档与回滞切换。待补(润色):Dirty Sync 视口剔除、子弹按 type 出图集、C# 化 transform 上传 - [
] Wave 120 完整内容(敌人配置表、Mini Boss Wave 8、Final Boss Wave 20)(game_design.md §3.3)— 核心已实现 2026-06-05:wave_manager.gd20 波配置表(W1-5 数值 / W6-9 护甲 / W11-19 弹幕量增 / W15+ 精英寻路);敌人原型EnemyManager.Type(BASIC/FAST/ARMORED/ELITE/MINIBOSS/BOSS)+ 按型速度/护甲;Mini Boss W8(450HP)、Final Boss W20(1800HP)+ 阶段系统(HP 66%/33% 进阶召援军)+BOSS_SPAWNED;Boss 死亡即通关本波。Endless 数值膨胀。实测全部通过。待补(润色):Boss 弹幕攻势(Phase2 走位)、元素抗性/反弹护盾怪、敌人 .tres 资源化、Boss JSON 驱动 - 20 张+ 法术卡(含所有 Tier 1/2/3)
- 至少 7 种 Core 可玩:
wand_basic、wand_fast、staff_long、matrix_board、circuit_fork、wand_memory、wand_eternal(名称与数值以resources/cores/为准;与「5 种」旧表述对齐为 7 种变体) - 死亡流程:结算屏、Run 截图(
get_viewport().get_texture().get_image(),死亡动画第 1 帧后执行)— 2026-06-05:combat_s2.gd结算屏(到达波次/击杀/用时/评分 + 排行榜 + 「再来一局」);_save_run_screenshot()存user://runs/run_*.png(结算屏非黑屏,P-S6-04 通过)。注:战场在 GAME_OVER 前已被_on_player_died重置,故截图为结算摘要屏而非死亡瞬间战场 - Endless 模式 + 本地排行榜(波次主排名、同波次按时间升序;评分公式:
score = wave × 100000 + (86400 - elapsed_sec),P6-N19;本地存储于user://endless_records.json)— 2026-06-05:endless_records.gd(AutoloadEndlessRecords)HMAC-SHA256 签名(ST-40)+ 读取验签拒绝篡改(ST-41);排序波次降序/同波次用时升序;compute_score实测 786386 正确。修正一处真实 bug:原按对象签名会因 JSON int↔float 往返破坏验签,改为对精确 JSON 字符串签名
P1 完成(时间允许)
DUAL_STREAMCore feature 实现(奇数槽中间槽前流专属,P6-N12)ALWAYS_CAST_LAST:尾槽为 MODIFIER/TRIGGER/LOGIC 时静默跳过(P6-N7)SHUFFLE_DECK:ACTION 原子单元整体乱序(P5-N2)INFINITE_SPELLS:配合heavy_cost的卖血流(P6-N18)- 敌人元素弱点 + 抗性 + 催化反应(StatusManager 交互矩阵)
- 音频系统(32 AudioStreamPlayer2D 池、0.1s 节流)— 2026-06-05:
audio_manager.gd(AutoloadAudioManager)32 池 round-robin + 每 sound_id 0.1s 节流 + 运行时建 SFX 总线 send→Master(受 SettingsManager 音量);EventBus 挂钩(ENEMY_KILLED/BULLET_HIT/PLAYER_DAMAGED/WAVE_COMPLETE/BOSS_SPAWNED/SHOP_OPENED/LEVEL_UP);无素材时程序化蜂鸣占位(放res://audio/sfx/<id>.ogg即替换)。实测池/节流/独立 id/未知 id 优雅 no-op/事件触发通过。待补:真实音效素材、BGM 分层 - 移动端适配(虚拟摇杆映射到 InputMap Action)
- 完整图鉴系统(法术、Core、共鸣配方发现机制 P5-C2)
- 难度系统:提供初学者 / 标准 / 挑战三档(初学者:敌人 HP ×0.7、伤害 ×0.7;挑战:W20 Boss HP ×1.3、Wave 怪物数 ×1.2);难度选项在首次启动时通过 FTUE 引导选择,后续可在主菜单修改;难度配置持久化到
user://save_data.json(difficulty: int,0=初学者、1=标准、2=挑战)— 2026-06-05:settings_manager.gd(AutoloadSettingsManager)三档难度 + 乘子(enemy_hp_mult/player_dmg_taken_mult/wave_count_mult/boss_hp_mult),接入PlayerStats.take_damage(初学者 20→14 实测)、WaveManager怪物数/血量/Boss 血(挑战 W1 20→24 实测);持久化save_data.json(difficulty/locale/master_volume/ftue_done)。设置面板(难度/语言/音量滑条/清除数据)实测渲染。FTUE 首启难度引导已完成 2026-06-05:首次启动(save_data.json无ftue_done)点「新游戏」弹出三档难度选择界面(初学者/标准/挑战,颜色编码绿/蓝/红 + 各档说明文字 + 「设置中可改」提示);选定后SettingsManager.complete_ftue()设难度+标记+存档→进游戏;二次启动不再引导;ST-03 清除数据后重新触发。实测全通 - 设置菜单 + 音量(Master 总线)+ 「清除所有本地数据」入口(ST-03)— 2026-06-05:
combat_s2.gd设置面板(商店「⚙ 设置」按钮打开);音量 HSlider→AudioServerMaster 总线;SettingsManager.clear_all_local_data()实测删除 save_data/endless_records/run_a·b/runs 目录并重置
P2 可选高级内容
LOGIC_LABEL / LOGIC_JUMP_IF(寄存器条件跳转,仅限 DEBUG Core 解锁)LogicHandler接口(LOGIC 指令集 > 10 条时替换 match 硬编码,P6-N4 规范)anywhere_in_deckO(N²) 共鸣扫描(仅传说配方)- SpellEvaluator 调试图(EditorPlugin 单步执行,Q2 预案)
ResourceLoader.load_threaded_request异步预加载(SpellRegistry P1 路线图)
验收标准 (P0)
- [
] P-S6-01:Wave 120 无崩溃完整通关;Mini Boss Wave 8 及 Final Boss Wave 20 AI 正常(game_design.md §3.3)— 系统已实现并实测:20 波配置全部正确生成(杂兵/护甲/精英寻路计数核对);W8/W20 Boss 入场(450/1800 HP)+ 三阶段进阶(60%→P2、30%→P3 各召援军)+BOSS_SPAWNED;Boss 死亡通关、普通波清空通关;护甲减伤(ARMORED 20→15)。完整 20 波连续通关压力测试待 S6 后续连跑 - P-S6-02:同屏 500 敌人 + 1500 子弹稳定 60fps(Profiler 实测)— 实测通过 2026-06-05(RTX 2060):实时 FPS=180,
_physics_process实测帧时间 0.91ms;手工逐系统计时(60 reps):Bullet 22.95μs + Enemy 396.97μs + SpatialGrid 0.15μs + Status 0.23μs + Zone 0.18μs + MultiMesh sync 180.10μs = 总计 0.601ms = 16.67ms 预算的 3.6%。全部 GDScript 回退路径(无 C# 热化),仍有极大裕量 - P-S6-03:Endless 模式可进入;本地排行榜以波次为主排名、同波次按时间升序排(P6-N19 权威公式:在线分 =
wave × 100000 + (86400 - elapsed_sec);本地存储于user://endless_records.json)— 实测 2026-06-05:排序 W12/250→W12/300→W8/90→W5/120;评分 786386 正确;HMAC 签名/验签/篡改拒绝通过。注:Endless「继续」入口(W20 后)属玩法接线,待补 - P-S6-04:死亡时截图正确保存到
user://runs/(不截到黑屏)— 实测 2026-06-05:user://runs/run_*.png已生成(结算屏内容,非黑屏) - P-S6-05:所有 P6-N 规范条目已在实际代码中验证(无违反)— 2026-06-05 审计通过(12 条约束)。发现并修复 1 件真实违反:
spell_evaluator.gd:504_acquire_ctx()在热路径execute_compiled→_physics_process中懒分配SpellContext.new()(违反 P6-N3)。修复:在非热路径的compile_wand()阶段检测PERSISTENT_MEMORYfeature tag 时预热分配 SpellContext(_persistent_ctx[0]),确保后续施法_acquire_ctx()走 dict.get() 取已有实例,不再触发new()。P-S5-03 回归测试通过[0,0,1,0]。其余 11 条全 PASS(ENEMY_STRIDE、BULLET_STRIDE、StatusID、compile_wand 参数序、zone_manager while+−=、vfx one_shot、PackedByteArray int()、CoreFeatureTag、registers 初始化、EventID 序列、ADR-A2 存档) - [~] P-S6-06:本地化与 ADR-A1 对齐:
translations/下至少存在zh_CN.po、zh_TW.po(繁体)、en.po、ja.po四个文件(键集一致;zh_TW/ja可先用占位译文,但商店页承诺的语言须在发售前填满);所有 S1–S6 玩家可见字符串均为tr("KEY")(调试push_warning/push_error除外);业务脚本无裸中文字符串(以项目约定之静态检查或人工审计为准)— 2026-06-05 骨架完成:translations/四个.po全部就位且键集一致(实测 4 语言 0 缺键),全部填了真实译文(非占位);LocaleAutoload 经TranslationServer.add_translation加载 + 切换;combat_s2.gdHUD/商店/结算/背包主 UI 字符串已全部tr("KEY")化(实测 en/ja/zh_TW 切换正确);商店内加语言切换按钮。待补:Core/法术/状态/Boss 的display_name/description(现为WandPreset/各 Def 内硬编码,需键化为SPELL_*/CORE_*/STATUS_*并补四语);裸中文静态检查脚本 - P-S6-07:内存峰值实测(Wave 20 Boss 战场景):PC 端 RSS < 512MB,对照
architecture_design.md §4.6内存预算表各类别实测均不超标 — 实测通过 2026-06-05(W20 满载:500敌+2000弹+500状态+32 Zone):MEMORY_STATIC=105.9MB + 引擎基础≈70MB → 估算 RSS≈175.9MB(34%,目标<512MB ✓);热数据(全 SoA+MultiMesh)仅 0.3MB;VRAM=92.6MB 为 GPU 显存不计入 PC RSS。Switch 共享内存(VRAM+RAM≈268MB)低于 SW-11 的 2.5GB 限额,实机测量待 Switch 移植时确认
三、时间线估算 (Timeline Estimate)
以"单人 + 2D 美术外包"为基准估算,仅供参考,实际根据团队调整。
| 切片 | 状态 | 估算工时 | 主要阻塞风险 |
|---|---|---|---|
| S0 技术骨架 | ✅ | 1~2 周 | R-01/R-03 已缓解(GDScript 回退路径) |
| S1 最小战斗 | ✅ | 1 周 | — |
| S2 核心循环 | ✅ | 1~2 周 | — |
| S3 法术管道 | ✅ | 2~3 周 | — |
| S4 战斗深度 | ✅ | 1~2 周 | — |
| S5 高级构建 | ✅* | 3~4 周 | 功能完成(*C# 压力 P-S5-ZM-01 顺延);_flatten_circuit 嵌套分叉已验、MinionManager AI 已验 |
| S6 润色发布 | ⬜ | 4~6 周 | 内容量(法术/敌人设计);平衡调整 |
| 合计 | — | R-08 C# 热路径迁移若触发则追加 1~2 周 |
四、跨切片技术约束速查 (Cross-Slice Constraints)
以下约束贯穿所有切片,违反时导致回归风险,开发时随时核查:
| 约束 | 来源规范 | 高风险操作 |
|---|---|---|
禁止 DamageContext / SpellContext 在战斗循环中 new() |
P6-N49、P6-N3 | 任何 apply_damage 调用路径 |
DamageContextPool.acquire() 必须先 ctx.reset() |
P6-N67 | 任何新增 acquire 调用 |
StatusType.XXX 枚举不存在,必须用 StatusID.XXX |
P6-N30 | 新增状态效果代码 |
CoreFeatureTag.XXX 常量,禁止裸字符串 |
ADR-R5-N2 | Feature Tag 判断逻辑 |
ENEMY_STRIDE=8 命名常量(禁止裸整数 8) |
P6-N54 | EnemyManager 所有 SoA 访问 |
BULLET_STRIDE=12 命名常量(禁止裸整数 12) |
architecture_design.md §4.2 |
BulletManager 所有 SoA 访问 |
SpellContext.registers 必须初始化长度 4 |
P6-N48 | SpellContext 对象池 reset() |
compile_wand(core, raw_deck) 参数顺序 core 在前 |
P6-N70 | SpellEvaluator 入口调用 |
ZoneManager tick 用 while + -=,不用 if + =0 |
P6-N71 | ZoneManager 任何 tick 逻辑 |
VFXManager._instantiate_vfx 必须 one_shot=true |
P6-N72 | VFX 节点创建路径 |
CHARGE_FIRED(ID=14) 一次性;SpellEvaluator 订阅此而非 CHARGE_STATE_CHANGED |
P6-N69 | 蓄力法术实现 |
SubPayloadRegistry 仅在关卡进行中只读,背包关闭后重编译 |
P6-N10 | 任何支持"战斗中改装备"的功能 |
Run 存档 A/B 双槽、schema_version、_migrate_run 链 |
ADR-A2 | 单槽覆写、跳过迁移、损坏全丢 |
EventID 1–21 与 implementation_plan.md §2.1 表一致(含 ACHIEVEMENT_UNLOCKED=18、GAME_STATE_CHANGED=20、SPELL_DROP_PICKUP=21);新增事件从 22 起递增并向 §2.1 表登记 |
E-N1、certification ST-10、boss_design §6 | 在业务代码中硬编码与表冲突的事件整数 |
C# 内层循环(for / while 体内)禁止 GodotObject.Call() / .Set() |
ADR-L1 规则 1 | BulletManagerCs / EnemyManagerCs 任何新增热路径代码 |
C# 访问 SoA 热数组必须通过 PackedFloat32Array.AsSpan() 获取 Span<float> 再做索引 |
ADR-L1 规则 2 | 任何新增 C# SoA 读写代码 |
PackedByteArray[i] 赋值必须显式 int() 转换 |
P6-N27 | _visible_flags 相关代码 |
ProjectileDef.reset() 必须重置 spawn_position |
P6-N41 | BulletManager spawn 路径 |
五、参考文档导航 (Reference Map)
| 需要了解 | 首要参考文档 | 章节 |
|---|---|---|
| 局内 Run 存档 A/B 与迁移 | technical/architecture_design.md |
ADR-A2 |
| Elite / Boss 寻路 | technical/architecture_design.md |
ADR-A4 |
| 全局架构与层级关系 | technical/architecture_design.md |
§2~4 |
| SpellEvaluator 执行流 + 预编译 | technical/architecture_design.md |
§3.4 |
| BulletManager SoA 字段定义 | technical/architecture_design.md |
§4.2 |
| ZoneManager 完整实现 | technical/architecture_design.md |
§8 ADR-R5-N1 |
| EventBus 事件目录 | technical/implementation_plan.md |
§2.1 |
| DamageContext 字段权威 | technical/implementation_plan.md |
§2.1 (P6-N49) |
| EnemyManager SoA + LOD | technical/implementation_plan.md |
§2.3.C |
| PlayerManager 移动状态追踪 | technical/implementation_plan.md |
§2.5.A |
| VFXManager 完整实现 | technical/implementation_plan.md |
§2.5.D |
| DPS 环形缓冲区 | technical/implementation_plan.md |
§2.5.E |
| Core 插槽拓扑、Feature Tags | design/core_wand_design.md |
§1~3 |
| SpellEvaluator 两阶段接口 | design/core_wand_design.md |
§6 |
| 数值公式(XP/伤害/商店) | design/numerical_design.md |
§1~3 |
| 法术参数参考表 | design/numerical_design.md |
§2.2 |
| 状态效果与 DoT | mechanics/combat_mechanics_depth.md |
§3~4 |
| BulletContext 连锁弹射 | mechanics/combat_mechanics_depth.md |
§2/5 |
| StatusManager Tick 机制 | mechanics/combat_mechanics_depth.md |
§3.2.5 |
| COMBO_MARK / VULNERABILITY | mechanics/consecutive_hits_stacking.md |
全文 |
| MinionManager / ZoneManager | mechanics/advanced_mechanics_summons_and_environment.md |
§1/3 |
| StatusID Autoload 规范 | mechanics/advanced_mechanics_summons_and_environment.md |
§3.2 |
| 运动学 / 物理扩展(P2/P3) | mechanics/combat_mechanics_extensions_v2.md |
§1~5 |
| 弹道修正器(正弦/环绕) | mechanics/weapon_system_expansion.md |
§1 |
| 全规范速查表 (P6-N*) | README.md |
§关键规范速查 |