From 5ad26b16e864e8084449b4b568c2e6f4b68e5053 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Fri, 3 Jul 2026 14:20:19 +0800 Subject: [PATCH] =?UTF-8?q?perf(ai):=20AiRuntime=20=E7=83=AD=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E5=8E=BB=E8=A3=85=E7=AE=B1/=E5=BC=B1=E8=A1=A8?= =?UTF-8?q?=E4=B8=80=E6=AC=A1=E6=B3=A8=E5=86=8C=20+=20=E8=A1=A5=20OnExit/E?= =?UTF-8?q?ntry=20OnEnter=20=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Tests/EditMode/AI/AiRuntimeTests.cs | 22 ++++++++++++++++++++++ Assets/_Game/Scripts/AI/AiRuntime.cs | 17 ++++++++++++----- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Assets/Tests/EditMode/AI/AiRuntimeTests.cs b/Assets/Tests/EditMode/AI/AiRuntimeTests.cs index e00fdaaa..b0348569 100644 --- a/Assets/Tests/EditMode/AI/AiRuntimeTests.cs +++ b/Assets/Tests/EditMode/AI/AiRuntimeTests.cs @@ -15,6 +15,7 @@ namespace BaseGames.Tests.EditMode.AI .To("Chase").When(c => c.Sensor.SeesPlayer(), "SeesPlayer"); b.State("Chase") .OnEnter(c => c.Mover.FacePlayer()) + .OnExit(c => c.Mover.Stop()) .Tick(c => c.Mover.MoveTo(c.Sensor.LastKnown)) .To("Search").When(c => c.Sensor.LostFor(2f), "LostFor(2s)"); b.State("Search") @@ -89,5 +90,26 @@ namespace BaseGames.Tests.EditMode.AI rt.Tick(1.5f); Assert.AreEqual("Patrol", rt.CurrentStateName); // 累计 >3s } + + [Test] + public void Switch_RunsPreviousStateOnExit() + { + var ctx = new FakeAiContext(); + var rt = new AiRuntime(Graph(), ctx); + ctx.S.Sees = true; rt.Tick(0.1f); // Patrol -> Chase + ctx.S.Sees = false; ctx.S.LostForValue = 5f; rt.Tick(0.1f);// Chase -> Search (触发 Chase.OnExit) + CollectionAssert.Contains(ctx.M.Calls, "Stop"); + } + + [Test] + public void Construct_InvokesEntryOnEnter() + { + var b = new BrainBuilder(); + b.Entry("Start"); + b.State("Start").OnEnter(c => c.Mover.LookAround()); + var ctx = new FakeAiContext(); + var rt = new AiRuntime(b.Build(), ctx); + CollectionAssert.Contains(ctx.M.Calls, "LookAround"); + } } } diff --git a/Assets/_Game/Scripts/AI/AiRuntime.cs b/Assets/_Game/Scripts/AI/AiRuntime.cs index f4c822ea..5a15df5f 100644 --- a/Assets/_Game/Scripts/AI/AiRuntime.cs +++ b/Assets/_Game/Scripts/AI/AiRuntime.cs @@ -31,6 +31,8 @@ namespace BaseGames.AI void EnterInitial() { + _byContext.Remove(_ctx); // 幂等:避免同一 ctx 重复键异常 + _byContext.Add(_ctx, this); _current = _graph.GetState(_graph.EntryState); _timeInState = 0f; _current.OnEnter?.Invoke(_ctx); @@ -44,9 +46,6 @@ namespace BaseGames.AI public void Tick(float dt) { - _byContext.Remove(_ctx); - _byContext.Add(_ctx, this); - // IsControllable 让位门:受击/硬直/击飞时挂起,不推进决策也不跑 Tick。 if (!_ctx.Vitals.IsControllable) { @@ -66,13 +65,21 @@ namespace BaseGames.AI bool TryTransition() { // 全局/父层转换优先(本任务只评估条件型;事件型在 Task 6 处理) - foreach (var t in _graph.GlobalTransitions) + var globals = _graph.GlobalTransitions; + for (int i = 0; i < globals.Count; i++) + { + var t = globals[i]; if (!t.IsEvent && t.Condition(_ctx)) return Switch(t); + } - foreach (var t in _current.Transitions) + var locals = _current.Transitions; + for (int i = 0; i < locals.Count; i++) + { + var t = locals[i]; if (!t.IsEvent && t.Condition(_ctx)) return Switch(t); + } return false; }