diff --git a/Assets/TEngine/Editor/Inspector/ProcedureModuleInspector.cs b/Assets/TEngine/Editor/Inspector/ProcedureModuleInspector.cs new file mode 100644 index 00000000..784295ed --- /dev/null +++ b/Assets/TEngine/Editor/Inspector/ProcedureModuleInspector.cs @@ -0,0 +1,163 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Editor.Inspector +{ + [CustomEditor(typeof(ProcedureModule))] + internal sealed class ProcedureModuleInspector : GameFrameworkInspector + { + private SerializedProperty m_AvailableProcedureTypeNames = null; + private SerializedProperty m_EntranceProcedureTypeName = null; + + private string[] m_ProcedureTypeNames = null; + private List m_CurrentAvailableProcedureTypeNames = null; + private int m_EntranceProcedureIndex = -1; + + public override void OnInspectorGUI() + { + base.OnInspectorGUI(); + + serializedObject.Update(); + + ProcedureModule t = (ProcedureModule)target; + + if (string.IsNullOrEmpty(m_EntranceProcedureTypeName.stringValue)) + { + EditorGUILayout.HelpBox("Entrance procedure is invalid.", MessageType.Error); + } + else if (EditorApplication.isPlaying) + { + EditorGUILayout.LabelField("Current Procedure", t.CurrentProcedure == null ? "None" : t.CurrentProcedure.GetType().ToString()); + } + + EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode); + { + GUILayout.Label("Available Procedures", EditorStyles.boldLabel); + if (m_ProcedureTypeNames.Length > 0) + { + EditorGUILayout.BeginVertical("box"); + { + foreach (string procedureTypeName in m_ProcedureTypeNames) + { + bool selected = m_CurrentAvailableProcedureTypeNames.Contains(procedureTypeName); + if (selected != EditorGUILayout.ToggleLeft(procedureTypeName, selected)) + { + if (!selected) + { + m_CurrentAvailableProcedureTypeNames.Add(procedureTypeName); + WriteAvailableProcedureTypeNames(); + } + else if (procedureTypeName != m_EntranceProcedureTypeName.stringValue) + { + m_CurrentAvailableProcedureTypeNames.Remove(procedureTypeName); + WriteAvailableProcedureTypeNames(); + } + } + } + } + EditorGUILayout.EndVertical(); + } + else + { + EditorGUILayout.HelpBox("There is no available procedure.", MessageType.Warning); + } + + if (m_CurrentAvailableProcedureTypeNames.Count > 0) + { + EditorGUILayout.Separator(); + + int selectedIndex = EditorGUILayout.Popup("Entrance Procedure", m_EntranceProcedureIndex, m_CurrentAvailableProcedureTypeNames.ToArray()); + if (selectedIndex != m_EntranceProcedureIndex) + { + m_EntranceProcedureIndex = selectedIndex; + m_EntranceProcedureTypeName.stringValue = m_CurrentAvailableProcedureTypeNames[selectedIndex]; + } + } + else + { + EditorGUILayout.HelpBox("Select available procedures first.", MessageType.Info); + } + } + EditorGUI.EndDisabledGroup(); + + serializedObject.ApplyModifiedProperties(); + + Repaint(); + } + + protected override void OnCompileComplete() + { + base.OnCompileComplete(); + + RefreshTypeNames(); + } + + private void OnEnable() + { + m_AvailableProcedureTypeNames = serializedObject.FindProperty("m_AvailableProcedureTypeNames"); + m_EntranceProcedureTypeName = serializedObject.FindProperty("m_EntranceProcedureTypeName"); + + RefreshTypeNames(); + } + + private void RefreshTypeNames() + { + m_ProcedureTypeNames = Type.GetRuntimeTypeNames(typeof(ProcedureBase)); + ReadAvailableProcedureTypeNames(); + int oldCount = m_CurrentAvailableProcedureTypeNames.Count; + m_CurrentAvailableProcedureTypeNames = m_CurrentAvailableProcedureTypeNames.Where(x => m_ProcedureTypeNames.Contains(x)).ToList(); + if (m_CurrentAvailableProcedureTypeNames.Count != oldCount) + { + WriteAvailableProcedureTypeNames(); + } + else if (!string.IsNullOrEmpty(m_EntranceProcedureTypeName.stringValue)) + { + m_EntranceProcedureIndex = m_CurrentAvailableProcedureTypeNames.IndexOf(m_EntranceProcedureTypeName.stringValue); + if (m_EntranceProcedureIndex < 0) + { + m_EntranceProcedureTypeName.stringValue = null; + } + } + + serializedObject.ApplyModifiedProperties(); + } + + private void ReadAvailableProcedureTypeNames() + { + m_CurrentAvailableProcedureTypeNames = new List(); + int count = m_AvailableProcedureTypeNames.arraySize; + for (int i = 0; i < count; i++) + { + m_CurrentAvailableProcedureTypeNames.Add(m_AvailableProcedureTypeNames.GetArrayElementAtIndex(i).stringValue); + } + } + + private void WriteAvailableProcedureTypeNames() + { + m_AvailableProcedureTypeNames.ClearArray(); + if (m_CurrentAvailableProcedureTypeNames == null) + { + return; + } + + m_CurrentAvailableProcedureTypeNames.Sort(); + int count = m_CurrentAvailableProcedureTypeNames.Count; + for (int i = 0; i < count; i++) + { + m_AvailableProcedureTypeNames.InsertArrayElementAtIndex(i); + m_AvailableProcedureTypeNames.GetArrayElementAtIndex(i).stringValue = m_CurrentAvailableProcedureTypeNames[i]; + } + + if (!string.IsNullOrEmpty(m_EntranceProcedureTypeName.stringValue)) + { + m_EntranceProcedureIndex = m_CurrentAvailableProcedureTypeNames.IndexOf(m_EntranceProcedureTypeName.stringValue); + if (m_EntranceProcedureIndex < 0) + { + m_EntranceProcedureTypeName.stringValue = null; + } + } + } + } +} diff --git a/Assets/TEngine/Editor/Inspector/ProcedureModuleInspector.cs.meta b/Assets/TEngine/Editor/Inspector/ProcedureModuleInspector.cs.meta new file mode 100644 index 00000000..8dd3a63f --- /dev/null +++ b/Assets/TEngine/Editor/Inspector/ProcedureModuleInspector.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f8c879b84e1b47bc87f3f0f8b547dfb2 +timeCreated: 1680663412 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Procedure.meta b/Assets/TEngine/Runtime/GameFramework/Procedure.meta new file mode 100644 index 00000000..2384463d --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Procedure.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ed454120d9944420baccd99a443c7fd8 +timeCreated: 1680663217 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Procedure/IProcedureManager.cs b/Assets/TEngine/Runtime/GameFramework/Procedure/IProcedureManager.cs new file mode 100644 index 00000000..1b7d5309 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Procedure/IProcedureManager.cs @@ -0,0 +1,73 @@ +using System; + +namespace TEngine +{ + /// + /// 流程管理器接口。 + /// + public interface IProcedureManager + { + /// + /// 获取当前流程。 + /// + ProcedureBase CurrentProcedure + { + get; + } + + /// + /// 获取当前流程持续时间。 + /// + float CurrentProcedureTime + { + get; + } + + /// + /// 初始化流程管理器。 + /// + /// 有限状态机管理器。 + /// 流程管理器包含的流程。 + void Initialize(IFsmManager fsmManager, params ProcedureBase[] procedures); + + /// + /// 开始流程。 + /// + /// 要开始的流程类型。 + void StartProcedure() where T : ProcedureBase; + + /// + /// 开始流程。 + /// + /// 要开始的流程类型。 + void StartProcedure(Type procedureType); + + /// + /// 是否存在流程。 + /// + /// 要检查的流程类型。 + /// 是否存在流程。 + bool HasProcedure() where T : ProcedureBase; + + /// + /// 是否存在流程。 + /// + /// 要检查的流程类型。 + /// 是否存在流程。 + bool HasProcedure(Type procedureType); + + /// + /// 获取流程。 + /// + /// 要获取的流程类型。 + /// 要获取的流程。 + ProcedureBase GetProcedure() where T : ProcedureBase; + + /// + /// 获取流程。 + /// + /// 要获取的流程类型。 + /// 要获取的流程。 + ProcedureBase GetProcedure(Type procedureType); + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Procedure/IProcedureManager.cs.meta b/Assets/TEngine/Runtime/GameFramework/Procedure/IProcedureManager.cs.meta new file mode 100644 index 00000000..ba9bac89 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Procedure/IProcedureManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5dd7530db77a4e9dac3f40e30aa05cb0 +timeCreated: 1680663217 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureBase.cs b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureBase.cs new file mode 100644 index 00000000..12f4b952 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureBase.cs @@ -0,0 +1,58 @@ +using ProcedureOwner = TEngine.IFsm; + +namespace TEngine +{ + /// + /// 流程基类。 + /// + public abstract class ProcedureBase : FsmState + { + /// + /// 状态初始化时调用。 + /// + /// 流程持有者。 + protected internal override void OnInit(ProcedureOwner procedureOwner) + { + base.OnInit(procedureOwner); + } + + /// + /// 进入状态时调用。 + /// + /// 流程持有者。 + protected internal override void OnEnter(ProcedureOwner procedureOwner) + { + base.OnEnter(procedureOwner); + } + + /// + /// 状态轮询时调用。 + /// + /// 流程持有者。 + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + protected internal override void OnUpdate(ProcedureOwner procedureOwner, float elapseSeconds, float realElapseSeconds) + { + base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds); + } + + /// + /// 离开状态时调用。 + /// + /// 流程持有者。 + /// 是否是关闭状态机时触发。 + protected internal override void OnLeave(ProcedureOwner procedureOwner, bool isShutdown) + { + base.OnLeave(procedureOwner, isShutdown); + } + + /// + /// 状态销毁时调用。 + /// + /// 流程持有者。 + protected internal override void OnDestroy(ProcedureOwner procedureOwner) + { + base.OnDestroy(procedureOwner); + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureBase.cs.meta b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureBase.cs.meta new file mode 100644 index 00000000..74dfb832 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureBase.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: eb8b31eea6d2468f8a717e780d44844f +timeCreated: 1680663217 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureManager.cs b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureManager.cs new file mode 100644 index 00000000..8d5121f2 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureManager.cs @@ -0,0 +1,193 @@ +using System; + +namespace TEngine +{ + /// + /// 流程管理器。 + /// + internal sealed class ProcedureManager : GameFrameworkModule, IProcedureManager + { + private IFsmManager m_FsmManager; + private IFsm m_ProcedureFsm; + + /// + /// 初始化流程管理器的新实例。 + /// + public ProcedureManager() + { + m_FsmManager = null; + m_ProcedureFsm = null; + } + + /// + /// 获取游戏框架模块优先级。 + /// + /// 优先级较高的模块会优先轮询,并且关闭操作会后进行。 + internal override int Priority + { + get { return -2; } + } + + /// + /// 获取当前流程。 + /// + public ProcedureBase CurrentProcedure + { + get + { + if (m_ProcedureFsm == null) + { + throw new GameFrameworkException("You must initialize procedure first."); + } + + return (ProcedureBase)m_ProcedureFsm.CurrentState; + } + } + + /// + /// 获取当前流程持续时间。 + /// + public float CurrentProcedureTime + { + get + { + if (m_ProcedureFsm == null) + { + throw new GameFrameworkException("You must initialize procedure first."); + } + + return m_ProcedureFsm.CurrentStateTime; + } + } + + /// + /// 流程管理器轮询。 + /// + /// 逻辑流逝时间,以秒为单位。 + /// 真实流逝时间,以秒为单位。 + internal override void Update(float elapseSeconds, float realElapseSeconds) + { + } + + /// + /// 关闭并清理流程管理器。 + /// + internal override void Shutdown() + { + if (m_FsmManager != null) + { + if (m_ProcedureFsm != null) + { + m_FsmManager.DestroyFsm(m_ProcedureFsm); + m_ProcedureFsm = null; + } + + m_FsmManager = null; + } + } + + /// + /// 初始化流程管理器。 + /// + /// 有限状态机管理器。 + /// 流程管理器包含的流程。 + public void Initialize(IFsmManager fsmManager, params ProcedureBase[] procedures) + { + if (fsmManager == null) + { + throw new GameFrameworkException("FSM manager is invalid."); + } + + m_FsmManager = fsmManager; + m_ProcedureFsm = m_FsmManager.CreateFsm(this, procedures); + } + + /// + /// 开始流程。 + /// + /// 要开始的流程类型。 + public void StartProcedure() where T : ProcedureBase + { + if (m_ProcedureFsm == null) + { + throw new GameFrameworkException("You must initialize procedure first."); + } + + m_ProcedureFsm.Start(); + } + + /// + /// 开始流程。 + /// + /// 要开始的流程类型。 + public void StartProcedure(Type procedureType) + { + if (m_ProcedureFsm == null) + { + throw new GameFrameworkException("You must initialize procedure first."); + } + + m_ProcedureFsm.Start(procedureType); + } + + /// + /// 是否存在流程。 + /// + /// 要检查的流程类型。 + /// 是否存在流程。 + public bool HasProcedure() where T : ProcedureBase + { + if (m_ProcedureFsm == null) + { + throw new GameFrameworkException("You must initialize procedure first."); + } + + return m_ProcedureFsm.HasState(); + } + + /// + /// 是否存在流程。 + /// + /// 要检查的流程类型。 + /// 是否存在流程。 + public bool HasProcedure(Type procedureType) + { + if (m_ProcedureFsm == null) + { + throw new GameFrameworkException("You must initialize procedure first."); + } + + return m_ProcedureFsm.HasState(procedureType); + } + + /// + /// 获取流程。 + /// + /// 要获取的流程类型。 + /// 要获取的流程。 + public ProcedureBase GetProcedure() where T : ProcedureBase + { + if (m_ProcedureFsm == null) + { + throw new GameFrameworkException("You must initialize procedure first."); + } + + return m_ProcedureFsm.GetState(); + } + + /// + /// 获取流程。 + /// + /// 要获取的流程类型。 + /// 要获取的流程。 + public ProcedureBase GetProcedure(Type procedureType) + { + if (m_ProcedureFsm == null) + { + throw new GameFrameworkException("You must initialize procedure first."); + } + + return (ProcedureBase)m_ProcedureFsm.GetState(procedureType); + } + } +} \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureManager.cs.meta b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureManager.cs.meta new file mode 100644 index 00000000..805e8d2c --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureManager.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: efb3019410a04559a5d3b39f9435d519 +timeCreated: 1680663217 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureModule.cs b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureModule.cs new file mode 100644 index 00000000..eb03b655 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureModule.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections; +using UnityEngine; + +namespace TEngine +{ + /// + /// 流程管理模块。 + /// + [DisallowMultipleComponent] + public sealed class ProcedureModule : GameFrameworkModuleBase + { + private IProcedureManager m_ProcedureManager = null; + private ProcedureBase m_EntranceProcedure = null; + + [SerializeField] + private string[] m_AvailableProcedureTypeNames = null; + + [SerializeField] + private string m_EntranceProcedureTypeName = null; + + /// + /// 获取当前流程。 + /// + public ProcedureBase CurrentProcedure + { + get + { + if (m_ProcedureManager == null) + { + return null; + } + return m_ProcedureManager.CurrentProcedure; + } + } + + /// + /// 获取当前流程持续时间。 + /// + public float CurrentProcedureTime + { + get + { + if (m_ProcedureManager == null) + { + return 0f; + } + return m_ProcedureManager.CurrentProcedureTime; + } + } + + /// + /// 游戏框架模块初始化。 + /// + protected override void Awake() + { + base.Awake(); + + m_ProcedureManager = GameFrameworkEntry.GetModule(); + if (m_ProcedureManager == null) + { + Log.Fatal("Procedure manager is invalid."); + } + } + + private IEnumerator Start() + { + ProcedureBase[] procedures = new ProcedureBase[m_AvailableProcedureTypeNames.Length]; + for (int i = 0; i < m_AvailableProcedureTypeNames.Length; i++) + { + Type procedureType = Utility.Assembly.GetType(m_AvailableProcedureTypeNames[i]); + if (procedureType == null) + { + Log.Error("Can not find procedure type '{0}'.", m_AvailableProcedureTypeNames[i]); + yield break; + } + + procedures[i] = (ProcedureBase)Activator.CreateInstance(procedureType); + if (procedures[i] == null) + { + Log.Error("Can not create procedure instance '{0}'.", m_AvailableProcedureTypeNames[i]); + yield break; + } + + if (m_EntranceProcedureTypeName == m_AvailableProcedureTypeNames[i]) + { + m_EntranceProcedure = procedures[i]; + } + } + + if (m_EntranceProcedure == null) + { + Log.Error("Entrance procedure is invalid."); + yield break; + } + + m_ProcedureManager.Initialize(GameFrameworkEntry.GetModule(), procedures); + + yield return new WaitForEndOfFrame(); + + m_ProcedureManager.StartProcedure(m_EntranceProcedure.GetType()); + } + + /// + /// 是否存在流程。 + /// + /// 要检查的流程类型。 + /// 是否存在流程。 + public bool HasProcedure() where T : ProcedureBase + { + return m_ProcedureManager.HasProcedure(); + } + + /// + /// 是否存在流程。 + /// + /// 要检查的流程类型。 + /// 是否存在流程。 + public bool HasProcedure(Type procedureType) + { + return m_ProcedureManager.HasProcedure(procedureType); + } + + /// + /// 获取流程。 + /// + /// 要获取的流程类型。 + /// 要获取的流程。 + public ProcedureBase GetProcedure() where T : ProcedureBase + { + return m_ProcedureManager.GetProcedure(); + } + + /// + /// 获取流程。 + /// + /// 要获取的流程类型。 + /// 要获取的流程。 + public ProcedureBase GetProcedure(Type procedureType) + { + return m_ProcedureManager.GetProcedure(procedureType); + } + } +} diff --git a/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureModule.cs.meta b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureModule.cs.meta new file mode 100644 index 00000000..d66a4619 --- /dev/null +++ b/Assets/TEngine/Runtime/GameFramework/Procedure/ProcedureModule.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1587cae710f84819b0056e98344f204d +timeCreated: 1680663317 \ No newline at end of file diff --git a/Assets/TEngine/Runtime/GameModule.cs b/Assets/TEngine/Runtime/GameModule.cs index 8c6f6b9e..4b6ec642 100644 --- a/Assets/TEngine/Runtime/GameModule.cs +++ b/Assets/TEngine/Runtime/GameModule.cs @@ -33,6 +33,11 @@ public class GameModule:MonoBehaviour /// 获取资源模块。 /// public static ResourceModule Resource { get; private set; } + + /// + /// 流程管理模块。 + /// + public static ProcedureModule Procedure { get; private set; } /// /// 获取配置模块。 @@ -56,6 +61,7 @@ public class GameModule:MonoBehaviour Fsm = Get(); ObjectPool = Get(); Resource = Get(); + Procedure = Get(); Setting = Get(); UI = Get(); }