using System.Collections.Generic; namespace BaseGames.AI { /// /// 每敌人实例一份的轻量执行器。持有当前状态、状态计时器、trace。 /// 共享的 AiGraph 不持有任何实例态。 /// public sealed class AiRuntime { readonly AiGraph _graph; readonly IAiContext _ctx; AiState _current; float _timeInState; readonly Queue _pending = new Queue(); readonly List _trace = new List(); const int TraceCap = 16; public AiRuntime(AiGraph graph, IAiContext ctx) { _graph = graph; _ctx = ctx; EnterInitial(); } public string CurrentStateName => _current.Name; public bool IsSuspended { get; private set; } public IReadOnlyList Trace => _trace; public float TimeInStateValue => _timeInState; void EnterInitial() { _byContext.Remove(_ctx); // 幂等:避免同一 ctx 重复键异常 _byContext.Add(_ctx, this); _current = _graph.GetState(_graph.EntryState); _timeInState = 0f; _current.OnEnter?.Invoke(_ctx); } /// 供 BrainBuilder.After() 通过 context 读取当前 runtime 的状态计时器。 static readonly System.Runtime.CompilerServices.ConditionalWeakTable _byContext = new System.Runtime.CompilerServices.ConditionalWeakTable(); internal static float TimeInState(IAiContext ctx) => _byContext.TryGetValue(ctx, out var rt) ? rt._timeInState : 0f; /// 推送一个信号,用于事件驱动转换(push)。 public void Send(AiSignal signal) => _pending.Enqueue(signal); public void Tick(float dt) { // 1) 事件转换:push,最高优先,且不受 IsControllable 门限制 while (_pending.Count > 0) { var sig = _pending.Dequeue(); if (TryEventTransition(sig)) { IsSuspended = false; // 已推进转换,非挂起 return; } } // 2) IsControllable 让位门:受击/硬直/击飞时挂起,不推进决策也不跑 Tick。 if (!_ctx.Vitals.IsControllable) { IsSuspended = true; return; } IsSuspended = false; // 3) 条件转换 _timeInState += dt; if (TryTransition()) return; // 本帧发生转换,新状态下一帧再 Tick _current.OnTick?.Invoke(_ctx, dt); } bool TryTransition() { // 全局/父层转换优先(只评估条件型;事件型由 TryEventTransition 处理) var globals = _graph.GlobalTransitions; for (int i = 0; i < globals.Count; i++) { var t = globals[i]; if (!t.IsEvent && t.Condition(_ctx)) { if (Switch(t)) return true; } } var locals = _current.Transitions; for (int i = 0; i < locals.Count; i++) { var t = locals[i]; if (!t.IsEvent && t.Condition(_ctx)) { if (Switch(t)) return true; } } return false; } bool TryEventTransition(AiSignal sig) { var globals = _graph.GlobalTransitions; for (int i = 0; i < globals.Count; i++) { var t = globals[i]; if (t.IsEvent && t.Event.Value == sig) { if (Switch(t)) return true; } } var locals = _current.Transitions; for (int i = 0; i < locals.Count; i++) { var t = locals[i]; if (t.IsEvent && t.Event.Value == sig) { if (Switch(t)) return true; } } return false; } bool Switch(Transition t) { if (t.Target == _current.Name) return false; // 自转换:no-op,不重跑 Exit/Enter var from = _current.Name; _current.OnExit?.Invoke(_ctx); _current = _graph.GetState(t.Target); _timeInState = 0f; RecordTrace(new TransitionRecord(from, t.Target, t.Label)); _current.OnEnter?.Invoke(_ctx); return true; } void RecordTrace(TransitionRecord r) { _trace.Add(r); if (_trace.Count > TraceCap) _trace.RemoveAt(0); } /// 对象池复用时重置到初始态,清空所有实例态。 public void Reset() { _pending.Clear(); _trace.Clear(); _ctx.Blackboard.Clear(); IsSuspended = false; EnterInitial(); } } }