PROJECT-v M3:完整战斗闭环实现拆解
M3 的目标不是把战斗“做得热闹”,而是把 M2 的基础攻击、受伤、死亡链路扩展成可继续承载动作游戏手感的完整战斗骨架:玩家能闪避,攻击能按命中结果取消,命中反馈能从战斗事实触发,技能和武器能通过配置资产接入,测试场景和 runtime probe 能证明这条链路在 Unity 工程里真实可运行。
除注明 docs 仓库的文档外,本文所有工程路径均以 PROJECT-V 项目根目录为基准;docs 路径均以 PROJECT-V-game-docs 根目录为基准。
关联提交和依据
M3 功能主体提交:
| commit | 用途 |
|---|---|
e5317f8 |
feat(combat): complete M3 full combat milestone,完整战斗里程碑实现 |
分支状态:
| 分支 | 说明 |
|---|---|
develop |
M3 实施分支 |
origin/develop |
已推送,范围 5d0e869..e5317f8 |
主要参考产物:
| 类型 | 路径 |
|---|---|
| 交付报告 | reports/M3_delivery_report.html |
| QA 报告 | qa/reports/M3_test_report.md |
| EditMode 结果 | qa/results/M3_EditMode_TestResults.xml |
| PlayMode 结果 | qa/results/M3_PlayMode_TestResults.xml |
| 30s Runtime Probe | qa/results/M3_RuntimeProbe.json |
| 60s Runtime Probe | qa/results/M3_RuntimeProbe_60s.json |
| M3 Feature Spec,docs 仓库 | design/feature_specs/M3_combat_full.md |
| 武器修饰规则,docs 仓库 | design/weapon_worldview_modifiers.md |
| Sprint 状态,docs 仓库 | project/sprints/SPRINT_03.md |
| Project State,docs 仓库 | project/PROJECT_STATE.md |
M3 的目标和边界
M3 承接 M2 的最小战斗闭环,但不重写 M2 的攻击、生命、敌人和事件基础。它的范围是把“能打、能受伤、能死”扩展成“能通过战斗状态做动作决策”。
M3 纳入实现的内容:
-
闪避系统:无敌帧、位移、冷却、SFX 和事件。
-
攻击取消:轻攻击命中后可转重攻击或闪避,重攻击后段可转闪避。
-
连招倍率:轻攻击链、重击终结倍率和输入缓冲。
-
命中反馈:hitstop、相机震动、闪白、粒子、伤害数字和 SFX hook。
-
技能系统:U/I 两个激活槽、O 预留槽、8 个内功/轻功/暗器技能。
-
武器修饰:8 种冷兵器 × 3 个异世界形态,共 24 个 form。
-
15 个 placeholder WAV,用于把音频调用链先接通。
-
M3_FullCombat.unity白盒战斗测试场景。 -
M3 setup、EditMode、PlayMode、Windows Development Build 和 runtime probe 验证。
M3 明确不把下面内容当成完成标准:
-
正式技能 HUD 和完整 UI 表现。
-
最终混音、正式技能特效和正式粒子资源。
-
正式 projectile 物理表现;当前暗器类技能仍是白盒化命中实现。
-
Boss、镜像 Boss、手工关卡流程和 Roguelite Build。
-
30 分钟 1080p soak;M3 的 30s/60s probe 只是阶段工程验证,不等同于 release-grade QA gate。
这个边界是必要的。M3 如果同时做 Boss、正式 UI、正式音频和关卡,会把战斗状态链、表现层和内容设计混在一起,问题定位会失控。当前阶段真正需要固定的是战斗因果链。
工程生成入口
M3 的落地入口是 unity-project/Assets/_Project/Editor/M3ProjectBuilder.cs。
SetupM3() 的第一步是调用 M2 setup,确保 M3 是在 M2 战斗闭环上增量构建,而不是另起一套战斗框架。之后它负责生成或更新:
| 类别 | 关键路径 |
|---|---|
| 闪避配置 | unity-project/Assets/_Project/Player/SO/Dodge_M3.asset |
| 技能资产 | unity-project/Assets/_Project/Player/SO/Skills/ |
| 武器资产 | unity-project/Assets/_Project/Player/SO/Weapons/ |
| M3 音频占位 | unity-project/Assets/_Project/Audio/SFX/M3/ |
| M3 测试场景 | unity-project/Assets/Scenes/TestScenes/M3_FullCombat.unity |
| Runtime Probe | unity-project/Assets/_Project/Player/Scripts/M3RuntimeProbe.cs |
VerifyM3Setup() 则做硬性检查:
-
闪避配置必须是
0.3s无敌、3m位移、0.6s冷却。 -
技能资产必须正好 8 个。
-
武器配置必须正好 8 个。
-
武器世界观修饰必须正好 24 个。
-
M3 placeholder SFX 至少 15 个。
-
Player Prefab、输入绑定、M3 场景和 scene harness 必须存在。
这里的工程决策很清楚:M3 不依赖一次手工搭好的 Unity 场景。配置、Prefab 接线、输入、场景和验收入口都能被同一个 Editor tooling 重建和校验。
闪避系统
闪避由两个核心对象组成:
| 文件 | 职责 |
|---|---|
unity-project/Assets/_Project/Player/Scripts/DodgeConfigSO.cs |
保存闪避参数和音效数组 |
unity-project/Assets/_Project/Player/Scripts/PlayerDodge.cs |
消费输入、位移、无敌、冷却和事件发布 |
M3 默认参数:
| 参数 | 值 |
|---|---|
| 无敌时间 | 0.3s |
| 位移距离 | 3m |
| 位移时长 | 0.3s |
| 冷却 | 0.6s |
| SFX | dodge_whoosh_01/02/03 |
PlayerDodge.TryStartDodge() 的实现重点不是“播放一个动作”,而是同时处理战斗语义:
-
死亡或冷却中不能闪避。
-
输入方向为空时,使用角色朝向的反方向作为 fallback。
-
根据
distance / durationSeconds计算闪避速度。 -
调用
HealthComponent.GrantInvulnerability()写入无敌时间。 -
播放随机闪避 SFX。
-
发布
OnPlayerDodge事件。
这让闪避在 M3 里不是单纯位移,而是战斗状态的一部分:它能避伤害、能被测试探针观察,也能被后续表现层订阅。
攻击取消和连招
M3 没有引入新的攻击框架,而是在 M2 的 PlayerAttack 上扩展状态规则。这个选择是正确的:M2 已经把输入、AttackConfigSO、命中框、HealthComponent 和事件链跑通,M3 应该复用这条链,而不是为了“完整战斗”重写一套更复杂的动作系统。
关键代码位于:
-
unity-project/Assets/_Project/Player/Scripts/PlayerAttack.cs -
unity-project/Assets/_Project/Player/Scripts/PlayerCombo.cs
取消规则:
| 当前动作 | 条件 | 可取消到 |
|---|---|---|
| 轻攻击 | 已命中目标 | 重攻击 |
| 轻攻击 | 已命中目标 | 闪避 |
| 轻攻击 | whiff,未命中 | 不允许取消 |
| 重攻击 | 进入最后 30% recovery | 闪避 |
PlayerAttack 中的关键状态是:
1 | currentAttack |
轻转重的判断依赖 hasLandedHit,这能把“命中奖励”与“空挥惩罚”区分开。空挥时输入不会立刻取消当前动作,只会进入输入缓冲;当前攻击结束后再消费。
连招由 PlayerCombo 管:
| 参数 | 值 |
|---|---|
| 轻攻击最大段数 | 3 |
| 输入缓冲 | 0.2s |
| 连招重置窗口 | 0.8s |
| 每段轻攻击加成 | +10% |
| 三段后重击终结倍率 | 1.35x |
最终伤害倍率在 PlayerAttack.StartAttack() 中合成:
1 | comboMultiplier * nextAttackDamageMultiplier * WeaponDamageMultiplier |
这个设计把三类来源分开了:
-
连招倍率来自
PlayerCombo。 -
技能下一击倍率来自
PlayerSkillController写入的nextAttackDamageMultiplier。 -
武器倍率来自
PlayerWeaponController。
这比把所有倍率塞进攻击配置更干净,因为攻击本身只定义基础招式,运行时构成由连招、技能和武器共同决定。
命中反馈链
M3 的反馈链不是装饰层强行播放效果,而是从命中事实触发。
实际链路:
1 | PlayerAttack.ApplyHitbox() |
涉及文件:
| 文件 | 职责 |
|---|---|
unity-project/Assets/_Project/Combat/Scripts/HitFeedback.cs |
闪白、粒子、伤害数字 |
unity-project/Assets/_Project/Player/Scripts/CameraShake.cs |
相机震动和 rumble SFX |
unity-project/Assets/_Project/Player/Scripts/PlayerAttack.cs |
命中后触发 feedback、hitstop、shake 和事件 |
轻攻击和重攻击的反馈参数也被写回攻击配置:
| 攻击 | hitstop | 相机震动 |
|---|---|---|
| Light | 6 frames |
0.05 |
| Heavy | 12 frames |
0.10 |
HitFeedback 当前会生成临时 HitParticle 和 DamageNumber 对象,并记录最后一次伤害数字和暴击状态,方便测试验证。它不是最终表现资产,但已经把表现触发点放在正确位置:有效伤害发生之后,而不是输入按下之后。
技能系统
技能系统由三层组成:
| 文件 | 职责 |
|---|---|
unity-project/Assets/_Project/Player/Scripts/SkillSlot.cs |
定义 U/I/O 槽位 |
unity-project/Assets/_Project/Player/Scripts/SkillSO.cs |
配置技能类别、冷却、效果参数和 SFX |
unity-project/Assets/_Project/Player/Scripts/PlayerSkillController.cs |
消费输入、执行效果、管理冷却、发布事件 |
M3 实装 U/I 两个槽,O 作为预留槽保留在输入和结构中。8 个主动技能如下:
| 技能 | 槽位 | 类别 | 效果 |
|---|---|---|---|
juqi |
U | Internal | 下一击 2.5x 伤害,窗口 5s |
huti |
U | Internal | 60 点护盾,持续 2s |
zhiyu |
U | Internal | 回复 35 HP |
jinzhongzhao |
U | Internal | 无敌 1.5s |
shuangbu |
I | Lightness | 位移 5m,无敌 0.4s,冷却 3s |
feizhen |
I | HiddenWeapon | 3 枚飞针,每枚 8 伤害 |
guiyingbu |
I | Lightness | 隐匿 1.5s |
feidao |
I | HiddenWeapon | 8 向飞刀,每枚 6 伤害 |
技能效果不是硬写在输入层,而是通过 SkillEffectType 分发:
-
NextAttackDamageMultiplier -
DamageShield -
Heal -
Invulnerability -
Dash -
ProjectileBurst -
Concealment
这保证了输入槽位、技能资产和技能效果之间保持松耦合。后续 M4/M5 可以替换技能装配逻辑或 UI,而不必把 U/I/O 键写死进每个效果分支。
当前暗器类技能仍是白盒实现:在范围内找最近敌人,把 projectile 数量和单枚伤害合并成一次伤害应用。这对 M3 验收足够,但不是正式 projectile 系统。
武器和异世界修饰
武器系统的核心不在“做 24 把武器”,而在“同一把冷兵器能被世界观层修饰”。
涉及文件:
| 文件 | 职责 |
|---|---|
unity-project/Assets/_Project/Player/Scripts/WeaponConfigSO.cs |
定义武器类型和世界观修饰数组 |
unity-project/Assets/_Project/Player/Scripts/PlayerWeaponController.cs |
应用当前武器和当前世界观修饰 |
M3 覆盖 8 种武器:
-
Sword
-
Dao
-
Spear
-
Dagger
-
Staff
-
Hammer
-
Flexible
-
DualShortBlades
每种武器有 3 个世界观 form:
| 世界观 | 视觉 tint |
|---|---|
| Wasteland | #CC6633 |
| Cyberpunk | #22D3EE |
| Biohazard | #84CC16 |
所有 form 当前的 damageMultiplier 统一为 1.2,不同武器通过 rangeMultiplier 拉开基础差异,例如 Spear 和 Staff 是 1.1,Flexible 是 1.15,Dagger 和 DualShortBlades 是 0.95。
PlayerWeaponController.SetWorldview() 的作用是切换当前修饰,并更新:
-
伤害倍率。
-
攻击范围倍率。
-
视觉 tint。
-
OnWeaponModifierChanged事件。
M3 场景中使用默认武器接线来验证倍率,Player Prefab 本身不应该被某个测试场景的具体武器选择锁死。这个决策能避免后续关卡、掉落和 Build 系统接入时反复改基础 Prefab。
音频占位
M3 生成了 15 个 placeholder WAV,覆盖:
-
3 个闪避 whoosh。
-
轻击、重击和暴击命中。
-
8 个技能激活音效。
-
1 个 camera rumble。
这些文件位于 unity-project/Assets/_Project/Audio/SFX/M3/。它们的价值不是音质,而是把音频 hook 放进正确位置:闪避、挥击、命中、技能和震屏都已经能触发音频调用。正式混音和替换资产应留到音频生产阶段。
M3 测试场景
M3 测试场景是 unity-project/Assets/Scenes/TestScenes/M3_FullCombat.unity。
这个场景的角色不是“关卡体验”,而是白盒 harness:
-
放置玩家和 3 个敌人。
-
挂接 M3 dodge、skills、weapon、feedback 和 runtime probe。
-
让 automated probe 能在固定时间窗口内触发闪避、U 技能、I 技能和武器世界观切换。
-
让 EditMode / PlayMode 可以检查 M3 系统是否真实挂进 Unity 场景。
这仍然是测试场景,不是正式关卡。M4 才应该把 M3 战斗链放进手工关卡、Boss 和镜像 Boss 的设计环境里验证。
验证结果
M3 当前验证结果如下:
| 验证项 | 证据 | 结果 |
|---|---|---|
| M3 setup generation | ProjectV.EditorTools.M3ProjectBuilder.SetupM3 |
Passed |
| M3 setup verification | ProjectV.EditorTools.M3ProjectBuilder.VerifyM3Setup |
Passed |
| EditMode | qa/results/M3_EditMode_TestResults.xml |
20 / 20 passed |
| PlayMode | qa/results/M3_PlayMode_TestResults.xml |
10 / 10 passed |
| Windows Development Build | unity-project/Builds/Windows/PROJECT-V.exe |
Success,构建产物被 gitignore 排除 |
| Runtime Probe 30s | qa/results/M3_RuntimeProbe.json |
Avg 60.008 FPS,m3SystemsReady: true |
| Runtime Probe 60s | qa/results/M3_RuntimeProbe_60s.json |
Avg 59.999 FPS,frameCount: 3600,m3SystemsReady: true |
60s probe 的关键结果:
1 | { |
这个结果说明 runtime probe 不只是空跑场景。它实际确认了:
-
M3 系统挂接完成。
-
闪避成功开始。
-
U 槽技能成功激活。
-
I 槽技能成功激活。
-
武器世界观切换后伤害倍率读到
1.2。 -
3 个敌人存在。
-
玩家在 60s probe 后仍存活。
EditMode 覆盖了配置、输入、Prefab、场景、技能资产、武器资产和 24 个世界观 form。PlayMode 覆盖了闪避无敌和冷却、命中反馈链、聚气下一击伤害、轻攻击命中取消、空挥不能取消、projectile / concealment 技能效果和武器修饰倍率。
已知风险
M3 已通过阶段验收,但还有明确风险:
-
60s runtime probe 的平均 FPS 是
59.999,但minFps出现48.154单帧 outlier。当前证据只说明它是单点异常,不能解释根因。M4 需要增加 percentile frame timing、GC allocation 和 spike 采样。 -
15 个 WAV 都是 placeholder,不代表最终音频质量。
-
暗器技能目前是白盒化命中,不是正式 projectile 物理与视觉系统。
-
正式技能 HUD 仍未实现,O 槽只是结构预留。
-
M3 场景是测试 harness,不代表正式关卡节奏。
-
30 分钟 1080p soak 未执行,仍然是后续 release-grade QA gate。
M4 方向
docs 仓库的 project/PROJECT_STATE.md 已把项目状态推进到 M3 完成、准备 M4。M4 不应该继续堆更多孤立战斗功能,而应该把 M3 的战斗链放进内容压力里验证。
M4 的合理方向:
-
制作手工关卡原型,让移动、敌人和战斗不再只在白盒平台上验证。
-
引入 Boss 和镜像 Boss,用更长的攻击节奏测试闪避、取消、技能和反馈链。
-
为 Boss 行为准备 PlayMode / QA matrix,而不是只靠人工观察。
-
把 M3 的 runtime probe 扩展成更细的性能采样,至少记录 percentile 和 GC spike。
-
让技能、武器和敌人配置进入关卡语境,验证这些系统是否真的支持可玩构成。
M3 的阶段价值在这里:PROJECT-V 现在已经有一条从输入到攻击、伤害、反馈、技能、武器倍率和自动验证的完整战斗实现链。后续如果战斗不好玩,问题会更多来自调参、内容和表现,而不是“工程链路还没接起来”。