419 lines
13 KiB
C#
419 lines
13 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|
|
}
|