feat(enemy): 嘲风迁移到新轨——能力资产 + 对象树 + 开战触发
删 5 个空 BossSkillSO,用向导重建 6 个 EnemyAbilitySO(含入场演出)。 预制体经放置工具重新产出:EnemyAiBrain(_definitionId=ChaoFeng) + BossPhaseAbilityGate + 6 个能力组件,不再有 BossSkillExecutor。 TestRoomA 加开战触发区。三件自检 0 error,EditMode 260/260。 首次真正跑通两条脚手架,暴露并修掉三个它们的缺陷: 1. 放置工具漏挂 EnemyLocomotion(PlaceChaoFeng 及其余 7 个敌人放置入口)。 EnemyBase.Awake 找不到它即报错且 Locomotion 为 null, AiStateFragments.ApplyLocomotion 直接 NRE、AiRuntime 构造失败—— Boss 建不出决策层,完全不动。已在所有敌人放置入口补齐。 2. 向导把空中阶段的 wind_stone 建成 requiresGrounded=true(SO 默认值)。 Boss 浮空后 IsGrounded 恒 false,选招器把它滤掉,空中阶段站着不打。 能力定义表加 grounded 列,阶段 1 的招显式置 false。 3. BossPhaseAbilityGate.ApplyPhase 只在 EnterPhase 与 OnSpawn 调用, 直接摆场景里的 Boss 两条路径都不走,开局后续阶段的招仍在候选池里。 BossBase.Start 补一次初始阶段应用。 另:Phase1_Tornado_HitBox 确认零消费者(龙卷判定随弹体走), 从放置工具删除其创建与伤害源绑定; EnemyBrainContext.UseAbility 对解析不到的能力 id 改为 LogError, 不再静默返回 false(CLAUDE.md 第 6 条); 新增 PlaceBossFightTrigger 脚手架(此前无对应创建入口,属裸建)。 播放模式实测状态序列(TestRoomA,玩家走进触发区): Wait →(Engaged) Intro → Ground⇄GroundAttack →(hp<50%) PhaseTransition → Air⇄AirAttack,浮空生效,阶段门按阶段换池,全程 0 报错。 动画 Clip 未接入,PlayClipAbility 空 clip 立即完成,故只验证了状态流转, 判定时序待美术接入后再校。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -328,12 +328,12 @@ namespace BaseGames.Editor
|
||||
var factory = MakeActionGroup();
|
||||
factory.Add(MakeFactoryButton("ENM_ChaoFeng_Stats.asset", () => { CreateChaoFengStatsSO(); RefreshSOStatus(); }));
|
||||
factory.Add(MakeFactoryButton("ENM_ChaoFeng_AnimConfig.asset",() => { CreateChaoFengAnimConfigSO(); RefreshSOStatus(); }));
|
||||
foreach (var (abName, abId, soType, r, cd, w, ph) in ChaoFengAbilityDefs)
|
||||
foreach (var (abName, abId, soType, r, cd, w, ph, g) in ChaoFengAbilityDefs)
|
||||
{
|
||||
string cName = abName; string cId = abId; System.Type cType = soType;
|
||||
float cR = r, cCd = cd, cW = w; int cPh = ph;
|
||||
float cR = r, cCd = cd, cW = w; int cPh = ph; bool cG = g;
|
||||
factory.Add(MakeFactoryButton($"ABL_ChaoFeng_{cName}.asset",
|
||||
() => { CreateChaoFengAbilitySO(cName, cId, cType, cR, cCd, cW, cPh); RefreshSOStatus(); }));
|
||||
() => { CreateChaoFengAbilitySO(cName, cId, cType, cR, cCd, cW, cPh, cG); RefreshSOStatus(); }));
|
||||
}
|
||||
root.Add(factory);
|
||||
|
||||
@@ -691,7 +691,7 @@ namespace BaseGames.Editor
|
||||
|
||||
private static void CreateChaoFengAbilitySO(
|
||||
string abilityName, string abilityId, System.Type soType,
|
||||
float rangeRadius, float cooldown, float weight, int phase)
|
||||
float rangeRadius, float cooldown, float weight, int phase, bool requiresGrounded)
|
||||
{
|
||||
const string dir = "Assets/_Game/Data/Enemies/ChaoFeng/Abilities";
|
||||
string name = $"ABL_ChaoFeng_{abilityName}";
|
||||
@@ -704,6 +704,9 @@ namespace BaseGames.Editor
|
||||
? "入场演出,不进选招池"
|
||||
: $"阶段 {phase} 招式;阶段可用性由 BossPhaseAbilityGate 配置";
|
||||
so.cooldown = cooldown;
|
||||
// 空中阶段的招必须放开这一项:选招器把 requiresGrounded 当硬门,
|
||||
// Boss 浮空时 IsGrounded 恒为 false,留着默认 true 会让该招永远选不中。
|
||||
so.requiresGrounded = requiresGrounded;
|
||||
|
||||
if (phase < 0)
|
||||
{
|
||||
@@ -727,25 +730,27 @@ namespace BaseGames.Editor
|
||||
/// (见放置工具 PlaceChaoFeng),这里只记录供说明文本使用。
|
||||
/// rangeRadius 必须 > 0:category==Attack 且射程为 0 的招永远够不着玩家,
|
||||
/// EnemyAbilitySO.Validate 与 EnemyBase.BuildAttackSelector 都会显式报错。
|
||||
/// grounded 是选招器的硬门:阶段 1 是空中阶段(Boss 浮空,IsGrounded 恒 false),
|
||||
/// 该阶段的招必须为 false,否则永远选不中,空中阶段会站着不打。
|
||||
/// </summary>
|
||||
private static readonly (string name, string id, System.Type soType,
|
||||
float rangeRadius, float cooldown, float weight, int phase)[]
|
||||
float rangeRadius, float cooldown, float weight, int phase, bool grounded)[]
|
||||
ChaoFengAbilityDefs =
|
||||
{
|
||||
("Intro", "chaofeng_intro", typeof(PlayClipAbilitySO), 0f, 0f, 0f, -1),
|
||||
("Boomerang", "boomerang", typeof(PlayClipAbilitySO), 9f, 4.0f, 1.0f, 0),
|
||||
("FanCombo", "fan_combo", typeof(EnemyAbilitySO), 2.5f, 2.5f, 1.5f, 0),
|
||||
("TornadoSmall", "tornado_small", typeof(PlayClipAbilitySO), 8f, 3.5f, 1.2f, 0),
|
||||
("TornadoLarge", "tornado_large", typeof(PlayClipAbilitySO), 12f, 6.0f, 0.8f, 0),
|
||||
("WindStone", "wind_stone", typeof(PlayClipAbilitySO), 12f, 3.0f, 1.0f, 1),
|
||||
("Intro", "chaofeng_intro", typeof(PlayClipAbilitySO), 0f, 0f, 0f, -1, true),
|
||||
("Boomerang", "boomerang", typeof(PlayClipAbilitySO), 9f, 4.0f, 1.0f, 0, true),
|
||||
("FanCombo", "fan_combo", typeof(EnemyAbilitySO), 2.5f, 2.5f, 1.5f, 0, true),
|
||||
("TornadoSmall", "tornado_small", typeof(PlayClipAbilitySO), 8f, 3.5f, 1.2f, 0, true),
|
||||
("TornadoLarge", "tornado_large", typeof(PlayClipAbilitySO), 12f, 6.0f, 0.8f, 0, true),
|
||||
("WindStone", "wind_stone", typeof(PlayClipAbilitySO), 12f, 3.0f, 1.0f, 1, false),
|
||||
};
|
||||
|
||||
private static void CreateAllChaoFengSOs()
|
||||
{
|
||||
CreateChaoFengStatsSO();
|
||||
CreateChaoFengAnimConfigSO();
|
||||
foreach (var (n, id, t, r, cd, w, ph) in ChaoFengAbilityDefs)
|
||||
CreateChaoFengAbilitySO(n, id, t, r, cd, w, ph);
|
||||
foreach (var (n, id, t, r, cd, w, ph, g) in ChaoFengAbilityDefs)
|
||||
CreateChaoFengAbilitySO(n, id, t, r, cd, w, ph, g);
|
||||
AssetDatabase.SaveAssets();
|
||||
EditorUtility.DisplayDialog("创建完成",
|
||||
"全部嘲风 SO 已创建(已存在的跳过)。\n" +
|
||||
@@ -825,7 +830,7 @@ namespace BaseGames.Editor
|
||||
("ENM_ChaoFeng_Stats", FindAtPath<EnemyStatsSO>($"{dir}/ENM_ChaoFeng_Stats.asset")),
|
||||
("ENM_ChaoFeng_AnimConfig",FindAtPath<EnemyAnimationConfigSO>($"{dir}/ENM_ChaoFeng_AnimConfig.asset")),
|
||||
};
|
||||
foreach (var (abName, _, _, _, _, _, _) in ChaoFengAbilityDefs)
|
||||
foreach (var (abName, _, _, _, _, _, _, _) in ChaoFengAbilityDefs)
|
||||
checks.Add(($"ABL_ChaoFeng_{abName}",
|
||||
FindAtPath<EnemyAbilitySO>($"{ablDir}/ABL_ChaoFeng_{abName}.asset")));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user