test(ai): PerceptionStateMachine locomotion 意图回归测试(绿)

新增 PerceptionStateMachineTests:待机态声明 Idle locomotion 意图、进追逐区
转 Chase 并触发追击能力、Chase 态不再直接声明 locomotion 意图。测试程序集
补 BaseGames.Enemies 引用。断言经真实状态机 + 测试 fake 验证通过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@
This commit is contained in:
2026-07-10 12:21:28 +08:00
parent 0f0112eb91
commit 63b3701fc7
3 changed files with 74 additions and 0 deletions
@@ -0,0 +1,62 @@
using System.Collections.Generic;
using NUnit.Framework;
using BaseGames.AI;
using BaseGames.Enemies;
namespace BaseGames.Tests.EditMode.AI
{
/// <summary>
/// 回归测试:PerceptionStateMachine 的待机/巡逻/警觉声明 locomotion 意图,
/// 追击走能力(不直接驱动 locomotion)。转换规则见 spec《敌人感知↔状态》。
/// </summary>
public class PerceptionStateMachineTests
{
static AiGraph Graph()
{
var b = new BrainBuilder();
PerceptionStateMachine.Add(b, new PerceptionStateMachine.Config
{
Idle = "Idle", Patrol = "Patrol", Alert = "Alert", Chase = "Chase",
Death = "Death", Entry = "Idle", Rest = "Patrol",
IdleMode = LocomotionMode.Idle,
PatrolMode = LocomotionMode.Patrol,
AlertMode = LocomotionMode.Face,
ChaseAbilityId = "chase",
});
return b.Build();
}
[Test]
public void EntryIdleState_SetsIdleLocomotionMode()
{
var ctx = new FakeAiContext();
var rt = new AiRuntime(Graph(), ctx);
Assert.AreEqual("Idle", rt.CurrentStateName);
Assert.AreEqual(LocomotionMode.Idle, ctx.L.CurrentMode); // 进入待机即声明 Idle 意图
}
[Test]
public void InChaseZone_TransitionsToChase_AndTriggersChaseAbility()
{
var ctx = new FakeAiContext();
var rt = new AiRuntime(Graph(), ctx);
ctx.S.Chase = true; // 进入追逐区
rt.Tick(0.1f);
Assert.AreEqual("Chase", rt.CurrentStateName);
CollectionAssert.Contains(ctx.C.Used, "chase"); // 追击由能力触发
}
[Test]
public void ChaseState_DoesNotDeclareLocomotionIntent()
{
// 追击移动归能力;AI 在 Chase 态不应再声明 SetMode/Approach/Face。
var ctx = new FakeAiContext();
var rt = new AiRuntime(Graph(), ctx);
ctx.S.Chase = true;
rt.Tick(0.1f); // Idle → Chase(退出 Idle 会调用一次 Stop)
int lastStop = ctx.L.Calls.FindLastIndex(s => s == "Stop");
var afterEnteringChase = ctx.L.Calls.GetRange(lastStop + 1, ctx.L.Calls.Count - lastStop - 1);
CollectionAssert.IsEmpty(afterEnteringChase);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 11d9c7954aefbd84e93387db27268546
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -6,6 +6,7 @@
"BaseGames.Combat",
"BaseGames.Core",
"BaseGames.AI",
"BaseGames.Enemies",
"BaseGames.Core.Events",
"BaseGames.Core.Save",
"BaseGames.EventChain",