refactor(ai): 终审收尾——删死字段/事件转换刷新IsSuspended + 局部事件与双实例独立性测试
This commit is contained in:
@@ -159,5 +159,42 @@ namespace BaseGames.Tests.EditMode.AI
|
|||||||
Assert.IsEmpty(rt.Trace);
|
Assert.IsEmpty(rt.Trace);
|
||||||
Assert.IsFalse(ctx.BB.Has("scratch"));
|
Assert.IsFalse(ctx.BB.Has("scratch"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void LocalEventTransition_Fires()
|
||||||
|
{
|
||||||
|
var b = new BrainBuilder();
|
||||||
|
b.Entry("Idle");
|
||||||
|
b.State("Idle").To("Flee").OnEvent(AiSignal.Parried);
|
||||||
|
b.State("Flee");
|
||||||
|
var ctx = new FakeAiContext();
|
||||||
|
var rt = new AiRuntime(b.Build(), ctx);
|
||||||
|
rt.Send(AiSignal.Parried);
|
||||||
|
rt.Tick(0.1f);
|
||||||
|
Assert.AreEqual("Flee", rt.CurrentStateName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TwoRuntimes_ShareGraph_IndependentStateAndTimer()
|
||||||
|
{
|
||||||
|
var graph = Graph(); // 共享同一张不可变图
|
||||||
|
var a = new FakeAiContext(); var rtA = new AiRuntime(graph, a);
|
||||||
|
var b = new FakeAiContext(); var rtB = new AiRuntime(graph, b);
|
||||||
|
|
||||||
|
// A 发现玩家推进,B 不动 —— 状态独立
|
||||||
|
a.S.Sees = true; rtA.Tick(0.1f);
|
||||||
|
Assert.AreEqual("Chase", rtA.CurrentStateName);
|
||||||
|
Assert.AreEqual("Patrol", rtB.CurrentStateName);
|
||||||
|
|
||||||
|
// 两者各自进入 Search(计时归零)
|
||||||
|
a.S.Sees = false; a.S.LostForValue = 5f; rtA.Tick(0.1f); // A -> Search
|
||||||
|
b.S.Sees = true; rtB.Tick(0.1f); // B -> Chase
|
||||||
|
b.S.Sees = false; b.S.LostForValue = 5f; rtB.Tick(0.1f); // B -> Search
|
||||||
|
|
||||||
|
// 只推进 A 的 After 计时越过 3s;B 计时独立、不动
|
||||||
|
rtA.Tick(2f); rtA.Tick(2f); // A Search 计时 ~4 -> After -> Patrol
|
||||||
|
Assert.AreEqual("Patrol", rtA.CurrentStateName);
|
||||||
|
Assert.AreEqual("Search", rtB.CurrentStateName); // B 独立,仍在 Search
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ namespace BaseGames.AI
|
|||||||
{
|
{
|
||||||
var sig = _pending.Dequeue();
|
var sig = _pending.Dequeue();
|
||||||
if (TryEventTransition(sig))
|
if (TryEventTransition(sig))
|
||||||
return; // 事件触发转换后本帧结束
|
{
|
||||||
|
IsSuspended = false; // 已推进转换,非挂起
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) IsControllable 让位门:受击/硬直/击飞时挂起,不推进决策也不跑 Tick。
|
// 2) IsControllable 让位门:受击/硬直/击飞时挂起,不推进决策也不跑 Tick。
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace BaseGames.AI
|
|||||||
s = new AiState(name);
|
s = new AiState(name);
|
||||||
_states[name] = s;
|
_states[name] = s;
|
||||||
}
|
}
|
||||||
return new StateBuilder(this, s);
|
return new StateBuilder(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalBuilder Global() => new GlobalBuilder(this);
|
public GlobalBuilder Global() => new GlobalBuilder(this);
|
||||||
@@ -52,9 +52,8 @@ namespace BaseGames.AI
|
|||||||
|
|
||||||
public sealed class StateBuilder
|
public sealed class StateBuilder
|
||||||
{
|
{
|
||||||
readonly BrainBuilder _owner;
|
|
||||||
readonly AiState _state;
|
readonly AiState _state;
|
||||||
internal StateBuilder(BrainBuilder owner, AiState state) { _owner = owner; _state = state; }
|
internal StateBuilder(AiState state) { _state = state; }
|
||||||
|
|
||||||
public StateBuilder OnEnter(Action<IAiContext> fn) { _state.OnEnter = fn; return this; }
|
public StateBuilder OnEnter(Action<IAiContext> fn) { _state.OnEnter = fn; return this; }
|
||||||
public StateBuilder Tick(Action<IAiContext> fn) { _state.OnTick = (c, _) => fn(c); return this; }
|
public StateBuilder Tick(Action<IAiContext> fn) { _state.OnTick = (c, _) => fn(c); return this; }
|
||||||
|
|||||||
Reference in New Issue
Block a user