diff --git a/GFramework.Core.Abstractions/state/IState.cs b/GFramework.Core.Abstractions/state/IState.cs new file mode 100644 index 0000000..8159804 --- /dev/null +++ b/GFramework.Core.Abstractions/state/IState.cs @@ -0,0 +1,26 @@ +namespace GFramework.Core.Abstractions.state; + +/// +/// 状态机状态接口,定义了状态的基本行为和转换规则 +/// +public interface IState +{ + /// + /// 当状态被激活进入时调用 + /// + /// 从哪个状态转换而来,可能为null表示初始状态 + void OnEnter(IState? from); + + /// + /// 当状态退出时调用 + /// + /// 将要转换到的目标状态,可能为null表示结束状态 + void OnExit(IState? to); + + /// + /// 判断当前状态是否可以转换到目标状态 + /// + /// 目标状态 + /// 如果可以转换则返回true,否则返回false + bool CanTransitionTo(IState target); +} \ No newline at end of file diff --git a/GFramework.Core.Abstractions/state/IStateMachine.cs b/GFramework.Core.Abstractions/state/IStateMachine.cs new file mode 100644 index 0000000..f0b6d61 --- /dev/null +++ b/GFramework.Core.Abstractions/state/IStateMachine.cs @@ -0,0 +1,37 @@ +namespace GFramework.Core.Abstractions.state; + +/// +/// 状态机接口,用于管理状态的注册、切换和验证 +/// +public interface IStateMachine +{ + /// + /// 获取当前激活的状态 + /// + IState? Current { get; } + + /// + /// 注册一个状态到状态机中 + /// + /// 要注册的状态实例 + void Register(IState state); + + /// + /// 从状态机中注销指定类型的状态 + /// + /// 要注销的状态类型,必须实现IState接口 + void Unregister() where T : IState; + + /// + /// 检查是否可以切换到指定类型的状态 + /// + /// 目标状态类型,必须实现IState接口 + /// 如果可以切换则返回true,否则返回false + bool CanChangeTo() where T : IState; + + /// + /// 切换到指定类型的状态 + /// + /// 要切换到的状态类型,必须实现IState接口 + void ChangeTo() where T : IState; +} \ No newline at end of file diff --git a/GFramework.Core/state/ContextAwareStateMachine.cs b/GFramework.Core/state/ContextAwareStateMachine.cs new file mode 100644 index 0000000..606cc0b --- /dev/null +++ b/GFramework.Core/state/ContextAwareStateMachine.cs @@ -0,0 +1,82 @@ +using GFramework.Core.Abstractions.architecture; +using GFramework.Core.Abstractions.enums; +using GFramework.Core.Abstractions.rule; +using GFramework.Core.Abstractions.state; +using GFramework.Core.Abstractions.system; +using GFramework.Core.extensions; + +namespace GFramework.Core.state; + +/// +/// 上下文感知状态机,继承自StateMachine并实现ISystem接口 +/// 该状态机能够感知架构上下文,并在状态切换时发送状态变更事件 +/// +public class ContextAwareStateMachine : StateMachine, ISystem +{ + /// + /// 架构上下文对象,用于提供系统运行所需的上下文信息 + /// + protected IArchitectureContext Context = null!; + + /// + /// 设置架构上下文的方法 + /// + /// 要设置的架构上下文对象 + public void SetContext(IArchitectureContext context) + { + Context = context; + } + + /// + /// 获取当前架构上下文的方法 + /// + /// 当前的架构上下文对象 + public IArchitectureContext GetContext() + { + return Context; + } + + /// + /// 处理架构生命周期阶段的方法 + /// + /// 当前所处的架构生命周期阶段 + public virtual void OnArchitecturePhase(ArchitecturePhase phase) + { + } + + /// + /// 初始化方法,在系统启动时调用 + /// 遍历所有状态实例,为实现了IContextAware接口的状态设置上下文 + /// + public virtual void Init() + { + foreach (var state in States.Values.OfType()) + { + state.SetContext(Context); + } + } + + /// + /// 销毁方法,在系统关闭时调用 + /// + public virtual void Destroy() + { + } + + /// + /// 内部状态切换方法,重写基类方法以添加状态变更事件通知功能 + /// + /// 要切换到的下一个状态 + protected override void ChangeInternal(IState next) + { + var old = Current; + base.ChangeInternal(next); + + // 发送状态变更事件,通知监听者状态已发生改变 + this.SendEvent(new StateChangedEvent + { + OldState = old, + NewState = Current + }); + } +} \ No newline at end of file diff --git a/GFramework.Core/state/StateChangedEvent.cs b/GFramework.Core/state/StateChangedEvent.cs new file mode 100644 index 0000000..3918874 --- /dev/null +++ b/GFramework.Core/state/StateChangedEvent.cs @@ -0,0 +1,19 @@ +using GFramework.Core.Abstractions.state; + +namespace GFramework.Core.state; + +/// +/// 表示状态变更事件的数据类 +/// +public sealed class StateChangedEvent +{ + /// + /// 获取变更前的旧状态 + /// + public IState? OldState { get; init; } + + /// + /// 获取变更后的新状态 + /// + public IState? NewState { get; init; } +} \ No newline at end of file diff --git a/GFramework.Core/state/StateMachine.cs b/GFramework.Core/state/StateMachine.cs new file mode 100644 index 0000000..c845340 --- /dev/null +++ b/GFramework.Core/state/StateMachine.cs @@ -0,0 +1,87 @@ +using GFramework.Core.Abstractions.state; + +namespace GFramework.Core.state; + +/// +/// 状态机实现类,用于管理状态的注册、切换和生命周期 +/// +public class StateMachine : IStateMachine +{ + /// + /// 存储所有已注册状态的字典,键为状态类型,值为状态实例 + /// + protected readonly Dictionary States = new(); + + /// + /// 获取当前激活的状态 + /// + public IState? Current { get; private set; } + + /// + /// 注册一个状态到状态机中 + /// + /// 要注册的状态实例 + public void Register(IState state) + => States[state.GetType()] = state; + + /// + /// 从状态机中注销指定类型的状态 + /// + /// 要注销的状态类型 + public void Unregister() where T : IState + { + var type = typeof(T); + if (!States.TryGetValue(type, out var state)) return; + + // 如果当前状态是要注销的状态,则先执行退出逻辑 + if (Current == state) + { + Current.OnExit(null); + Current = null; + } + + States.Remove(type); + } + + /// + /// 检查是否可以切换到指定类型的状态 + /// + /// 目标状态类型 + /// 如果可以切换则返回true,否则返回false + public bool CanChangeTo() where T : IState + { + if (!States.TryGetValue(typeof(T), out var target)) + return false; + + return Current?.CanTransitionTo(target) ?? true; + } + + /// + /// 切换到指定类型的状态 + /// + /// 目标状态类型 + /// 当目标状态未注册时抛出 + public void ChangeTo() where T : IState + { + if (!States.TryGetValue(typeof(T), out var target)) + throw new InvalidOperationException("State not registered."); + + ChangeInternal(target); + } + + /// + /// 内部状态切换方法,处理状态切换的核心逻辑 + /// + /// 下一个状态实例 + protected virtual void ChangeInternal(IState next) + { + if (Current == next) return; + if (Current != null && !Current.CanTransitionTo(next)) return; + + var old = Current; + old?.OnExit(next); + + Current = next; + Current.OnEnter(old); + } +} \ No newline at end of file diff --git a/GFramework.Game/state/GameStateMachine.cs b/GFramework.Game/state/GameStateMachine.cs new file mode 100644 index 0000000..64298e7 --- /dev/null +++ b/GFramework.Game/state/GameStateMachine.cs @@ -0,0 +1,26 @@ +using GFramework.Core.Abstractions.state; +using GFramework.Core.state; + +namespace GFramework.Game.state; + +/// +/// 游戏状态机类,继承自ContextAwareStateMachine,用于管理游戏中的各种状态 +/// +public sealed class GameStateMachine : ContextAwareStateMachine +{ + /// + /// 检查当前状态是否为指定类型的状态 + /// + /// 要检查的状态类型,必须实现IState接口 + /// 如果当前状态是指定类型则返回true,否则返回false + public bool IsIn() where T : IState + => Current is T; + + /// + /// 获取当前状态的实例,如果当前状态是指定类型则进行类型转换 + /// + /// 要获取的状态类型,必须是引用类型并实现IState接口 + /// 如果当前状态是指定类型则返回转换后的实例,否则返回null + public T? Get() where T : class, IState + => Current as T; +} \ No newline at end of file