[Opt]轻量化Procudure模块

This commit is contained in:
陈思海 2026-04-27 19:43:38 +08:00
parent 9afd5d9ff9
commit bbced819dc
10 changed files with 437 additions and 217 deletions

View File

@ -71,25 +71,6 @@ public static partial class GameApp
private static IObjectPoolService _objectPool; private static IObjectPoolService _objectPool;
/// <summary>
/// 获取有限状态机组件。
/// </summary>
public static IProcedureService Procedure
{
get
{
if (_procedure == null)
{
_procedure = AppServices.RequireApp<IProcedureService>();
}
return _procedure;
}
}
private static IProcedureService _procedure;
/// <summary> /// <summary>
/// 获取Asset组件。 /// 获取Asset组件。
/// </summary> /// </summary>

View File

@ -1,27 +0,0 @@
using System;
using System.Collections.Generic;
namespace AlicizaX
{
public interface IProcedureService : IService
{
Type CurrentProcedureType { get; }
void InitializeProcedure(IEnumerable<IProcedure> availableProcedures, Type defaultProcedureType);
void ClearAllProcedures();
bool ContainsProcedure(Type procedureType);
bool TrySwitchProcedure(Type procedureType);
}
public static class ProcedureServiceExtensions
{
public static bool SwitchProcedure<T>(this IProcedureService procedureService) where T : IProcedure
{
return procedureService.TrySwitchProcedure(typeof(T));
}
public static bool SwitchProcedure(this IProcedureService procedureService, Type procedureType)
{
return procedureService.TrySwitchProcedure(procedureType);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 18d5ca3d17384612b2d2023084b6a97b
timeCreated: 1763451617

View File

@ -1,72 +1,43 @@
namespace AlicizaX namespace AlicizaX
{ {
public interface IProcedure // public interface IProcedure
{ // {
IProcedureService ProcedureService { get; set; } // void Init();
void Init(); // void Enter();
void Enter(); // void Leave();
void Leave(); // void Update();
void Update(); // void Destroy();
void Destroy(); // }
}
/// <summary> /// <summary>
/// 流程基类 - 使用模板方法模式定义流程生命周期 /// 流程基类 - 使用模板方法模式定义流程生命周期
/// </summary> /// </summary>
public abstract class ProcedureBase : IProcedure public abstract class ProcedureBase
{ {
public IProcedureService ProcedureService { get; set; } protected internal virtual void OnInit()
void IProcedure.Init()
{
OnInit();
}
void IProcedure.Enter()
{
OnEnter();
}
void IProcedure.Leave()
{
OnLeave();
}
void IProcedure.Update()
{
OnUpdate();
}
void IProcedure.Destroy()
{
OnDestroy();
}
protected virtual void OnInit()
{ {
} }
protected virtual void OnEnter() protected internal virtual void OnEnter()
{ {
} }
protected virtual void OnLeave() protected internal virtual void OnLeave()
{ {
} }
protected virtual void OnUpdate() protected internal virtual void OnUpdate()
{ {
} }
protected virtual void OnDestroy() protected internal virtual void OnDestroy()
{ {
} }
protected internal void SwitchProcedure<T>() where T : ProcedureBase protected internal void SwitchProcedure<T>() where T : ProcedureBase
{ {
ProcedureService.SwitchProcedure<T>(); ProcedureBuilder.SwitchProcedure<T>();
} }
} }
} }

View File

@ -0,0 +1,418 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace AlicizaX
{
public static class ProcedureBuilder
{
private const string ProcedureRunnerName = "[ProcedureRunner]";
private static ProcedureRunner _runner;
public static Type CurrentProcedureType => _runner != null ? _runner.CurrentProcedureType : null;
public static bool IsRunning => _runner != null;
public static void InitializeProcedure(IEnumerable<ProcedureBase> availableProcedures, Type defaultProcedureType)
{
DestroyProcedure();
var gameObject = new GameObject(ProcedureRunnerName);
UnityEngine.Object.DontDestroyOnLoad(gameObject);
_runner = gameObject.AddComponent<ProcedureRunner>();
_runner.InitializeProcedure(availableProcedures, defaultProcedureType);
}
public static void InitializeProcedure(ProcedureBase[] availableProcedures, Type defaultProcedureType)
{
DestroyProcedure();
var gameObject = new GameObject(ProcedureRunnerName);
UnityEngine.Object.DontDestroyOnLoad(gameObject);
_runner = gameObject.AddComponent<ProcedureRunner>();
_runner.InitializeProcedure(availableProcedures, defaultProcedureType);
}
public static void InitializeProcedure(List<ProcedureBase> availableProcedures, Type defaultProcedureType)
{
DestroyProcedure();
var gameObject = new GameObject(ProcedureRunnerName);
UnityEngine.Object.DontDestroyOnLoad(gameObject);
_runner = gameObject.AddComponent<ProcedureRunner>();
_runner.InitializeProcedure(availableProcedures, defaultProcedureType);
}
public static bool SwitchProcedure<T>() where T : ProcedureBase
{
return _runner != null && _runner.TrySwitchProcedure(typeof(T));
}
public static bool SwitchProcedure(Type procedureType)
{
return _runner != null && _runner.TrySwitchProcedure(procedureType);
}
public static bool ContainsProcedure<T>() where T : ProcedureBase
{
return _runner != null && _runner.ContainsProcedure(typeof(T));
}
public static bool ContainsProcedure(Type procedureType)
{
return _runner != null && _runner.ContainsProcedure(procedureType);
}
public static void DestroyProcedure()
{
if (_runner == null)
{
return;
}
var gameObject = _runner.gameObject;
_runner.Shutdown();
_runner = null;
if (gameObject != null)
{
UnityEngine.Object.Destroy(gameObject);
}
}
private sealed class ProcedureRunner : MonoBehaviour
{
private const int MaxLoadFactorNumerator = 7;
private const int MaxLoadFactorDenominator = 10;
private Type[] _types;
private ProcedureBase[] _procedures;
private int[] _orderedIndices;
private ProcedureBase[] _orderedProcedures;
private int _procedureCount;
private int _bucketCount;
private ProcedureBase _currentProcedure;
private ProcedureBase _defaultProcedure;
private bool _isDestroying;
public Type CurrentProcedureType => _currentProcedure != null ? _currentProcedure.GetType() : null;
public void InitializeProcedure(IEnumerable<ProcedureBase> availableProcedures, Type defaultProcedureType)
{
ClearAllProcedures();
if (availableProcedures == null || defaultProcedureType == null)
{
return;
}
foreach (var procedure in availableProcedures)
{
AddProcedure(procedure);
}
if (TryGetProcedure(defaultProcedureType, out var defaultProcedure))
{
_defaultProcedure = defaultProcedure;
TrySwitchProcedure(defaultProcedureType);
}
}
public void InitializeProcedure(ProcedureBase[] availableProcedures, Type defaultProcedureType)
{
ClearAllProcedures();
if (availableProcedures == null || defaultProcedureType == null)
{
return;
}
EnsureCapacity(availableProcedures.Length);
for (var i = 0; i < availableProcedures.Length; i++)
{
AddProcedure(availableProcedures[i]);
}
if (TryGetProcedure(defaultProcedureType, out var defaultProcedure))
{
_defaultProcedure = defaultProcedure;
TrySwitchProcedure(defaultProcedureType);
}
}
public void InitializeProcedure(List<ProcedureBase> availableProcedures, Type defaultProcedureType)
{
ClearAllProcedures();
if (availableProcedures == null || defaultProcedureType == null)
{
return;
}
EnsureCapacity(availableProcedures.Count);
for (var i = 0; i < availableProcedures.Count; i++)
{
AddProcedure(availableProcedures[i]);
}
if (TryGetProcedure(defaultProcedureType, out var defaultProcedure))
{
_defaultProcedure = defaultProcedure;
TrySwitchProcedure(defaultProcedureType);
}
}
public void ClearAllProcedures()
{
if (_currentProcedure != null)
{
_currentProcedure.OnLeave();
_currentProcedure = null;
}
for (var i = 0; i < _procedureCount; i++)
{
var procedure = _orderedProcedures[i];
if (procedure == null)
{
continue;
}
procedure.OnDestroy();
_orderedProcedures[i] = null;
}
if (_types != null)
{
for (var i = 0; i < _types.Length; i++)
{
_types[i] = null;
_procedures[i] = null;
}
}
_procedureCount = 0;
_bucketCount = 0;
_defaultProcedure = null;
}
public bool ContainsProcedure(Type procedureType)
{
return TryGetProcedure(procedureType, out _);
}
public bool TrySwitchProcedure(Type procedureType)
{
if (procedureType == null || _isDestroying)
{
return false;
}
if (!TryGetProcedure(procedureType, out var nextProcedure))
{
nextProcedure = _defaultProcedure;
}
if (nextProcedure == null)
{
return false;
}
if (ReferenceEquals(_currentProcedure, nextProcedure))
{
return true;
}
var previousProcedure = _currentProcedure;
_currentProcedure = nextProcedure;
previousProcedure?.OnLeave();
nextProcedure.OnEnter();
return true;
}
public void Shutdown()
{
_isDestroying = true;
ClearAllProcedures();
}
private void AddProcedure(ProcedureBase procedure)
{
if (procedure == null)
{
return;
}
var type = procedure.GetType();
EnsureCapacity(_procedureCount + 1);
var index = FindIndex(type);
if (index >= 0)
{
var previousProcedure = _procedures[index];
if (previousProcedure != null)
{
if (ReferenceEquals(_currentProcedure, previousProcedure))
{
_currentProcedure = null;
}
if (ReferenceEquals(_defaultProcedure, previousProcedure))
{
_defaultProcedure = procedure;
}
previousProcedure.OnDestroy();
}
_procedures[index] = procedure;
_orderedProcedures[_orderedIndices[index]] = procedure;
procedure.OnInit();
return;
}
var insertIndex = FindInsertIndex(type);
_types[insertIndex] = type;
_procedures[insertIndex] = procedure;
_orderedIndices[insertIndex] = _procedureCount;
_orderedProcedures[_procedureCount] = procedure;
_procedureCount++;
_bucketCount++;
procedure.OnInit();
}
private bool TryGetProcedure(Type procedureType, out ProcedureBase procedure)
{
var index = FindIndex(procedureType);
if (index >= 0)
{
procedure = _procedures[index];
return true;
}
procedure = null;
return false;
}
private int FindIndex(Type procedureType)
{
if (procedureType == null || _types == null)
{
return -1;
}
var mask = _types.Length - 1;
var index = procedureType.GetHashCode() & mask;
for (var i = 0; i < _types.Length; i++)
{
var type = _types[index];
if (type == null)
{
return -1;
}
if (type == procedureType)
{
return index;
}
index = (index + 1) & mask;
}
return -1;
}
private int FindInsertIndex(Type procedureType)
{
var mask = _types.Length - 1;
var index = procedureType.GetHashCode() & mask;
while (_types[index] != null)
{
index = (index + 1) & mask;
}
return index;
}
private void EnsureCapacity(int targetCapacity)
{
if (_types != null && _orderedProcedures.Length >= targetCapacity && (_bucketCount + 1) * MaxLoadFactorDenominator < _types.Length * MaxLoadFactorNumerator)
{
return;
}
var newProcedureCapacity = _orderedProcedures == null ? 4 : _orderedProcedures.Length;
while (newProcedureCapacity < targetCapacity)
{
newProcedureCapacity <<= 1;
}
var newBucketCapacity = _types == null ? 8 : _types.Length;
while (targetCapacity * MaxLoadFactorDenominator >= newBucketCapacity * MaxLoadFactorNumerator)
{
newBucketCapacity <<= 1;
}
var newTypes = new Type[newBucketCapacity];
var newProcedures = new ProcedureBase[newBucketCapacity];
var newOrderedIndices = new int[newBucketCapacity];
var newOrderedProcedures = new ProcedureBase[newProcedureCapacity];
for (var i = 0; i < _procedureCount; i++)
{
var procedure = _orderedProcedures[i];
var type = procedure.GetType();
var insertIndex = FindInsertIndex(newTypes, type);
newTypes[insertIndex] = type;
newProcedures[insertIndex] = procedure;
newOrderedIndices[insertIndex] = i;
newOrderedProcedures[i] = procedure;
}
_types = newTypes;
_procedures = newProcedures;
_orderedIndices = newOrderedIndices;
_orderedProcedures = newOrderedProcedures;
_bucketCount = _procedureCount;
}
private static int FindInsertIndex(Type[] types, Type procedureType)
{
var mask = types.Length - 1;
var index = procedureType.GetHashCode() & mask;
while (types[index] != null)
{
index = (index + 1) & mask;
}
return index;
}
private void Update()
{
_currentProcedure?.OnUpdate();
}
private void OnDestroy()
{
if (_runner == this)
{
_runner = null;
}
Shutdown();
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6c4b9af55d3c4f53a8d9e58a45d8a2b1
timeCreated: 1777305600

View File

@ -1,15 +0,0 @@
using UnityEngine;
namespace AlicizaX
{
[DisallowMultipleComponent]
[AddComponentMenu("Game Framework/Procedure")]
public sealed class ProcedureComponent : MonoBehaviour
{
private void Awake()
{
AppServices.RegisterApp(new ProcedureService());
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: c0a419f3af614cdda5267671f803dfdb
timeCreated: 1763451994

View File

@ -1,102 +0,0 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace AlicizaX
{
internal class ProcedureService : ServiceBase, IProcedureService, IServiceTickable
{
private readonly Dictionary<Type, IProcedure> _procedures = new Dictionary<Type, IProcedure>();
private IProcedure _currentProcedure;
private IProcedure _defaultProcedure;
public Type CurrentProcedureType => _currentProcedure?.GetType();
public void InitializeProcedure(IEnumerable<IProcedure> availableProcedures, Type defaultProcedureType)
{
_procedures.Clear();
foreach (var procedure in availableProcedures)
{
if (procedure == null)
{
continue;
}
var type = procedure.GetType();
_procedures[type] = procedure;
procedure.ProcedureService = this;
procedure.Init();
}
if (_procedures.TryGetValue(defaultProcedureType, out var defaultProcedure))
{
_defaultProcedure = defaultProcedure;
TrySwitchProcedure(defaultProcedureType);
}
else
{
Log.Info($"榛樿娴佺▼ {defaultProcedureType.Name} 鏈敞鍐?");
}
}
public void ClearAllProcedures()
{
foreach (var procedure in _procedures.Values)
{
procedure.Destroy();
}
_procedures.Clear();
_currentProcedure = null;
_defaultProcedure = null;
}
public bool ContainsProcedure(Type procedureType)
{
return procedureType != null && _procedures.ContainsKey(procedureType);
}
public bool TrySwitchProcedure(Type procedureType)
{
if (procedureType == null)
{
return false;
}
if (!_procedures.TryGetValue(procedureType, out var nextProcedure))
{
if (_defaultProcedure == null)
{
return false;
}
Debug.LogWarning($"娴佺▼ {procedureType.Name} 涓嶅瓨鍦紝鍒囨崲鍒伴粯璁ゆ祦绋?");
nextProcedure = _defaultProcedure;
}
if (ReferenceEquals(_currentProcedure, nextProcedure))
{
return true;
}
_currentProcedure?.Leave();
nextProcedure.Enter();
_currentProcedure = nextProcedure;
return true;
}
protected override void OnInitialize()
{
}
protected override void OnDestroyService()
{
ClearAllProcedures();
}
void IServiceTickable.Tick(float deltaTime)
{
_currentProcedure?.Update();
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 4272c5c31b6b4a459f1da37a228cfd3f
timeCreated: 1763450372