# SharedFSM **Repository Path**: TheSourceCode/SharedFSM ## Basic Information - **Project Name**: SharedFSM - **Description**: No description available - **Primary Language**: C# - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-02-08 - **Last Updated**: 2026-05-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: Unity ## README # SharedFSM ## 使用示例 ### 安装 1. 找到项目的manifest.json 2. 在dependencies中加一句:"com.shared.sharedfsm": "https://gitee.com/TheSourceCode/SharedFSM.git?path=src/SharedFSM/Assets", ### 实战示例:好吃懒做的Bob 和 勤劳的Elsa #### 场景剧本 1. Elsa做家务 2. Bob回家睡觉 3. Elsa停止家务,并开始做饭 4. Elsa做好饭,通知Bob吃饭,并开始做家务 5. Bob 收到消息,从睡觉状态切换到吃饭状态 6. Bob吃饭完后,回到第2条,继续睡觉。 #### 游戏状态定义 推荐在项目中都建立这两个类 ```Csharp public class GameFSM : FSM { public GameFSM(BaseGameEntity owner, IGameState globalState = null) : base(owner, globalState) { } } public interface IGameState : IState { // 一些自定义方法 } ``` #### 实体定义 ```Csharp public class BaseGameEntity : FSMEntity { // 一些自定义方法 } public class Bob : BaseGameEntity { private void Awake() { _fsm = new GameFSM(this); ChangeState(ServiceProvider.Get()); } } public class Elsa : BaseGameEntity { public bool IsCooking { get; set; } private void Awake() { _fsm = new GameFSM(this, ServiceProvider.Get()); ChangeState(ServiceProvider.Get()); } } ``` #### 游戏控制器 ```Csharp using System.Collections; using System.Reflection; using SharedFSM; using SharedFSM.Entities; using SharedFSM.Msgs; using UnityEngine; public class GameController : MonoBehaviour { private IFSMEntityManager _entityManager; private void Awake() { // 注册所有state SimpleServiceContainer.Instance.RegisterAllImplementations( // 当前程序集 Assembly.GetExecutingAssembly() //Assembly.Load("Other") // 其他程序集) ); SimpleServiceContainer.Instance.Register(); SimpleServiceContainer.Instance.Register(); DontDestroyOnLoad(gameObject); } private void Start() { // 通过接口获取管理器,不依赖具体实现 _entityManager = ServiceProvider.Get(); // 初始化妻子Elsa var elsaObject = new GameObject("Elsa"); var elsa = elsaObject.AddComponent(); elsa.ID = 2; _entityManager.RegisterEntity(elsa); StartCoroutine(InitBob()); } private IEnumerator InitBob() { // 一段时间后初始化Bob yield return new WaitForSeconds(2); var bobObject = new GameObject("Bob"); var bob = bobObject.AddComponent(); bob.ID = 1; _entityManager.RegisterEntity(bob); } } ``` #### 此处省略状态类 #### 在unity运行 新建一个空场景,加两个空对象,分别挂载 `MessageController` 和`GameController` ## 设计思路 ### 状态机核心(FSM) ```Csharp public class FSM where TState : IState { protected readonly TOwner Owner; public TState PreviousState { get; protected set; } public TState CurrentState { get; protected set; } public TState GlobalState { get; protected set; } public FSM(TOwner owner, TState globalState = default) { Owner = owner; GlobalState = globalState; } public void ChangeState(TState newState) { // 先执行上一个状态的退出,在执行当前状态的进入。 PreviousState = CurrentState; CurrentState?.ExitState(Owner); CurrentState = newState; CurrentState.EnterState(Owner); } public void Update() { CurrentState?.UpdateState(Owner); GlobalState?.UpdateState(Owner); } public void RevertToPreviousState() { if (PreviousState != null) { ChangeState(PreviousState); } } public bool HandleMessage(Telegram msg) { if (CurrentState != null && CurrentState.OnMessage(Owner, msg)) { return true; } if (GlobalState != null && GlobalState.OnMessage(Owner, msg)) { return true; } return false; } } public interface IState { void EnterState(TOwner owner = default); void UpdateState(TOwner owner = default); void ExitState(TOwner owner = default); bool OnMessage(TOwner owner, Telegram msg); } ``` #### 设计思路 * 全局状态:例如,在《模拟人生》中,Sim可能会感到本能的迫切要求,不得不去洗手间去方便。这种急切的需求会发生在Sim的任何状态或任何可能的时间。最好创建一个全局状态,这样每次FSM更新时就会被调用。 * 记录上一个状态:例如,还是在《模拟人生》中,Sim去完洗手间,还需要回到之前的状态,这种需求就需要记录上一个状态。 * 消息系统:HandleMessage函数处理外部消息。例如,《刺客信条》中,玩家进入了敌人的视野范围,敌人就会停止巡逻,开始攻击追击玩家。 ### 消息系统(Message Dispatcher) ```Csharp public class MessageDispatcher { // 通过接口依赖,而非具体类 private readonly IFSMEntityManager _entityManager; public MessageDispatcher() { _entityManager = ServiceProvider.Get(); } public SortedSet PriorityQueue { get; private set; } = new SortedSet(Comparer.Create((t1, t2) => t1.DispatchTime.CompareTo(t2.DispatchTime))); public void DispatchMessage(double delay, int sender, int receiver, int msg, object extraInfo = null) { if (delay <= 0) { var receiverEntity = _entityManager?.GetEntityFromID(receiver); receiverEntity?.HandleMessage(new Telegram(sender, receiver, msg, 0, extraInfo)); } else { var telegram = new Telegram(sender, receiver, msg, Time.time + delay, extraInfo); PriorityQueue.Add(telegram); } } } public class MessageController : MonoBehaviour { private IFSMEntityManager _entityManager; private MessageDispatcher _messageDispatcher; private void Awake() { DontDestroyOnLoad(gameObject); } private void Start() { _entityManager = ServiceProvider.Get(); _messageDispatcher = ServiceProvider.Get(); } private void Update() { DispatchDelayedMessages(); } private void DispatchDelayedMessages() { while (_messageDispatcher.PriorityQueue.Count > 0 && _messageDispatcher.PriorityQueue.Min.DispatchTime <= Time.time) { var telegram = _messageDispatcher.PriorityQueue.Min; var receiverEntity = _entityManager?.GetEntityFromID(telegram.Receiver); receiverEntity?.HandleMessage(telegram); _messageDispatcher.PriorityQueue.Remove(telegram); } } } ``` #### 设计思路 * 支持"5秒后通知我"这样的时序逻辑 * 使用 SortedSet 保证 O(log n) 的插入和 O(1) 的最小值获取 * 通过实体管理器获取实体,_entityManager.GetEntityFromID ### 实体管理 ```Csharp public class FSMEntity : MonoBehaviour, IFSMEntity where TState : IState where TOwner : IFSMEntity { protected FSM _fsm; public int ID { get; set; } private void Update() { _fsm?.Update(); } public void ChangeState(TState state) { _fsm?.ChangeState(state); } public void RevertToPreviousState() { _fsm?.RevertToPreviousState(); } public virtual bool HandleMessage(Telegram msg) { return _fsm?.HandleMessage(msg) ?? false; } } public class FSMEntityManager : IFSMEntityManager { private Dictionary _entityMap = new Dictionary(); public FSMEntityManager() { } public void RegisterEntity(IFSMEntity entity) { if (entity != null) { _entityMap[entity.ID] = entity; } } public IFSMEntity GetEntityFromID(int id) { if (_entityMap.TryGetValue(id, out var entity)) { return entity; } return null; } public void RemoveEntity(IFSMEntity entity) { if (entity != null) { _entityMap.Remove(entity.ID); } } } ``` #### 设计思路 * 一个智能体要做的事情就是去拥有一个StateMachine类的实例,并且为了得到完全的FSM功能,实现一个方法来更新状态机。 * 在实体管理器中注册,注销,查找实体。 ### 一个简单的服务定位器 当然,你应该使用更加完善的服务定位器或依赖注入框架,而不是这个简易版。 单例的状态类,导致了状态内部不能存储与具体实例相关的数据,否则当多个实体共享同一个状态实例时,就会出现数据污染的问题。 ```Csharp public class SimpleServiceContainer { private static SimpleServiceContainer instance; public static SimpleServiceContainer Instance { get { if (instance == null) { lock (typeof(SimpleServiceContainer)) { if (instance == null) { instance = new SimpleServiceContainer(); } } } return instance; } } private readonly Dictionary> _services = new Dictionary>(); public void Register() where TImplementation : TService, new() { _services[typeof(TService)] = new Lazy(() => new TImplementation()); } public void Register() where TImplementation : new() { _services[typeof(TImplementation)] = new Lazy(() => new TImplementation()); } public T Get() where T : class { var key = typeof(T); if (_services.TryGetValue(key, out var retInstance)) { return retInstance.Value as T; } return null; } public void RegisterAllImplementations(params Assembly[] assemblies) { var serviceType = typeof(TService); var implementations = assemblies.SelectMany(a => a.GetTypes()) .Where(t => serviceType.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract && t.GetConstructors().Length > 0); foreach (var implementation in implementations) { _services[implementation] = new Lazy(() => Activator.CreateInstance(implementation)); } } // 自动注册泛型接口 IState 的实现类 public void AutoRegisterGenericInterfaces(params Assembly[] assemblies) { var types = assemblies.SelectMany(a => a.GetTypes()) .Where(t => t.GetInterfaces() .Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IState<>))); foreach (var type in types) { _services[type] = new Lazy(() => Activator.CreateInstance(type)); } } } public static class ServiceProvider { public static T Get() where T : class { return SimpleServiceContainer.Instance.Get(); } } ``` #### 设计思路 * Lazy:服务只在首次获取时才实例化,避免启动时的大量对象创建 * 双检锁单例模式:确保线程安全的同时保持高性能 * 自动注册机制:通过反射自动扫描并注册 IState 的实现