feat(enemy): RushEngagement——两个耦合 flag 合并为 RushExit 语义枚举

This commit is contained in:
2026-07-29 12:32:50 +08:00
parent 1c6ce57648
commit bb1e2f7705
7 changed files with 204 additions and 0 deletions
@@ -0,0 +1,83 @@
using NUnit.Framework;
using BaseGames.AI;
using BaseGames.Enemies;
namespace BaseGames.Tests.EditMode.AI
{
public class RushEngagementTests
{
const string Rest = "Rest";
// 单独跑交战模块:手搓一个最小 rest 态,直接以交战入口为图入口。
static AiRuntime RunAlone(RushEngagement m, FakeAiContext ctx)
{
var b = new BrainBuilder();
AiStateFragments.Locomotion(b, Rest, LocomotionMode.Patrol);
m.Build(b, Rest);
b.Entry(m.EntryState);
return new AiRuntime(b.Build(), ctx);
}
[Test]
public void OnLostTarget_CanEngage_IsChaseZoneOnly()
{
var m = new RushEngagement("rush", RushExit.OnLostTarget);
var ctx = new FakeAiContext();
ctx.C.CanUse = false; // 冷却中
ctx.S.Chase = true;
Assert.IsTrue(m.CanEngage(ctx)); // 不看冷却
Assert.AreEqual("InChaseZone", m.EngageLabel);
}
[Test]
public void Committed_CanEngage_AlsoRequiresOffCooldown()
{
var m = new RushEngagement("rush", RushExit.Committed);
var ctx = new FakeAiContext();
ctx.S.Chase = true;
ctx.C.CanUse = false;
Assert.IsFalse(m.CanEngage(ctx)); // CD 中不进
ctx.C.CanUse = true;
Assert.IsTrue(m.CanEngage(ctx));
Assert.AreEqual("InChaseZone+offCD", m.EngageLabel);
}
[Test]
public void OnLostTarget_ExitsWhenAllZonesLost()
{
var ctx = new FakeAiContext { };
ctx.S.Chase = true;
var rt = RunAlone(new RushEngagement("rush", RushExit.OnLostTarget), ctx);
Assert.AreEqual(RushEngagement.Rush, rt.CurrentStateName);
rt.Tick(0.1f);
Assert.AreEqual(RushEngagement.Rush, rt.CurrentStateName); // 还在追逐区
ctx.S.Chase = false; ctx.S.Vision = false;
rt.Tick(0.1f);
Assert.AreEqual(Rest, rt.CurrentStateName);
}
[Test]
public void Committed_NotInterruptedByLosingTarget()
{
var ctx = new FakeAiContext();
ctx.S.Chase = true;
var rt = RunAlone(new RushEngagement("rush", RushExit.Committed), ctx);
Assert.AreEqual(RushEngagement.Rush, rt.CurrentStateName);
ctx.S.Chase = false; ctx.S.Vision = false; // 中途丢失感知
rt.Tick(0.1f);
Assert.AreEqual(RushEngagement.Rush, rt.CurrentStateName); // 冲锋不被打断
ctx.C.Running = null; // 冲锋跑完
rt.Tick(0.1f);
Assert.AreEqual(Rest, rt.CurrentStateName);
}
[Test]
public void Build_Throws_WhenAbilityMissing()
{
var b = new BrainBuilder();
AiStateFragments.Locomotion(b, Rest, LocomotionMode.Patrol);
var m = new RushEngagement(); // 未配能力
Assert.Throws<System.InvalidOperationException>(() => m.Build(b, Rest));
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a4d24a2a0dafa43488bf4da3c32ca72a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: