修改合并

This commit is contained in:
陈思海 2025-10-11 15:18:09 +08:00
parent 8f5a88e1f5
commit 8fe1167292
306 changed files with 24932 additions and 33 deletions

View File

@ -2,11 +2,9 @@
"name": "AlicizaX.Framework.Editor",
"rootNamespace": "AlicizaX.Framework.Editor",
"references": [
"GUID:acfef7cabed3b0a42b25edb1cd4fa259",
"GUID:1619e00706139ce488ff80c0daeea8e7",
"GUID:e34a5702dd353724aa315fb8011f08c3",
"GUID:4d1926c9df5b052469a1c63448b7609a",
"GUID:75b6f2078d190f14dbda4a5b747d709c"
"GUID:4d1926c9df5b052469a1c63448b7609a"
],
"includePlatforms": [
"Editor"

3
Editor/Inspector.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6a6566b690eb46c3acc8685a8ccabfeb
timeCreated: 1737362727

View File

@ -0,0 +1,58 @@
using UnityEditor;
namespace AlicizaX.Editor
{
/// <summary>
/// 游戏框架 Inspector 抽象类。
/// </summary>
public abstract class GameFrameworkInspector : UnityEditor.Editor
{
protected const string NoneOptionName = "<None>";
private bool m_IsCompiling = false;
/// <summary>
/// 绘制事件。
/// </summary>
public override void OnInspectorGUI()
{
if (m_IsCompiling && !EditorApplication.isCompiling)
{
m_IsCompiling = false;
OnCompileComplete();
}
else if (!m_IsCompiling && EditorApplication.isCompiling)
{
m_IsCompiling = true;
OnCompileStart();
}
}
/// <summary>
/// 编译开始事件。
/// </summary>
protected virtual void OnCompileStart()
{
}
/// <summary>
/// 编译完成事件。
/// </summary>
protected virtual void OnCompileComplete()
{
}
protected bool IsPrefabInHierarchy(UnityEngine.Object obj)
{
if (obj == null)
{
return false;
}
#if UNITY_2018_3_OR_NEWER
return PrefabUtility.GetPrefabAssetType(obj) != PrefabAssetType.Regular;
#else
return PrefabUtility.GetPrefabType(obj) != PrefabType.Prefab;
#endif
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b5bb373d9bcd4bd45861670fa5208e05
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,27 @@
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEditor;
namespace AlicizaX.Editor
{
public class InspectorEditor<T> : UnityEditor.Editor where T : Object
{
public T Target { get; private set; }
public PropertyCollection Properties { get; private set; }
public virtual void OnEnable()
{
Target = target as T;
Properties = EditorDrawing.GetAllProperties(serializedObject);
}
public override void OnInspectorGUI()
{
string name = Target.GetType().Name;
string spacedName = Regex.Replace(name, "(\\B[A-Z])", " $1");
EditorDrawing.DrawInspectorHeader(new GUIContent(spacedName), Target);
EditorGUILayout.Space();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ecc870911d5f4da592d757183a315b49
timeCreated: 1758265131

View File

@ -0,0 +1,150 @@
using AlicizaX;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
[CustomEditor(typeof(MemoryPoolSetting))]
internal sealed class MemoryPoolComponentInspector : GameFrameworkInspector
{
private readonly Dictionary<string, List<MemoryPoolInfo>> m_ReferencePoolInfos = new Dictionary<string, List<MemoryPoolInfo>>(StringComparer.Ordinal);
private readonly HashSet<string> m_OpenedItems = new HashSet<string>();
private SerializedProperty m_EnableStrictCheck = null;
private bool m_ShowFullClassName = false;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
MemoryPoolSetting t = (MemoryPoolSetting)target;
if (EditorApplication.isPlaying && IsPrefabInHierarchy(t.gameObject))
{
bool enableStrictCheck = EditorGUILayout.Toggle("Enable Strict Check", t.EnableStrictCheck);
if (enableStrictCheck != t.EnableStrictCheck)
{
t.EnableStrictCheck = enableStrictCheck;
}
EditorGUILayout.LabelField("Reference Pool Count", MemoryPool.Count.ToString());
m_ShowFullClassName = EditorGUILayout.Toggle("Show Full Class Name", m_ShowFullClassName);
m_ReferencePoolInfos.Clear();
MemoryPoolInfo[] referencePoolInfos = MemoryPool.GetAllMemoryPoolInfos();
foreach (MemoryPoolInfo referencePoolInfo in referencePoolInfos)
{
string assemblyName = referencePoolInfo.Type.Assembly.GetName().Name;
List<MemoryPoolInfo> results = null;
if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out results))
{
results = new List<MemoryPoolInfo>();
m_ReferencePoolInfos.Add(assemblyName, results);
}
results.Add(referencePoolInfo);
}
foreach (KeyValuePair<string, List<MemoryPoolInfo>> assemblyReferencePoolInfo in m_ReferencePoolInfos)
{
bool lastState = m_OpenedItems.Contains(assemblyReferencePoolInfo.Key);
bool currentState = EditorGUILayout.Foldout(lastState, assemblyReferencePoolInfo.Key);
if (currentState != lastState)
{
if (currentState)
{
m_OpenedItems.Add(assemblyReferencePoolInfo.Key);
}
else
{
m_OpenedItems.Remove(assemblyReferencePoolInfo.Key);
}
}
if (currentState)
{
EditorGUILayout.BeginVertical("box");
{
var label = "Unused\tUsing.\tAcquire\tRelease\tAdd\tRemove";
EditorGUILayout.LabelField(m_ShowFullClassName ? "Full Class Name" : "Class Name", label);
assemblyReferencePoolInfo.Value.Sort(Comparison);
foreach (MemoryPoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value)
{
DrawReferencePoolInfo(referencePoolInfo);
}
if (GUILayout.Button("Export CSV Data"))
{
string exportFileName = EditorUtility.SaveFilePanel("Export CSV Data", string.Empty, Utility.Text.Format("Reference Pool Data - {0}.csv", assemblyReferencePoolInfo.Key), string.Empty);
if (!string.IsNullOrEmpty(exportFileName))
{
try
{
int index = 0;
string[] data = new string[assemblyReferencePoolInfo.Value.Count + 1];
data[index++] = "Class Name,Full Class Name,Unused,Using,Acquire,Release,Add,Remove";
foreach (MemoryPoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value)
{
data[index++] = Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7}", referencePoolInfo.Type.Name, referencePoolInfo.Type.FullName, referencePoolInfo.UnusedMemoryCount, referencePoolInfo.UsingMemoryCount, referencePoolInfo.AcquireMemoryCount, referencePoolInfo.ReleaseMemoryCount, referencePoolInfo.AddMemoryCount, referencePoolInfo.RemoveMemoryCount);
}
File.WriteAllLines(exportFileName, data, Encoding.UTF8);
Debug.Log(Utility.Text.Format("Export reference pool CSV data to '{0}' success.", exportFileName));
}
catch (Exception exception)
{
Debug.LogError(Utility.Text.Format("Export reference pool CSV data to '{0}' failure, exception is '{1}'.", exportFileName, exception));
}
}
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
}
}
}
else
{
EditorGUILayout.PropertyField(m_EnableStrictCheck);
}
serializedObject.ApplyModifiedProperties();
Repaint();
}
private void OnEnable()
{
m_EnableStrictCheck = serializedObject.FindProperty("m_EnableStrictCheck");
}
private void DrawReferencePoolInfo(MemoryPoolInfo referencePoolInfo)
{
#if UNITY_6000_0_OR_NEWER
EditorGUILayout.LabelField(m_ShowFullClassName ? referencePoolInfo.Type.FullName : referencePoolInfo.Type.Name, Utility.Text.Format("{0,-12}\t{1,-12}\t{2,-12}\t{3,-12}\t{4}\t{5}", referencePoolInfo.UnusedMemoryCount, referencePoolInfo.UsingMemoryCount, referencePoolInfo.AcquireMemoryCount, referencePoolInfo.ReleaseMemoryCount, referencePoolInfo.AddMemoryCount, referencePoolInfo.RemoveMemoryCount));
#else
EditorGUILayout.LabelField(m_ShowFullClassName ? referencePoolInfo.Type.FullName :
referencePoolInfo.Type.Name, Utility.Text.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}",referencePoolInfo.UnusedMemoryCount, referencePoolInfo.UsingMemoryCount, referencePoolInfo.AcquireMemoryCount, referencePoolInfo.ReleaseMemoryCount, referencePoolInfo.AddMemoryCount, referencePoolInfo.RemoveMemoryCount));
#endif
}
private int Comparison(MemoryPoolInfo a, MemoryPoolInfo b)
{
if (m_ShowFullClassName)
{
return a.Type.FullName.CompareTo(b.Type.FullName);
}
else
{
return a.Type.Name.CompareTo(b.Type.Name);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 705a7441224da3d4cbc6593bdc58f167
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using AlicizaX;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
[CustomEditor(typeof(RootModule))]
internal sealed class RootModuleInspector : GameFrameworkInspector
{
private static readonly float[] GameSpeed = new float[] { 0f, 0.01f, 0.1f, 0.25f, 0.5f, 1f, 1.5f, 2f, 4f, 8f };
private static readonly string[] GameSpeedForDisplay = new string[] { "0x", "0.01x", "0.1x", "0.25x", "0.5x", "1x", "1.5x", "2x", "4x", "8x" };
private SerializedProperty _frameRate = null;
private SerializedProperty _gameSpeed = null;
private SerializedProperty _runInBackground = null;
private SerializedProperty _neverSleep = null;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
RootModule t = (RootModule)target;
EditorGUILayout.BeginVertical("box");
{
int frameRate = EditorGUILayout.IntSlider("Frame Rate", _frameRate.intValue, 1, 120);
if (frameRate != _frameRate.intValue)
{
if (EditorApplication.isPlaying)
{
t.FrameRate = frameRate;
}
else
{
_frameRate.intValue = frameRate;
}
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical("box");
{
float gameSpeed = EditorGUILayout.Slider("Game Speed", _gameSpeed.floatValue, 0f, 8f);
int selectedGameSpeed = GUILayout.SelectionGrid(GetSelectedGameSpeed(gameSpeed), GameSpeedForDisplay, 5);
if (selectedGameSpeed >= 0)
{
gameSpeed = GetGameSpeed(selectedGameSpeed);
}
if (Math.Abs(gameSpeed - _gameSpeed.floatValue) > 0.01f)
{
if (EditorApplication.isPlaying)
{
t.GameSpeed = gameSpeed;
}
else
{
_gameSpeed.floatValue = gameSpeed;
}
}
}
EditorGUILayout.EndVertical();
bool runInBackground = EditorGUILayout.Toggle("Run in Background", _runInBackground.boolValue);
if (runInBackground != _runInBackground.boolValue)
{
if (EditorApplication.isPlaying)
{
t.RunInBackground = runInBackground;
}
else
{
_runInBackground.boolValue = runInBackground;
}
}
bool neverSleep = EditorGUILayout.Toggle("Never Sleep", _neverSleep.boolValue);
if (neverSleep != _neverSleep.boolValue)
{
if (EditorApplication.isPlaying)
{
t.NeverSleep = neverSleep;
}
else
{
_neverSleep.boolValue = neverSleep;
}
}
serializedObject.ApplyModifiedProperties();
}
private void OnEnable()
{
_frameRate = serializedObject.FindProperty("frameRate");
_gameSpeed = serializedObject.FindProperty("gameSpeed");
_runInBackground = serializedObject.FindProperty("runInBackground");
_neverSleep = serializedObject.FindProperty("neverSleep");
}
private float GetGameSpeed(int selectedGameSpeed)
{
if (selectedGameSpeed < 0)
{
return GameSpeed[0];
}
if (selectedGameSpeed >= GameSpeed.Length)
{
return GameSpeed[GameSpeed.Length - 1];
}
return GameSpeed[selectedGameSpeed];
}
private int GetSelectedGameSpeed(float gameSpeed)
{
for (int i = 0; i < GameSpeed.Length; i++)
{
if (Mathf.Approximately(gameSpeed, GameSpeed[i]))
{
return i;
}
}
return -1;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55195676d06c1cd418e6f3201eae7176
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,52 @@
using System;
using System.Linq;
using AlicizaX.Editor;
using AlicizaX.Localization.Runtime;
using UnityEditor;
namespace AlicizaX.Localization.Editor
{
[CustomEditor(typeof(LocalizationComponent))]
internal sealed class LocalizationComponentInspector : GameFrameworkInspector
{
private string[] _languageNames = new[] { "None" };
private SerializedProperty _language;
private int languageIndex = -1;
private void OnEnable()
{
_language = serializedObject.FindProperty("_language");
_languageNames = LocalizationConfiguration.Instance.LanguageTypeNames.ToArray();
var languageName = EditorPrefs.GetString(LocalizationComponent.PrefsKey, "None");
_language.stringValue = languageName;
serializedObject.ApplyModifiedProperties();
languageIndex = _languageNames.ToList().FindIndex(t => t.Equals(languageName));
}
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
LocalizationComponent t = (LocalizationComponent)target;
EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
{
if (EditorApplication.isPlaying && IsPrefabInHierarchy(t.gameObject))
{
int index = UnityEditor.EditorPrefs.GetInt(LocalizationComponent.PrefsKey, 0);
int selectedIndex = EditorGUILayout.Popup("Language", index, _languageNames);
}
else
{
int selectedIndex = EditorGUILayout.Popup("Play Mode", languageIndex, _languageNames);
if (selectedIndex != languageIndex)
{
languageIndex = selectedIndex;
_language.stringValue = _languageNames[languageIndex];
EditorPrefs.SetString(LocalizationComponent.PrefsKey, _language.stringValue);
}
}
}
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 93a2512d8bf54c0a8032fe6f4a7eb52b
timeCreated: 1760164418

3
Editor/Misc.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c6d5d730968b49849be897d89a8b9049
timeCreated: 1736410513

View File

@ -0,0 +1,46 @@
using AlicizaX.Editor;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
internal static class EventScriptingDefineSymbols
{
private const string MenuPath = "Tools/AlicizaX/Event/Strict Check";
private const string DefineSymbol = "Event_StrictCheck";
[MenuItem(MenuPath)]
private static void ToggleStrictCheck()
{
bool enabled = IsEnabled();
SetEnabled(!enabled);
}
[MenuItem(MenuPath, true)]
private static bool ToggleStrictCheckValidate()
{
Menu.SetChecked(MenuPath, IsEnabled());
return true;
}
private static bool IsEnabled()
{
return ScriptingDefineSymbols.HasScriptingDefineSymbol(EditorUserBuildSettings.selectedBuildTargetGroup, DefineSymbol);
}
private static void SetEnabled(bool enabled)
{
var targetGroup = EditorUserBuildSettings.selectedBuildTargetGroup;
if (enabled)
{
ScriptingDefineSymbols.AddScriptingDefineSymbol(targetGroup, DefineSymbol);
}
else
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(targetGroup, DefineSymbol);
}
Debug.Log($"[EventKit] Strict Check {(enabled ? "Enabled" : "Disabled")}");
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a0f666b24d9c41c4a355f9a9a643928b
timeCreated: 1756782190

View File

@ -0,0 +1,172 @@
using UnityEditor;
namespace AlicizaX.Editor
{
/// <summary>
/// 日志脚本宏定义。
/// </summary>
public static class LogScriptingDefineSymbols
{
private const string EnableLogScriptingDefineSymbol = "ENABLE_LOG";
private const string EnableDebugAndAboveLogScriptingDefineSymbol = "ENABLE_DEBUG_AND_ABOVE_LOG";
private const string EnableInfoAndAboveLogScriptingDefineSymbol = "ENABLE_INFO_AND_ABOVE_LOG";
private const string EnableWarningAndAboveLogScriptingDefineSymbol = "ENABLE_WARNING_AND_ABOVE_LOG";
private const string EnableErrorAndAboveLogScriptingDefineSymbol = "ENABLE_ERROR_AND_ABOVE_LOG";
private const string EnableFatalAndAboveLogScriptingDefineSymbol = "ENABLE_FATAL_AND_ABOVE_LOG";
private const string EnableDebugLogScriptingDefineSymbol = "ENABLE_DEBUG_LOG";
private const string EnableInfoLogScriptingDefineSymbol = "ENABLE_INFO_LOG";
private const string EnableWarningLogScriptingDefineSymbol = "ENABLE_WARNING_LOG";
private const string EnableErrorLogScriptingDefineSymbol = "ENABLE_ERROR_LOG";
private const string EnableFatalLogScriptingDefineSymbol = "ENABLE_FATAL_LOG";
private static readonly string[] AboveLogScriptingDefineSymbols = new string[]
{
EnableDebugAndAboveLogScriptingDefineSymbol,
EnableInfoAndAboveLogScriptingDefineSymbol,
EnableWarningAndAboveLogScriptingDefineSymbol,
EnableErrorAndAboveLogScriptingDefineSymbol,
EnableFatalAndAboveLogScriptingDefineSymbol
};
private static readonly string[] SpecifyLogScriptingDefineSymbols = new string[]
{
EnableDebugLogScriptingDefineSymbol,
EnableInfoLogScriptingDefineSymbol,
EnableWarningLogScriptingDefineSymbol,
EnableErrorLogScriptingDefineSymbol,
EnableFatalLogScriptingDefineSymbol
};
/// <summary>
/// 禁用所有日志脚本宏定义。
/// </summary>
[MenuItem("Tools/AlicizaX/Scripting Define Symbols/Disable All Logs", false, 30)]
public static void DisableAllLogs()
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(EnableLogScriptingDefineSymbol);
foreach (string specifyLogScriptingDefineSymbol in SpecifyLogScriptingDefineSymbols)
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(specifyLogScriptingDefineSymbol);
}
foreach (string aboveLogScriptingDefineSymbol in AboveLogScriptingDefineSymbols)
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(aboveLogScriptingDefineSymbol);
}
}
/// <summary>
/// 开启所有日志脚本宏定义。
/// </summary>
[MenuItem("Tools/AlicizaX/Scripting Define Symbols/Enable All Logs", false, 31)]
public static void EnableAllLogs()
{
DisableAllLogs();
ScriptingDefineSymbols.AddScriptingDefineSymbol(EnableLogScriptingDefineSymbol);
}
/// <summary>
/// 开启调试及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("Tools/AlicizaX/Scripting Define Symbols/Enable Debug And Above Logs", false, 32)]
public static void EnableDebugAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableDebugAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启信息及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("Tools/AlicizaX/Scripting Define Symbols/Enable Info And Above Logs", false, 33)]
public static void EnableInfoAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableInfoAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启警告及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("Tools/AlicizaX/Scripting Define Symbols/Enable Warning And Above Logs", false, 34)]
public static void EnableWarningAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableWarningAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启错误及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("Tools/AlicizaX/Scripting Define Symbols/Enable Error And Above Logs", false, 35)]
public static void EnableErrorAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableErrorAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启严重错误及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("Tools/AlicizaX/Scripting Define Symbols/Enable Fatal And Above Logs", false, 36)]
public static void EnableFatalAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableFatalAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 设置日志脚本宏定义。
/// </summary>
/// <param name="aboveLogScriptingDefineSymbol">要设置的日志脚本宏定义。</param>
public static void SetAboveLogScriptingDefineSymbol(string aboveLogScriptingDefineSymbol)
{
if (string.IsNullOrEmpty(aboveLogScriptingDefineSymbol))
{
return;
}
foreach (string i in AboveLogScriptingDefineSymbols)
{
if (i == aboveLogScriptingDefineSymbol)
{
DisableAllLogs();
ScriptingDefineSymbols.AddScriptingDefineSymbol(aboveLogScriptingDefineSymbol);
return;
}
}
}
/// <summary>
/// 设置日志脚本宏定义。
/// </summary>
/// <param name="specifyLogScriptingDefineSymbols">要设置的日志脚本宏定义。</param>
public static void SetSpecifyLogScriptingDefineSymbols(string[] specifyLogScriptingDefineSymbols)
{
if (specifyLogScriptingDefineSymbols == null || specifyLogScriptingDefineSymbols.Length <= 0)
{
return;
}
bool removed = false;
foreach (string specifyLogScriptingDefineSymbol in specifyLogScriptingDefineSymbols)
{
if (string.IsNullOrEmpty(specifyLogScriptingDefineSymbol))
{
continue;
}
foreach (string i in SpecifyLogScriptingDefineSymbols)
{
if (i == specifyLogScriptingDefineSymbol)
{
if (!removed)
{
removed = true;
DisableAllLogs();
}
ScriptingDefineSymbols.AddScriptingDefineSymbol(specifyLogScriptingDefineSymbol);
break;
}
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b5eed5f56efa7e4cb245bee9b064c21
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,108 @@
using System;
using UnityEditor;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEditor.Callbacks;
using UnityEditorInternal;
using UnityEngine;
namespace AlicizaX.Editor
{
public static class OpenAssetLogLine
{
[OnOpenAsset(0)]
private static bool OnOpenAsset(int instanceID, int line)
{
if (line <= 0)
{
return false;
}
// 获取资源路径
string assetPath = AssetDatabase.GetAssetPath(instanceID);
// 判断资源类型
if (!assetPath.EndsWith(".cs"))
{
return false;
}
bool autoFirstMatch = assetPath.Contains("Log.cs");
var stackTrace = GetStackTrace();
if (!string.IsNullOrEmpty(stackTrace) && stackTrace.Contains("Log.cs"))
{
if (!autoFirstMatch)
{
var fullPath = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("Assets", StringComparison.Ordinal));
fullPath = $"{fullPath}{assetPath}";
// 跳转到目标代码的特定行
InternalEditorUtility.OpenFileAtLineExternal(fullPath.Replace('/', '\\'), line);
return true;
}
// 使用正则表达式匹配at的哪个脚本的哪一行
var matches = Regex.Match(stackTrace, @"\(at (.+)\)",
RegexOptions.IgnoreCase);
while (matches.Success)
{
var pathLine = matches.Groups[1].Value;
if (!pathLine.Contains("Log.cs"))
{
var splitIndex = pathLine.LastIndexOf(":", StringComparison.Ordinal);
// 脚本路径
var path = pathLine.Substring(0, splitIndex);
// 行号
line = Convert.ToInt32(pathLine.Substring(splitIndex + 1));
var fullPath = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("Assets", StringComparison.Ordinal));
fullPath = $"{fullPath}{path}";
// 跳转到目标代码的特定行
InternalEditorUtility.OpenFileAtLineExternal(fullPath.Replace('/', '\\'), line);
break;
}
matches = matches.NextMatch();
}
return true;
}
return false;
}
/// <summary>
/// 获取当前日志窗口选中的日志的堆栈信息。
/// </summary>
/// <returns>选中日志的堆栈信息实例。</returns>
private static string GetStackTrace()
{
// 通过反射获取ConsoleWindow类
var consoleWindowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.ConsoleWindow");
// 获取窗口实例
var fieldInfo = consoleWindowType.GetField("ms_ConsoleWindow",
BindingFlags.Static |
BindingFlags.NonPublic);
if (fieldInfo != null)
{
var consoleInstance = fieldInfo.GetValue(null);
if (consoleInstance != null)
if (EditorWindow.focusedWindow == (EditorWindow)consoleInstance)
{
// 获取m_ActiveText成员
fieldInfo = consoleWindowType.GetField("m_ActiveText",
BindingFlags.Instance |
BindingFlags.NonPublic);
// 获取m_ActiveText的值
if (fieldInfo != null)
{
var activeText = fieldInfo.GetValue(consoleInstance).ToString();
return activeText;
}
}
}
return null;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb8c486fbd51ec64fab3749895432f7d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

84
Editor/Misc/OpenFolder.cs Normal file
View File

@ -0,0 +1,84 @@
using AlicizaX;
using System.Diagnostics;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
/// <summary>
/// 打开文件夹相关的实用函数。
/// </summary>
public static class OpenFolder
{
/// <summary>
/// 打开 Data Path 文件夹。
/// </summary>
[MenuItem("Tools/AlicizaX/Open Folder/Data Path", false, 10)]
public static void OpenFolderDataPath()
{
Execute(Application.dataPath);
}
/// <summary>
/// 打开 Persistent Data Path 文件夹。
/// </summary>
[MenuItem("Tools/AlicizaX/Open Folder/Persistent Data Path", false, 11)]
public static void OpenFolderPersistentDataPath()
{
Execute(Application.persistentDataPath);
}
/// <summary>
/// 打开 Streaming Assets Path 文件夹。
/// </summary>
[MenuItem("Tools/AlicizaX/Open Folder/Streaming Assets Path", false, 12)]
public static void OpenFolderStreamingAssetsPath()
{
Execute(Application.streamingAssetsPath);
}
/// <summary>
/// 打开 Temporary Cache Path 文件夹。
/// </summary>
[MenuItem("Tools/AlicizaX/Open Folder/Temporary Cache Path", false, 13)]
public static void OpenFolderTemporaryCachePath()
{
Execute(Application.temporaryCachePath);
}
#if UNITY_2018_3_OR_NEWER
/// <summary>
/// 打开 Console Log Path 文件夹。
/// </summary>
[MenuItem("Tools/AlicizaX/Open Folder/Console Log Path", false, 14)]
public static void OpenFolderConsoleLogPath()
{
Execute(System.IO.Path.GetDirectoryName(Application.consoleLogPath));
}
#endif
/// <summary>
/// 打开指定路径的文件夹。
/// </summary>
/// <param name="folder">要打开的文件夹的路径。</param>
public static void Execute(string folder)
{
folder = Utility.Text.Format("\"{0}\"", folder);
switch (Application.platform)
{
case RuntimePlatform.WindowsEditor:
Process.Start("Explorer.exe", folder.Replace('/', '\\'));
break;
case RuntimePlatform.OSXEditor:
Process.Start("open", folder);
break;
default:
throw new GameFrameworkException(Utility.Text.Format("Not support open folder on '{0}' platform.", Application.platform));
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62e84f6e8237ea540b348d07a13891c7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,98 @@
using System;
using System.IO;
using System.Linq;
using UnityEditorInternal;
using UnityEngine;
namespace AlicizaX.Editor.Setting
{
public class ScriptableSingleton<T> : ScriptableObject where T : ScriptableObject
{
private static T s_Instance;
public static T Instance
{
get
{
if (!s_Instance)
{
LoadOrCreate();
}
return s_Instance;
}
}
public static T LoadOrCreate()
{
string filePath = GetFilePath();
if (!string.IsNullOrEmpty(filePath))
{
var arr = InternalEditorUtility.LoadSerializedFileAndForget(filePath);
s_Instance = arr.Length > 0 ? arr[0] as T : s_Instance ?? CreateInstance<T>();
}
else
{
Debug.LogError($"save location of {nameof(ScriptableSingleton<T>)} is invalid");
}
return s_Instance;
}
public static void Save(bool saveAsText = true)
{
if (!s_Instance)
{
Debug.LogError("Cannot save ScriptableSingleton: no instance!");
return;
}
string filePath = GetFilePath();
if (!string.IsNullOrEmpty(filePath))
{
string directoryName = Path.GetDirectoryName(filePath);
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
UnityEngine.Object[] obj = new T[1] { s_Instance };
InternalEditorUtility.SaveToSerializedFileAndForget(obj, filePath, saveAsText);
}
}
protected static string GetFilePath()
{
return typeof(T).GetCustomAttributes(inherit: true)
.Where(v => v is FilePathAttribute)
.Cast<FilePathAttribute>()
.FirstOrDefault()
?.filepath;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class FilePathAttribute : Attribute
{
internal string filepath;
/// <summary>
/// 单例存放路径
/// </summary>
/// <param name="path">相对 Project 路径</param>
public FilePathAttribute(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException("Invalid relative path (it is empty)");
}
if (path[0] == '/')
{
path = path.Substring(1);
}
filepath = path;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9da8cbe84b4045d093d326334bbf4c7f
timeCreated: 1739529382

View File

@ -0,0 +1,159 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Build;
namespace AlicizaX.Editor
{
/// <summary>
/// 脚本宏定义。
/// </summary>
public static class ScriptingDefineSymbols
{
private static readonly BuildTargetGroup[] BuildTargetGroups = new BuildTargetGroup[]
{
BuildTargetGroup.Standalone,
BuildTargetGroup.iOS,
BuildTargetGroup.Android,
BuildTargetGroup.WSA,
BuildTargetGroup.WebGL
};
/// <summary>
/// 检查指定平台是否存在指定的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要检查脚本宏定义的平台。</param>
/// <param name="scriptingDefineSymbol">要检查的脚本宏定义。</param>
/// <returns>指定平台是否存在指定的脚本宏定义。</returns>
public static bool HasScriptingDefineSymbol(BuildTargetGroup buildTargetGroup, string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return false;
}
string[] scriptingDefineSymbols = GetScriptingDefineSymbols(buildTargetGroup);
foreach (string i in scriptingDefineSymbols)
{
if (i == scriptingDefineSymbol)
{
return true;
}
}
return false;
}
/// <summary>
/// 为指定平台增加指定的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要增加脚本宏定义的平台。</param>
/// <param name="scriptingDefineSymbol">要增加的脚本宏定义。</param>
public static void AddScriptingDefineSymbol(BuildTargetGroup buildTargetGroup, string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return;
}
if (HasScriptingDefineSymbol(buildTargetGroup, scriptingDefineSymbol))
{
return;
}
List<string> scriptingDefineSymbols = new List<string>(GetScriptingDefineSymbols(buildTargetGroup))
{
scriptingDefineSymbol
};
SetScriptingDefineSymbols(buildTargetGroup, scriptingDefineSymbols.ToArray());
}
/// <summary>
/// 为指定平台移除指定的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要移除脚本宏定义的平台。</param>
/// <param name="scriptingDefineSymbol">要移除的脚本宏定义。</param>
public static void RemoveScriptingDefineSymbol(BuildTargetGroup buildTargetGroup, string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return;
}
if (!HasScriptingDefineSymbol(buildTargetGroup, scriptingDefineSymbol))
{
return;
}
List<string> scriptingDefineSymbols = new List<string>(GetScriptingDefineSymbols(buildTargetGroup));
while (scriptingDefineSymbols.Contains(scriptingDefineSymbol))
{
scriptingDefineSymbols.Remove(scriptingDefineSymbol);
}
SetScriptingDefineSymbols(buildTargetGroup, scriptingDefineSymbols.ToArray());
}
/// <summary>
/// 为所有平台增加指定的脚本宏定义。
/// </summary>
/// <param name="scriptingDefineSymbol">要增加的脚本宏定义。</param>
public static void AddScriptingDefineSymbol(string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return;
}
foreach (BuildTargetGroup buildTargetGroup in BuildTargetGroups)
{
AddScriptingDefineSymbol(buildTargetGroup, scriptingDefineSymbol);
}
}
/// <summary>
/// 为所有平台移除指定的脚本宏定义。
/// </summary>
/// <param name="scriptingDefineSymbol">要移除的脚本宏定义。</param>
public static void RemoveScriptingDefineSymbol(string scriptingDefineSymbol)
{
if (string.IsNullOrEmpty(scriptingDefineSymbol))
{
return;
}
foreach (BuildTargetGroup buildTargetGroup in BuildTargetGroups)
{
RemoveScriptingDefineSymbol(buildTargetGroup, scriptingDefineSymbol);
}
}
/// <summary>
/// 获取指定平台的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要获取脚本宏定义的平台。</param>
/// <returns>平台的脚本宏定义。</returns>
public static string[] GetScriptingDefineSymbols(BuildTargetGroup buildTargetGroup)
{
#if UNITY_6000_0_OR_NEWER
return PlayerSettings.GetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup)).Split(';');
#else
return PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Split(';');
#endif
}
/// <summary>
/// 设置指定平台的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要设置脚本宏定义的平台。</param>
/// <param name="scriptingDefineSymbols">要设置的脚本宏定义。</param>
public static void SetScriptingDefineSymbols(BuildTargetGroup buildTargetGroup, string[] scriptingDefineSymbols)
{
#if UNITY_6000_0_OR_NEWER
PlayerSettings.SetScriptingDefineSymbols(NamedBuildTarget.FromBuildTargetGroup(buildTargetGroup), string.Join(";", scriptingDefineSymbols));
#else
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, string.Join(";", scriptingDefineSymbols));
#endif
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 99eaa7f830bac2c469f9aab52406b8ed
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

71
Editor/Misc/TypeUtil.cs Normal file
View File

@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AlicizaX;
namespace AlicizaX.Editor
{
/// <summary>
/// 类型相关的实用函数。
/// </summary>
public static class TypeUtil
{
private static readonly string[] RuntimeAssemblyNames = Utility.Assembly.GetAssemblies().Where(m => !m.FullName.Contains("Editor")).Select(m => m.FullName).ToArray();
private static readonly string[] RuntimeOrEditorAssemblyNames = Utility.Assembly.GetAssemblies().Select(m => m.FullName).ToArray();
/// <summary>
/// 在运行时程序集中获取指定基类的所有子类的名称。
/// </summary>
/// <param name="typeBase">基类类型。</param>
/// <returns>指定基类的所有子类的名称。</returns>
public static string[] GetRuntimeTypeNames(System.Type typeBase)
{
return GetTypeNames(typeBase, RuntimeAssemblyNames);
}
/// <summary>
/// 在运行时或编辑器程序集中获取指定基类的所有子类的名称。
/// </summary>
/// <param name="typeBase">基类类型。</param>
/// <returns>指定基类的所有子类的名称。</returns>
internal static string[] GetRuntimeOrEditorTypeNames(System.Type typeBase)
{
return GetTypeNames(typeBase, RuntimeOrEditorAssemblyNames);
}
private static string[] GetTypeNames(System.Type typeBase, string[] assemblyNames)
{
var typeNames = new List<string>();
foreach (var assemblyName in assemblyNames)
{
Assembly assembly = null;
try
{
assembly = Assembly.Load(assemblyName);
}
catch
{
continue;
}
if (assembly == null)
{
continue;
}
var types = assembly.GetTypes();
foreach (var type in types)
{
if (type.IsClass && !type.IsAbstract && typeBase.IsAssignableFrom(type))
{
typeNames.Add(type.FullName);
}
}
}
typeNames.Sort();
return typeNames.ToArray();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 82f73088e6e538649abbb7e35e3d6bf5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Editor/ObjectPool.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f8f1cd19221bf3c48b13c9b505adc436
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,141 @@
using AlicizaX;
using AlicizaX.ObjectPool;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
[CustomEditor(typeof(ObjectPoolComponent))]
internal sealed class ObjectPoolComponentInspector : GameFrameworkInspector
{
private readonly HashSet<string> m_OpenedItems = new HashSet<string>();
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (!EditorApplication.isPlaying)
{
EditorGUILayout.HelpBox("Available during runtime only.", MessageType.Info);
return;
}
ObjectPoolComponent t = (ObjectPoolComponent)target;
if (IsPrefabInHierarchy(t.gameObject))
{
EditorGUILayout.LabelField("Object Pool Count", t.Count.ToString());
ObjectPoolBase[] objectPools = t.GetAllObjectPools(true);
foreach (ObjectPoolBase objectPool in objectPools)
{
DrawObjectPool(objectPool);
}
}
Repaint();
}
private void DrawObjectPool(ObjectPoolBase objectPool)
{
bool lastState = m_OpenedItems.Contains(objectPool.FullName);
bool currentState = EditorGUILayout.Foldout(lastState, objectPool.FullName);
if (currentState != lastState)
{
if (currentState)
{
m_OpenedItems.Add(objectPool.FullName);
}
else
{
m_OpenedItems.Remove(objectPool.FullName);
}
}
if (currentState)
{
EditorGUILayout.BeginVertical("box");
{
EditorGUILayout.LabelField("Name", objectPool.Name);
EditorGUILayout.LabelField("Type", objectPool.ObjectType.FullName);
EditorGUILayout.LabelField("Auto Release Interval", objectPool.AutoReleaseInterval.ToString());
EditorGUILayout.LabelField("Capacity", objectPool.Capacity.ToString());
EditorGUILayout.LabelField("Used Count", objectPool.Count.ToString());
EditorGUILayout.LabelField("Can Release Count", objectPool.CanReleaseCount.ToString());
EditorGUILayout.LabelField("Expire Time", objectPool.ExpireTime.ToString());
EditorGUILayout.LabelField("Priority", objectPool.Priority.ToString());
ObjectInfo[] objectInfos = objectPool.GetAllObjectInfos();
if (objectInfos.Length > 0)
{
EditorGUILayout.LabelField("Name", objectPool.AllowMultiSpawn ? "Locked\tCount\tFlag\tPriority\tLast Use Time" : "Locked\tIn Use\tFlag\tPriority\tLast Use Time");
foreach (ObjectInfo objectInfo in objectInfos)
{
#if UNITY_6000_0_OR_NEWER
EditorGUILayout.LabelField(string.IsNullOrEmpty(objectInfo.Name) ? "<None>" : objectInfo.Name,
objectPool.AllowMultiSpawn
? Utility.Text.Format("{0,-12}\t{1,-12}\t{2,-12}\t{3,-12}\t{4:yyyy-MM-dd HH:mm:ss,-12}", objectInfo.Locked, objectInfo.SpawnCount, objectInfo.CustomCanReleaseFlag, objectInfo.Priority, objectInfo.LastUseTime.ToLocalTime())
: Utility.Text.Format("{0,-12}\t{1,-12}\t{2,-12}\t{3,-12}\t{4:yyyy-MM-dd HH:mm:ss,-12}", objectInfo.Locked, objectInfo.IsInUse, objectInfo.CustomCanReleaseFlag, objectInfo.Priority, objectInfo.LastUseTime.ToLocalTime()));
#else
EditorGUILayout.LabelField(string.IsNullOrEmpty(objectInfo.Name) ? "<None>" : objectInfo.Name,
objectPool.AllowMultiSpawn
? Utility.Text.Format("{0}\t{1}\t{2}\t{3}\t{4:yyyy-MM-dd HH:mm:ss}", objectInfo.Locked, objectInfo.SpawnCount, objectInfo.CustomCanReleaseFlag, objectInfo.Priority, objectInfo.LastUseTime.ToLocalTime())
: Utility.Text.Format("{0}\t{1}\t{2}\t{3}\t{4:yyyy-MM-dd HH:mm:ss}", objectInfo.Locked, objectInfo.IsInUse, objectInfo.CustomCanReleaseFlag, objectInfo.Priority, objectInfo.LastUseTime.ToLocalTime()));
#endif
}
if (GUILayout.Button("Release"))
{
objectPool.Release();
}
if (GUILayout.Button("Release All Unused"))
{
objectPool.ReleaseAllUnused();
}
if (GUILayout.Button("Export CSV Data"))
{
string exportFileName = EditorUtility.SaveFilePanel("Export CSV Data", string.Empty, Utility.Text.Format("Object Pool Data - {0}.csv", objectPool.Name), string.Empty);
if (!string.IsNullOrEmpty(exportFileName))
{
try
{
int index = 0;
string[] data = new string[objectInfos.Length + 1];
data[index++] = Utility.Text.Format("Name,Locked,{0},Custom Can Release Flag,Priority,Last Use Time", objectPool.AllowMultiSpawn ? "Count" : "In Use");
foreach (ObjectInfo objectInfo in objectInfos)
{
data[index++] = objectPool.AllowMultiSpawn
? Utility.Text.Format("{0},{1},{2},{3},{4},{5:yyyy-MM-dd HH:mm:ss}", objectInfo.Name, objectInfo.Locked, objectInfo.SpawnCount, objectInfo.CustomCanReleaseFlag, objectInfo.Priority, objectInfo.LastUseTime.ToLocalTime())
: Utility.Text.Format("{0},{1},{2},{3},{4},{5:yyyy-MM-dd HH:mm:ss}", objectInfo.Name, objectInfo.Locked, objectInfo.IsInUse, objectInfo.CustomCanReleaseFlag, objectInfo.Priority, objectInfo.LastUseTime.ToLocalTime());
}
File.WriteAllLines(exportFileName, data, Encoding.UTF8);
Debug.Log(Utility.Text.Format("Export object pool CSV data to '{0}' success.", exportFileName));
}
catch (Exception exception)
{
Debug.LogError(Utility.Text.Format("Export object pool CSV data to '{0}' failure, exception is '{1}'.", exportFileName, exception));
}
}
}
}
else
{
GUILayout.Label("Object Pool is Empty ...");
}
}
EditorGUILayout.EndVertical();
EditorGUILayout.Separator();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: eb0d4111d9ba0ad469b2361c7731666a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -18,6 +18,7 @@ namespace AlicizaX.Resource.Editor
"HostPlayMode (联机运行模式)",
"WebPlayMode (WebGL运行模式)"
};
private SerializedProperty _milliseconds = null;
private SerializedProperty _minUnloadUnusedAssetsInterval = null;
private SerializedProperty _maxUnloadUnusedAssetsInterval = null;
@ -30,6 +31,7 @@ namespace AlicizaX.Resource.Editor
private SerializedProperty _failedTryAgain = null;
private SerializedProperty _packageName = null;
private SerializedProperty _decryptionServices = null;
private SerializedProperty _playMode = null;
private int _packageNameIndex = 0;
private string[] _packageNames;
@ -37,6 +39,7 @@ namespace AlicizaX.Resource.Editor
private int m_DecryptionSelectIndex;
private int _playModeIndex = 0;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
@ -58,6 +61,7 @@ namespace AlicizaX.Resource.Editor
if (selectedIndex != _playModeIndex)
{
_playModeIndex = selectedIndex;
_playMode.enumValueIndex = _playModeIndex;
EditorPrefs.SetInt(ResourceComponent.PrefsKey, selectedIndex);
}
}
@ -230,7 +234,6 @@ namespace AlicizaX.Resource.Editor
private void OnEnable()
{
_milliseconds = serializedObject.FindProperty("milliseconds");
_minUnloadUnusedAssetsInterval = serializedObject.FindProperty("minUnloadUnusedAssetsInterval");
_maxUnloadUnusedAssetsInterval = serializedObject.FindProperty("maxUnloadUnusedAssetsInterval");
@ -243,8 +246,11 @@ namespace AlicizaX.Resource.Editor
_failedTryAgain = serializedObject.FindProperty("failedTryAgain");
_packageName = serializedObject.FindProperty("packageName");
_decryptionServices = serializedObject.FindProperty("_decryptionServices");
_playMode = serializedObject.FindProperty("_playMode");
RefreshDecryptionServices();
RefreshTypeNames();
_playModeIndex = EditorPrefs.GetInt(ResourceComponent.PrefsKey, 0);
_playMode.enumValueIndex = _playModeIndex;
}

3
Editor/Utility.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3a0cecfd631d46f09c53e7e2374100d0
timeCreated: 1758265178

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cb3982e0339e441fbb4134b5be9094f0
timeCreated: 1758265207

View File

@ -0,0 +1,519 @@
using System.Collections.Generic;
using System.Reflection;
using System;
using UnityEngine;
using UnityEditor;
namespace AlicizaX.Editor
{
public static class EditorUtils
{
public static class Styles
{
public static GUIStyle IconButton => GUI.skin.FindStyle("IconButton");
public static readonly GUIContent PlusIcon = EditorGUIUtility.TrIconContent("Toolbar Plus", "Add Item");
public static readonly GUIContent MinusIcon = EditorGUIUtility.TrIconContent("Toolbar Minus", "Remove Item");
public static readonly GUIContent TrashIcon = EditorGUIUtility.TrIconContent("TreeEditor.Trash", "Remove Item");
public static readonly GUIContent RefreshIcon = EditorGUIUtility.TrIconContent("Refresh", "Refresh");
public static readonly GUIContent Linked = EditorGUIUtility.TrIconContent("Linked");
public static readonly GUIContent UnLinked = EditorGUIUtility.TrIconContent("Unlinked");
public static readonly GUIContent Database = EditorGUIUtility.TrIconContent("Package Manager");
public static readonly GUIContent GreenLight = EditorGUIUtility.TrIconContent("greenLight");
public static readonly GUIContent OrangeLight = EditorGUIUtility.TrIconContent("orangeLight");
public static readonly GUIContent RedLight = EditorGUIUtility.TrIconContent("redLight");
public static GUIStyle RichLabel => new GUIStyle(EditorStyles.label)
{
richText = true
};
}
public class BoxGroupScope : GUI.Scope
{
public BoxGroupScope(string icon, string title, float height = 22)
{
GUIContent iconTitle = EditorGUIUtility.TrTextContentWithIcon(" " + title, icon);
EditorGUILayout.BeginVertical(GUI.skin.box);
Rect headerRect = GUILayoutUtility.GetRect(1, height);
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
headerRect.x += EditorGUIUtility.standardVerticalSpacing;
EditorGUI.LabelField(headerRect, iconTitle, EditorStyles.boldLabel);
EditorGUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
}
public BoxGroupScope(string title, float height = 22)
{
EditorGUILayout.BeginVertical(GUI.skin.box);
Rect headerRect = GUILayoutUtility.GetRect(1, height);
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
headerRect.x += EditorGUIUtility.standardVerticalSpacing;
EditorGUI.LabelField(headerRect, title, EditorStyles.boldLabel);
EditorGUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
}
protected override void CloseScope()
{
EditorGUILayout.EndVertical();
}
}
public struct PopupArray
{
public List<string> contents;
public List<string> selected;
public PopupArray(string[] contents, string[] selected)
{
this.contents = new List<string>(contents);
this.selected = new List<string>(selected);
}
}
public sealed class PopupElement
{
public Action<string[]> onSelect;
public PopupArray popupArray;
public string name;
public PopupElement(PopupArray popupArray, string name, Action<string[]> onSelect)
{
this.popupArray = popupArray;
this.name = name;
this.onSelect = onSelect;
}
}
public static void DrawOutline(Rect rect, RectOffset border)
{
Color color = new Color(0.6f, 0.6f, 0.6f, 1.333f);
if (EditorGUIUtility.isProSkin)
{
color.r = 0.12f;
color.g = 0.12f;
color.b = 0.12f;
}
if (Event.current.type != EventType.Repaint)
return;
Color orgColor = GUI.color;
GUI.color *= color;
GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, border.top), EditorGUIUtility.whiteTexture); //top
GUI.DrawTexture(new Rect(rect.x, rect.yMax - border.bottom, rect.width, border.bottom), EditorGUIUtility.whiteTexture); //bottom
GUI.DrawTexture(new Rect(rect.x, rect.y + 1, border.left, rect.height - 2 * border.left), EditorGUIUtility.whiteTexture); //left
GUI.DrawTexture(new Rect(rect.xMax - border.right, rect.y + 1, border.right, rect.height - 2 * border.right), EditorGUIUtility.whiteTexture); //right
GUI.color = orgColor;
}
public static void DrawOutline(Rect rect, RectOffset border, Color color)
{
if (Event.current.type != EventType.Repaint)
return;
Color orgColor = GUI.color;
GUI.color *= color;
GUI.DrawTexture(new Rect(rect.x, rect.y, rect.width, border.top), EditorGUIUtility.whiteTexture); //top
GUI.DrawTexture(new Rect(rect.x, rect.yMax - border.bottom, rect.width, border.bottom), EditorGUIUtility.whiteTexture); //bottom
GUI.DrawTexture(new Rect(rect.x, rect.y + 1, border.left, rect.height - 2 * border.left), EditorGUIUtility.whiteTexture); //left
GUI.DrawTexture(new Rect(rect.xMax - border.right, rect.y + 1, border.right, rect.height - 2 * border.right), EditorGUIUtility.whiteTexture); //right
GUI.color = orgColor;
}
public static Rect DrawHeader(float height, string title)
{
Rect rect = GUILayoutUtility.GetRect(0, height);
EditorGUI.DrawRect(rect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
var labelRect = rect;
labelRect.x += 3f;
EditorGUI.LabelField(labelRect, title, EditorStyles.boldLabel);
return rect;
}
public static void DrawHeader(Rect rect, string title, float labelX, float labelY, GUIStyle labelStyle)
{
EditorGUI.DrawRect(rect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
var labelRect = rect;
labelRect.x += labelX;
labelRect.y += labelY;
EditorGUI.LabelField(labelRect, title, labelStyle);
}
public static Rect DrawHeaderWithBorder(GUIContent title, float height, ref Rect rect, bool rounded)
{
GUI.Box(rect, GUIContent.none, new GUIStyle(rounded ? "HelpBox" : "Tooltip"));
Rect headerRect = rect;
headerRect.height = height;
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
Rect labelRect = headerRect;
labelRect.y += height / 2;
EditorGUI.LabelField(labelRect, title, EditorStyles.miniBoldLabel);
rect.x += 1;
rect.y += 1;
rect.height -= 1;
rect.width -= 2;
rect.y += height;
rect.height -= height;
return rect;
}
public static Rect DrawHeaderWithBorder(GUIContent title, float height, ref Rect rect, GUIStyle boxStyle)
{
GUI.Box(rect, GUIContent.none, boxStyle);
rect.x += 1;
rect.y += 1;
rect.height -= 1;
rect.width -= 2;
var headerRect = rect;
headerRect.height = height + EditorGUIUtility.standardVerticalSpacing;
rect.y += headerRect.height;
rect.height -= headerRect.height;
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
var labelRect = headerRect;
labelRect.y += EditorGUIUtility.standardVerticalSpacing;
labelRect.x += 2f;
EditorGUI.LabelField(labelRect, title, EditorStyles.miniBoldLabel);
return headerRect;
}
public static Rect DrawHeaderWithBorder(GUIContent title, float height, ref Rect rect, RectOffset border)
{
DrawOutline(rect, border);
rect.x += 1;
rect.y += 1;
rect.height -= 1;
rect.width -= 2;
var headerRect = rect;
headerRect.height = height + EditorGUIUtility.standardVerticalSpacing;
rect.y += headerRect.height;
rect.height -= headerRect.height;
EditorGUI.DrawRect(headerRect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
var labelRect = headerRect;
labelRect.y += EditorGUIUtility.standardVerticalSpacing;
labelRect.x += 2f;
EditorGUI.LabelField(labelRect, title, EditorStyles.miniBoldLabel);
return headerRect;
}
public static bool DrawBoxFoldoutHeader(string title, bool state, float height = 22)
{
Rect rect = GUILayoutUtility.GetRect(1, height);
EditorGUI.DrawRect(rect, new Color(0.1f, 0.1f, 0.1f, 0.4f));
rect.x += EditorGUIUtility.standardVerticalSpacing;
Rect foldoutRect = EditorGUI.IndentedRect(rect);
state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout);
rect.x += EditorGUIUtility.singleLineHeight - EditorGUIUtility.standardVerticalSpacing * 2;
EditorGUI.LabelField(rect, new GUIContent(title), EditorStyles.boldLabel);
return state;
}
public static bool DrawFoldoutHeader(float height, string title, bool state)
{
Rect rect = GUILayoutUtility.GetRect(1, height);
rect.x += EditorGUIUtility.standardVerticalSpacing;
Rect foldoutRect = EditorGUI.IndentedRect(rect);
state = GUI.Toggle(foldoutRect, state, GUIContent.none, EditorStyles.foldout);
rect.x += EditorGUIUtility.singleLineHeight - EditorGUIUtility.standardVerticalSpacing * 2;
EditorGUI.LabelField(rect, new GUIContent(title), EditorStyles.boldLabel);
return state;
}
public static void DrawFoldoutToggleHeader(Rect rect, string title, ref bool isExpanded, ref bool isEnabled, bool showFoldout = true, bool toggleDisable = false)
{
Color headerColor = new Color(0.1f, 0.1f, 0.1f, 0f);
var expandRect = rect;
expandRect.xMin += EditorGUIUtility.singleLineHeight * 2;
var foldoutRect = rect;
foldoutRect.width = EditorGUIUtility.singleLineHeight;
var toggleRect = rect;
toggleRect.width = EditorGUIUtility.singleLineHeight;
toggleRect.x += EditorGUIUtility.singleLineHeight;
var labelRect = rect;
labelRect.xMin += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 2;
if (showFoldout)
{
// events
var e = Event.current;
if (expandRect.Contains(e.mousePosition))
{
if (e.type == EventType.MouseDown && e.button == 0)
{
isExpanded = !isExpanded;
e.Use();
}
}
// foldout
isExpanded = GUI.Toggle(foldoutRect, isExpanded, GUIContent.none, EditorStyles.foldout);
}
// background
EditorGUI.DrawRect(rect, headerColor);
// toggle
using (new EditorGUI.DisabledGroupScope(toggleDisable))
{
isEnabled = GUI.Toggle(toggleRect, isEnabled, new GUIContent("", "Extension Enabled State"), EditorStyles.toggle);
}
// title
EditorGUI.LabelField(labelRect, new GUIContent(title), EditorStyles.boldLabel);
}
private static void PopupSelect(object data)
{
PopupElement element = (PopupElement)data;
PopupArray array = element.popupArray;
string name = element.name;
if (array.selected.Contains(name))
array.selected.Remove(name);
else array.selected.Add(name);
element.onSelect?.Invoke(array.selected.ToArray());
}
public static void DrawMultiSelectionPopup(Rect rect, string title, PopupArray popupArray, Action<string[]> onSelect)
{
GenericMenu menu = new GenericMenu();
for (int i = 0; i < popupArray.contents.Count; i++)
{
string name = popupArray.contents[i];
bool on = popupArray.selected.Contains(name);
PopupElement element = new PopupElement(popupArray, name, onSelect);
menu.AddItem(new GUIContent(name), on, PopupSelect, element);
}
if(GUI.Button(rect, title, EditorStyles.popup))
{
menu.ShowAsContext();
}
}
public static void DrawRelativeProperties(SerializedProperty root, float width)
{
var childrens = root.GetVisibleChildrens();
foreach (var childProperty in childrens)
{
float height = EditorGUI.GetPropertyHeight(childProperty, true);
Rect rect = GUILayoutUtility.GetRect(1f, height);
rect.xMin += width;
EditorGUI.PropertyField(rect, childProperty, true);
EditorGUILayout.Space(EditorGUIUtility.standardVerticalSpacing);
}
}
public static IEnumerable<SerializedProperty> GetVisibleChildrens(this SerializedProperty serializedProperty)
{
SerializedProperty currentProperty = serializedProperty.Copy();
SerializedProperty nextSiblingProperty = serializedProperty.Copy();
{
nextSiblingProperty.NextVisible(false);
}
if (currentProperty.NextVisible(true))
{
do
{
if (SerializedProperty.EqualContents(currentProperty, nextSiblingProperty))
break;
yield return currentProperty;
}
while (currentProperty.NextVisible(false));
}
}
public static void TrHelpIconText(string message, string icon, bool rich = false)
{
GUIStyle style = new GUIStyle(EditorStyles.helpBox)
{
richText = rich
};
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(" " + message, icon), style, new GUILayoutOption[0]);
}
public static void TrHelpIconText(Rect rect, string message, string icon, bool rich = false)
{
GUIStyle style = new GUIStyle(EditorStyles.helpBox)
{
richText = rich
};
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(" " + message, icon), style);
}
public static void TrHelpIconText(string message, MessageType messageType, bool rich = false, bool space = true)
{
string icon = string.Empty;
GUIStyle style = new GUIStyle(EditorStyles.helpBox)
{
richText = rich
};
switch (messageType)
{
case MessageType.Info:
icon = "console.infoicon.sml";
break;
case MessageType.Warning:
icon = "console.warnicon.sml";
break;
case MessageType.Error:
icon = "console.erroricon.sml";
break;
}
if (!string.IsNullOrEmpty(icon))
{
string text = space ? " " + message : message;
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style, new GUILayoutOption[0]);
}
else
{
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContent(message), style, new GUILayoutOption[0]);
}
}
public static void TrHelpIconText(Rect rect, string message, MessageType messageType, bool rich = false, bool space = true)
{
string icon = string.Empty;
GUIStyle style = new GUIStyle(EditorStyles.helpBox)
{
richText = rich
};
switch (messageType)
{
case MessageType.Info:
icon = "console.infoicon.sml";
break;
case MessageType.Warning:
icon = "console.warnicon.sml";
break;
case MessageType.Error:
icon = "console.erroricon.sml";
break;
}
if (!string.IsNullOrEmpty(icon))
{
string text = space ? " " + message : message;
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style);
}
else
{
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContent(message), style);
}
}
public static void TrIconText(string message, string icon, GUIStyle style, bool rich = false, bool space = true)
{
style.richText = rich;
string text = space ? " " + message : message;
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style, new GUILayoutOption[0]);
}
public static void TrIconText(string message, MessageType messageType, GUIStyle style, bool rich = false, bool space = true)
{
string icon = string.Empty;
style.richText = rich;
switch (messageType)
{
case MessageType.Info:
icon = "console.infoicon.sml";
break;
case MessageType.Warning:
icon = "console.warnicon.sml";
break;
case MessageType.Error:
icon = "console.erroricon.sml";
break;
}
if (!string.IsNullOrEmpty(icon))
{
string text = space ? " " + message : message;
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style, new GUILayoutOption[0]);
}
else
{
EditorGUILayout.LabelField(GUIContent.none, EditorGUIUtility.TrTextContent(message), style, new GUILayoutOption[0]);
}
}
public static void TrIconText(Rect rect, string message, MessageType messageType, GUIStyle style, bool rich = false, bool space = true)
{
string icon = string.Empty;
style.richText = rich;
switch (messageType)
{
case MessageType.Info:
icon = "console.infoicon.sml";
break;
case MessageType.Warning:
icon = "console.warnicon.sml";
break;
case MessageType.Error:
icon = "console.erroricon.sml";
break;
}
if (!string.IsNullOrEmpty(icon))
{
string text = space ? " " + message : message;
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContentWithIcon(text, icon), style);
}
else
{
EditorGUI.LabelField(rect, GUIContent.none, EditorGUIUtility.TrTextContent(message), style);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b87483fd614d43e781d70a25494f0616
timeCreated: 1758265230

View File

@ -0,0 +1,207 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
using System;
namespace AlicizaX.Editor
{
public class PropertyCollection : Dictionary<string, SerializedProperty>
{
public SerializedProperty GetRelative(string propertyPath)
{
string[] paths = propertyPath.Split(new char[] { '.' });
if (TryGetValue(paths[0], out SerializedProperty pathProperty))
{
for (int i = 1; i < paths.Length; i++)
{
pathProperty = pathProperty.FindPropertyRelative(paths[i]);
}
return pathProperty;
}
return null;
}
public void DrawRelative(string propertyPath)
{
string[] paths = propertyPath.Split(new char[] { '.' });
if (TryGetValue(paths[0], out SerializedProperty pathProperty))
{
for (int i = 1; i < paths.Length; i++)
{
pathProperty = pathProperty.FindPropertyRelative(paths[i]);
}
EditorGUILayout.PropertyField(pathProperty);
}
}
public void DrawRelative(string propertyPath, GUIContent label)
{
string[] paths = propertyPath.Split(new char[] { '.' });
if (TryGetValue(paths[0], out SerializedProperty pathProperty))
{
for (int i = 1; i < paths.Length; i++)
{
pathProperty = pathProperty.FindPropertyRelative(paths[i]);
}
EditorGUILayout.PropertyField(pathProperty, label);
}
}
public void Draw(string propertyName, int indent = 0)
{
if (TryGetValue(propertyName, out SerializedProperty property))
{
if (indent > 0) EditorGUI.indentLevel += indent;
EditorGUILayout.PropertyField(property);
if (indent > 0) EditorGUI.indentLevel -= indent;
}
}
public void DrawBacking(string propertyName)
{
propertyName = $"<{propertyName}>k__BackingField";
if (TryGetValue(propertyName, out SerializedProperty property))
EditorGUILayout.PropertyField(property);
}
public bool DrawGetBool(string propertyName)
{
if (TryGetValue(propertyName, out SerializedProperty property))
{
EditorGUILayout.PropertyField(property);
return property.boolValue;
}
return false;
}
public bool DrawToggleLeft(string propertyName)
{
if (TryGetValue(propertyName, out SerializedProperty property))
{
GUIContent label = new(property.displayName, property.tooltip);
return property.boolValue = EditorGUILayout.ToggleLeft(label, property.boolValue);
}
return false;
}
public void DrawAll(bool indentDropdowns = false, int skip = 0)
{
foreach (var property in this.Skip(skip))
{
bool shouldIndent = property.Value.propertyType == SerializedPropertyType.Generic;
if (indentDropdowns && shouldIndent) EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(property.Value);
if (indentDropdowns && shouldIndent) EditorGUI.indentLevel--;
}
}
public void DrawAllExcept(bool indentDropdowns = false, int skip = 0, params string[] except)
{
foreach (var property in this.Skip(skip))
{
if (except.Contains(property.Key))
continue;
bool shouldIndent = property.Value.propertyType == SerializedPropertyType.Generic;
if (indentDropdowns && shouldIndent) EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(property.Value);
if (indentDropdowns && shouldIndent) EditorGUI.indentLevel--;
}
}
public void DrawAllPredicate(bool indentDropdowns, int skip, Predicate<string> predicate)
{
foreach (var property in this.Skip(skip))
{
if (!predicate(property.Key))
continue;
bool shouldIndent = property.Value.propertyType == SerializedPropertyType.Generic;
if (indentDropdowns && shouldIndent) EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(property.Value);
if (indentDropdowns && shouldIndent) EditorGUI.indentLevel--;
}
}
public bool BoolValue(string propertyName)
{
if (TryGetValue(propertyName, out SerializedProperty property))
return property.boolValue;
return false;
}
public void DrawArray(string propertyName)
{
if (TryGetValue(propertyName, out SerializedProperty property))
{
if (property.isArray) EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(property);
if (property.isArray) EditorGUI.indentLevel--;
}
}
public void Draw(string propertyName, bool includeChildren)
{
if (TryGetValue(propertyName, out SerializedProperty property))
EditorGUILayout.PropertyField(property, includeChildren);
}
public void Draw(string propertyName, GUIContent label)
{
if (TryGetValue(propertyName, out SerializedProperty property))
EditorGUILayout.PropertyField(property, label);
}
public bool DrawGetBool(string propertyName, GUIContent label)
{
if (TryGetValue(propertyName, out SerializedProperty property))
{
EditorGUILayout.PropertyField(property, label);
return property.boolValue;
}
return false;
}
public void Draw(string propertyName, GUIContent label, bool includeChildren)
{
if (TryGetValue(propertyName, out SerializedProperty property))
EditorGUILayout.PropertyField(property, label, includeChildren);
}
public void Draw(Rect rect, string propertyName)
{
if (TryGetValue(propertyName, out SerializedProperty property))
EditorGUI.PropertyField(rect, property);
}
public void Draw(Rect rect, string propertyName, bool includeChildren)
{
if (TryGetValue(propertyName, out SerializedProperty property))
EditorGUI.PropertyField(rect, property, includeChildren);
}
public void Draw(Rect rect, string propertyName, GUIContent label)
{
if (TryGetValue(propertyName, out SerializedProperty property))
EditorGUI.PropertyField(rect, property, label);
}
public void Draw(Rect rect, string propertyName, GUIContent label, bool includeChildren)
{
if (TryGetValue(propertyName, out SerializedProperty property))
EditorGUI.PropertyField(rect, property, label, includeChildren);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c59762160e44443096c548c7ea024d00
timeCreated: 1758265184

Binary file not shown.

View File

@ -0,0 +1,52 @@
fileFormatVersion: 2
guid: e0f9fe588fe13b14ca28cddf735a2f0b
labels:
- RoslynAnalyzer
PluginImporter:
externalObjects: {}
serializedVersion: 3
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
Any:
enabled: 1
settings:
Exclude Editor: 1
Exclude Linux64: 0
Exclude OSXUniversal: 0
Exclude Win: 0
Exclude Win64: 0
Editor:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
Linux64:
enabled: 1
settings:
CPU: AnyCPU
OSXUniversal:
enabled: 1
settings:
CPU: AnyCPU
Win:
enabled: 1
settings:
CPU: AnyCPU
Win64:
enabled: 1
settings:
CPU: AnyCPU
WindowsStoreApps:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,83 @@
fileFormatVersion: 2
guid: de67d260a82e87742b9c3c297379ffb3
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
: Linux
second:
enabled: 0
settings:
CPU: x86
- first:
: LinuxUniversal
second:
enabled: 0
settings:
CPU: None
- first:
: OSXIntel
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
: OSXIntel64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: None
- first:
Standalone: Win
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

BIN
Plugins/System.Buffers.dll Normal file

Binary file not shown.

View File

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 80b599a6cb4f1c94dbb15cbb8b52b5e7
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

BIN
Plugins/System.Memory.dll Normal file

Binary file not shown.

View File

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: e69802479db3f6540b50e7515c7e7ef9
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

View File

@ -0,0 +1,33 @@
fileFormatVersion: 2
guid: 8f0fcff9f03d0ec498f1e227c9c6878a
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 0
isExplicitlyReferenced: 0
validateReferences: 1
platformData:
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
- first:
Windows Store Apps: WindowsStoreApps
second:
enabled: 0
settings:
CPU: AnyCPU
userData:
assetBundleName:
assetBundleVariant:

BIN
Plugins/XLog.dll Normal file

Binary file not shown.

2
Plugins/XLog.dll.meta Normal file
View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d54020f8ad5378144bdeef5bb9b7a769

8
Runtime/ABase.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 74d41a49b8f6d8442b0484a9d0f1a99d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

3
Runtime/ABase/Base.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 17ad22f068c740e7a51303c78a742268
timeCreated: 1736324891

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a40ddcd0fd47a4c06bb194117de12597
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,446 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace AlicizaX
{
/// <summary>
/// 游戏框架链表类。
/// </summary>
/// <typeparam name="T">指定链表的元素类型。</typeparam>
public sealed class GameFrameworkLinkedList<T> : ICollection<T>, IEnumerable<T>, ICollection, IEnumerable
{
private readonly LinkedList<T> m_LinkedList;
private readonly Queue<LinkedListNode<T>> m_CachedNodes;
/// <summary>
/// 初始化游戏框架链表类的新实例。
/// </summary>
public GameFrameworkLinkedList()
{
m_LinkedList = new LinkedList<T>();
m_CachedNodes = new Queue<LinkedListNode<T>>();
}
/// <summary>
/// 获取链表中实际包含的结点数量。
/// </summary>
public int Count
{
get
{
return m_LinkedList.Count;
}
}
/// <summary>
/// 获取链表结点缓存数量。
/// </summary>
public int CachedNodeCount
{
get
{
return m_CachedNodes.Count;
}
}
/// <summary>
/// 获取链表的第一个结点。
/// </summary>
public LinkedListNode<T> First
{
get
{
return m_LinkedList.First;
}
}
/// <summary>
/// 获取链表的最后一个结点。
/// </summary>
public LinkedListNode<T> Last
{
get
{
return m_LinkedList.Last;
}
}
/// <summary>
/// 获取一个值,该值指示 ICollection`1 是否为只读。
/// </summary>
public bool IsReadOnly
{
get
{
return ((ICollection<T>)m_LinkedList).IsReadOnly;
}
}
/// <summary>
/// 获取可用于同步对 ICollection 的访问的对象。
/// </summary>
public object SyncRoot
{
get
{
return ((ICollection)m_LinkedList).SyncRoot;
}
}
/// <summary>
/// 获取一个值,该值指示是否同步对 ICollection 的访问(线程安全)。
/// </summary>
public bool IsSynchronized
{
get
{
return ((ICollection)m_LinkedList).IsSynchronized;
}
}
/// <summary>
/// 在链表中指定的现有结点后添加包含指定值的新结点。
/// </summary>
/// <param name="node">指定的现有结点。</param>
/// <param name="value">指定值。</param>
/// <returns>包含指定值的新结点。</returns>
public LinkedListNode<T> AddAfter(LinkedListNode<T> node, T value)
{
LinkedListNode<T> newNode = AcquireNode(value);
m_LinkedList.AddAfter(node, newNode);
return newNode;
}
/// <summary>
/// 在链表中指定的现有结点后添加指定的新结点。
/// </summary>
/// <param name="node">指定的现有结点。</param>
/// <param name="newNode">指定的新结点。</param>
public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
m_LinkedList.AddAfter(node, newNode);
}
/// <summary>
/// 在链表中指定的现有结点前添加包含指定值的新结点。
/// </summary>
/// <param name="node">指定的现有结点。</param>
/// <param name="value">指定值。</param>
/// <returns>包含指定值的新结点。</returns>
public LinkedListNode<T> AddBefore(LinkedListNode<T> node, T value)
{
LinkedListNode<T> newNode = AcquireNode(value);
m_LinkedList.AddBefore(node, newNode);
return newNode;
}
/// <summary>
/// 在链表中指定的现有结点前添加指定的新结点。
/// </summary>
/// <param name="node">指定的现有结点。</param>
/// <param name="newNode">指定的新结点。</param>
public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
{
m_LinkedList.AddBefore(node, newNode);
}
/// <summary>
/// 在链表的开头处添加包含指定值的新结点。
/// </summary>
/// <param name="value">指定值。</param>
/// <returns>包含指定值的新结点。</returns>
public LinkedListNode<T> AddFirst(T value)
{
LinkedListNode<T> node = AcquireNode(value);
m_LinkedList.AddFirst(node);
return node;
}
/// <summary>
/// 在链表的开头处添加指定的新结点。
/// </summary>
/// <param name="node">指定的新结点。</param>
public void AddFirst(LinkedListNode<T> node)
{
m_LinkedList.AddFirst(node);
}
/// <summary>
/// 在链表的结尾处添加包含指定值的新结点。
/// </summary>
/// <param name="value">指定值。</param>
/// <returns>包含指定值的新结点。</returns>
public LinkedListNode<T> AddLast(T value)
{
LinkedListNode<T> node = AcquireNode(value);
m_LinkedList.AddLast(node);
return node;
}
/// <summary>
/// 在链表的结尾处添加指定的新结点。
/// </summary>
/// <param name="node">指定的新结点。</param>
public void AddLast(LinkedListNode<T> node)
{
m_LinkedList.AddLast(node);
}
/// <summary>
/// 从链表中移除所有结点。
/// </summary>
public void Clear()
{
LinkedListNode<T> current = m_LinkedList.First;
while (current != null)
{
ReleaseNode(current);
current = current.Next;
}
m_LinkedList.Clear();
}
/// <summary>
/// 清除链表结点缓存。
/// </summary>
public void ClearCachedNodes()
{
m_CachedNodes.Clear();
}
/// <summary>
/// 确定某值是否在链表中。
/// </summary>
/// <param name="value">指定值。</param>
/// <returns>某值是否在链表中。</returns>
public bool Contains(T value)
{
return m_LinkedList.Contains(value);
}
/// <summary>
/// 从目标数组的指定索引处开始将整个链表复制到兼容的一维数组。
/// </summary>
/// <param name="array">一维数组,它是从链表复制的元素的目标。数组必须具有从零开始的索引。</param>
/// <param name="index">array 中从零开始的索引,从此处开始复制。</param>
public void CopyTo(T[] array, int index)
{
m_LinkedList.CopyTo(array, index);
}
/// <summary>
/// 从特定的 ICollection 索引开始,将数组的元素复制到一个数组中。
/// </summary>
/// <param name="array">一维数组,它是从 ICollection 复制的元素的目标。数组必须具有从零开始的索引。</param>
/// <param name="index">array 中从零开始的索引,从此处开始复制。</param>
public void CopyTo(Array array, int index)
{
((ICollection)m_LinkedList).CopyTo(array, index);
}
/// <summary>
/// 查找包含指定值的第一个结点。
/// </summary>
/// <param name="value">要查找的指定值。</param>
/// <returns>包含指定值的第一个结点。</returns>
public LinkedListNode<T> Find(T value)
{
return m_LinkedList.Find(value);
}
/// <summary>
/// 查找包含指定值的最后一个结点。
/// </summary>
/// <param name="value">要查找的指定值。</param>
/// <returns>包含指定值的最后一个结点。</returns>
public LinkedListNode<T> FindLast(T value)
{
return m_LinkedList.FindLast(value);
}
/// <summary>
/// 从链表中移除指定值的第一个匹配项。
/// </summary>
/// <param name="value">指定值。</param>
/// <returns>是否移除成功。</returns>
public bool Remove(T value)
{
LinkedListNode<T> node = m_LinkedList.Find(value);
if (node != null)
{
m_LinkedList.Remove(node);
ReleaseNode(node);
return true;
}
return false;
}
/// <summary>
/// 从链表中移除指定的结点。
/// </summary>
/// <param name="node">指定的结点。</param>
public void Remove(LinkedListNode<T> node)
{
m_LinkedList.Remove(node);
ReleaseNode(node);
}
/// <summary>
/// 移除位于链表开头处的结点。
/// </summary>
public void RemoveFirst()
{
LinkedListNode<T> first = m_LinkedList.First;
if (first == null)
{
throw new GameFrameworkException("First is invalid.");
}
m_LinkedList.RemoveFirst();
ReleaseNode(first);
}
/// <summary>
/// 移除位于链表结尾处的结点。
/// </summary>
public void RemoveLast()
{
LinkedListNode<T> last = m_LinkedList.Last;
if (last == null)
{
throw new GameFrameworkException("Last is invalid.");
}
m_LinkedList.RemoveLast();
ReleaseNode(last);
}
/// <summary>
/// 返回循环访问集合的枚举数。
/// </summary>
/// <returns>循环访问集合的枚举数。</returns>
public Enumerator GetEnumerator()
{
return new Enumerator(m_LinkedList);
}
private LinkedListNode<T> AcquireNode(T value)
{
LinkedListNode<T> node = null;
if (m_CachedNodes.Count > 0)
{
node = m_CachedNodes.Dequeue();
node.Value = value;
}
else
{
node = new LinkedListNode<T>(value);
}
return node;
}
private void ReleaseNode(LinkedListNode<T> node)
{
node.Value = default(T);
m_CachedNodes.Enqueue(node);
}
/// <summary>
/// 将值添加到 ICollection`1 的结尾处。
/// </summary>
/// <param name="value">要添加的值。</param>
void ICollection<T>.Add(T value)
{
AddLast(value);
}
/// <summary>
/// 返回循环访问集合的枚举数。
/// </summary>
/// <returns>循环访问集合的枚举数。</returns>
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// 返回循环访问集合的枚举数。
/// </summary>
/// <returns>循环访问集合的枚举数。</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// 循环访问集合的枚举数。
/// </summary>
[StructLayout(LayoutKind.Auto)]
public struct Enumerator : IEnumerator<T>, IEnumerator
{
private LinkedList<T>.Enumerator m_Enumerator;
internal Enumerator(LinkedList<T> linkedList)
{
if (linkedList == null)
{
throw new GameFrameworkException("Linked list is invalid.");
}
m_Enumerator = linkedList.GetEnumerator();
}
/// <summary>
/// 获取当前结点。
/// </summary>
public T Current
{
get
{
return m_Enumerator.Current;
}
}
/// <summary>
/// 获取当前的枚举数。
/// </summary>
object IEnumerator.Current
{
get
{
return m_Enumerator.Current;
}
}
/// <summary>
/// 清理枚举数。
/// </summary>
public void Dispose()
{
m_Enumerator.Dispose();
}
/// <summary>
/// 获取下一个结点。
/// </summary>
/// <returns>返回下一个结点。</returns>
public bool MoveNext()
{
return m_Enumerator.MoveNext();
}
/// <summary>
/// 重置枚举数。
/// </summary>
void IEnumerator.Reset()
{
((IEnumerator<T>)m_Enumerator).Reset();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8662d2b0651bb46308fa9ea5a24a9f08
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,210 @@
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace AlicizaX
{
/// <summary>
/// 游戏框架链表范围。
/// </summary>
/// <typeparam name="T">指定链表范围的元素类型。</typeparam>
[StructLayout(LayoutKind.Auto)]
public readonly struct GameFrameworkLinkedListRange<T> : IEnumerable<T>, IEnumerable
{
private readonly LinkedListNode<T> m_First;
private readonly LinkedListNode<T> m_Terminal;
/// <summary>
/// 初始化游戏框架链表范围的新实例。
/// </summary>
/// <param name="first">链表范围的开始结点。</param>
/// <param name="terminal">链表范围的终结标记结点。</param>
public GameFrameworkLinkedListRange(LinkedListNode<T> first, LinkedListNode<T> terminal)
{
if (first == null || terminal == null || first == terminal)
{
throw new GameFrameworkException("Range is invalid.");
}
m_First = first;
m_Terminal = terminal;
}
/// <summary>
/// 获取链表范围是否有效。
/// </summary>
public bool IsValid
{
get
{
return m_First != null && m_Terminal != null && m_First != m_Terminal;
}
}
/// <summary>
/// 获取链表范围的开始结点。
/// </summary>
public LinkedListNode<T> First
{
get
{
return m_First;
}
}
/// <summary>
/// 获取链表范围的终结标记结点。
/// </summary>
public LinkedListNode<T> Terminal
{
get
{
return m_Terminal;
}
}
/// <summary>
/// 获取链表范围的结点数量。
/// </summary>
public int Count
{
get
{
if (!IsValid)
{
return 0;
}
int count = 0;
for (LinkedListNode<T> current = m_First; current != null && current != m_Terminal; current = current.Next)
{
count++;
}
return count;
}
}
/// <summary>
/// 检查是否包含指定值。
/// </summary>
/// <param name="value">要检查的值。</param>
/// <returns>是否包含指定值。</returns>
public bool Contains(T value)
{
for (LinkedListNode<T> current = m_First; current != null && current != m_Terminal; current = current.Next)
{
if (current.Value.Equals(value))
{
return true;
}
}
return false;
}
/// <summary>
/// 返回循环访问集合的枚举数。
/// </summary>
/// <returns>循环访问集合的枚举数。</returns>
public Enumerator GetEnumerator()
{
return new Enumerator(this);
}
/// <summary>
/// 返回循环访问集合的枚举数。
/// </summary>
/// <returns>循环访问集合的枚举数。</returns>
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// 返回循环访问集合的枚举数。
/// </summary>
/// <returns>循环访问集合的枚举数。</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// 循环访问集合的枚举数。
/// </summary>
[StructLayout(LayoutKind.Auto)]
public struct Enumerator : IEnumerator<T>, IEnumerator
{
private readonly GameFrameworkLinkedListRange<T> m_GameFrameworkLinkedListRange;
private LinkedListNode<T> m_Current;
private T m_CurrentValue;
internal Enumerator(GameFrameworkLinkedListRange<T> range)
{
if (!range.IsValid)
{
throw new GameFrameworkException("Range is invalid.");
}
m_GameFrameworkLinkedListRange = range;
m_Current = m_GameFrameworkLinkedListRange.m_First;
m_CurrentValue = default(T);
}
/// <summary>
/// 获取当前结点。
/// </summary>
public T Current
{
get
{
return m_CurrentValue;
}
}
/// <summary>
/// 获取当前的枚举数。
/// </summary>
object IEnumerator.Current
{
get
{
return m_CurrentValue;
}
}
/// <summary>
/// 清理枚举数。
/// </summary>
public void Dispose()
{
}
/// <summary>
/// 获取下一个结点。
/// </summary>
/// <returns>返回下一个结点。</returns>
public bool MoveNext()
{
if (m_Current == null || m_Current == m_GameFrameworkLinkedListRange.m_Terminal)
{
return false;
}
m_CurrentValue = m_Current.Value;
m_Current = m_Current.Next;
return true;
}
/// <summary>
/// 重置枚举数。
/// </summary>
void IEnumerator.Reset()
{
m_Current = m_GameFrameworkLinkedListRange.m_First;
m_CurrentValue = default(T);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7edbebe0b1fd848418e2f1d863b611e1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,276 @@
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace AlicizaX
{
/// <summary>
/// 游戏框架多值字典类。
/// </summary>
/// <typeparam name="TKey">指定多值字典的主键类型。</typeparam>
/// <typeparam name="TValue">指定多值字典的值类型。</typeparam>
public sealed class GameFrameworkMultiDictionary<TKey, TValue> : IEnumerable<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>, IEnumerable
{
private readonly GameFrameworkLinkedList<TValue> m_LinkedList;
private readonly Dictionary<TKey, GameFrameworkLinkedListRange<TValue>> m_Dictionary;
/// <summary>
/// 初始化游戏框架多值字典类的新实例。
/// </summary>
public GameFrameworkMultiDictionary()
{
m_LinkedList = new GameFrameworkLinkedList<TValue>();
m_Dictionary = new Dictionary<TKey, GameFrameworkLinkedListRange<TValue>>();
}
/// <summary>
/// 获取多值字典中实际包含的主键数量。
/// </summary>
public int Count
{
get
{
return m_Dictionary.Count;
}
}
/// <summary>
/// 获取多值字典中指定主键的范围。
/// </summary>
/// <param name="key">指定的主键。</param>
/// <returns>指定主键的范围。</returns>
public GameFrameworkLinkedListRange<TValue> this[TKey key]
{
get
{
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
m_Dictionary.TryGetValue(key, out range);
return range;
}
}
/// <summary>
/// 清理多值字典。
/// </summary>
public void Clear()
{
m_Dictionary.Clear();
m_LinkedList.Clear();
}
/// <summary>
/// 检查多值字典中是否包含指定主键。
/// </summary>
/// <param name="key">要检查的主键。</param>
/// <returns>多值字典中是否包含指定主键。</returns>
public bool Contains(TKey key)
{
return m_Dictionary.ContainsKey(key);
}
/// <summary>
/// 检查多值字典中是否包含指定值。
/// </summary>
/// <param name="key">要检查的主键。</param>
/// <param name="value">要检查的值。</param>
/// <returns>多值字典中是否包含指定值。</returns>
public bool Contains(TKey key, TValue value)
{
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
if (m_Dictionary.TryGetValue(key, out range))
{
return range.Contains(value);
}
return false;
}
/// <summary>
/// 尝试获取多值字典中指定主键的范围。
/// </summary>
/// <param name="key">指定的主键。</param>
/// <param name="range">指定主键的范围。</param>
/// <returns>是否获取成功。</returns>
public bool TryGetValue(TKey key, out GameFrameworkLinkedListRange<TValue> range)
{
return m_Dictionary.TryGetValue(key, out range);
}
/// <summary>
/// 向指定的主键增加指定的值。
/// </summary>
/// <param name="key">指定的主键。</param>
/// <param name="value">指定的值。</param>
public void Add(TKey key, TValue value)
{
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
if (m_Dictionary.TryGetValue(key, out range))
{
m_LinkedList.AddBefore(range.Terminal, value);
}
else
{
LinkedListNode<TValue> first = m_LinkedList.AddLast(value);
LinkedListNode<TValue> terminal = m_LinkedList.AddLast(default(TValue));
m_Dictionary.Add(key, new GameFrameworkLinkedListRange<TValue>(first, terminal));
}
}
/// <summary>
/// 从指定的主键中移除指定的值。
/// </summary>
/// <param name="key">指定的主键。</param>
/// <param name="value">指定的值。</param>
/// <returns>是否移除成功。</returns>
public bool Remove(TKey key, TValue value)
{
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
if (m_Dictionary.TryGetValue(key, out range))
{
for (LinkedListNode<TValue> current = range.First; current != null && current != range.Terminal; current = current.Next)
{
if (current.Value.Equals(value))
{
if (current == range.First)
{
LinkedListNode<TValue> next = current.Next;
if (next == range.Terminal)
{
m_LinkedList.Remove(next);
m_Dictionary.Remove(key);
}
else
{
m_Dictionary[key] = new GameFrameworkLinkedListRange<TValue>(next, range.Terminal);
}
}
m_LinkedList.Remove(current);
return true;
}
}
}
return false;
}
/// <summary>
/// 从指定的主键中移除所有的值。
/// </summary>
/// <param name="key">指定的主键。</param>
/// <returns>是否移除成功。</returns>
public bool RemoveAll(TKey key)
{
GameFrameworkLinkedListRange<TValue> range = default(GameFrameworkLinkedListRange<TValue>);
if (m_Dictionary.TryGetValue(key, out range))
{
m_Dictionary.Remove(key);
LinkedListNode<TValue> current = range.First;
while (current != null)
{
LinkedListNode<TValue> next = current != range.Terminal ? current.Next : null;
m_LinkedList.Remove(current);
current = next;
}
return true;
}
return false;
}
/// <summary>
/// 返回循环访问集合的枚举数。
/// </summary>
/// <returns>循环访问集合的枚举数。</returns>
public Enumerator GetEnumerator()
{
return new Enumerator(m_Dictionary);
}
/// <summary>
/// 返回循环访问集合的枚举数。
/// </summary>
/// <returns>循环访问集合的枚举数。</returns>
IEnumerator<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>> IEnumerable<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// 返回循环访问集合的枚举数。
/// </summary>
/// <returns>循环访问集合的枚举数。</returns>
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// <summary>
/// 循环访问集合的枚举数。
/// </summary>
[StructLayout(LayoutKind.Auto)]
public struct Enumerator : IEnumerator<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>, IEnumerator
{
private Dictionary<TKey, GameFrameworkLinkedListRange<TValue>>.Enumerator m_Enumerator;
internal Enumerator(Dictionary<TKey, GameFrameworkLinkedListRange<TValue>> dictionary)
{
if (dictionary == null)
{
throw new GameFrameworkException("Dictionary is invalid.");
}
m_Enumerator = dictionary.GetEnumerator();
}
/// <summary>
/// 获取当前结点。
/// </summary>
public KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>> Current
{
get
{
return m_Enumerator.Current;
}
}
/// <summary>
/// 获取当前的枚举数。
/// </summary>
object IEnumerator.Current
{
get
{
return m_Enumerator.Current;
}
}
/// <summary>
/// 清理枚举数。
/// </summary>
public void Dispose()
{
m_Enumerator.Dispose();
}
/// <summary>
/// 获取下一个结点。
/// </summary>
/// <returns>返回下一个结点。</returns>
public bool MoveNext()
{
return m_Enumerator.MoveNext();
}
/// <summary>
/// 重置枚举数。
/// </summary>
void IEnumerator.Reset()
{
((IEnumerator<KeyValuePair<TKey, GameFrameworkLinkedListRange<TValue>>>)m_Enumerator).Reset();
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce20200ce38864c44b02e0c1ebc7fc3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,199 @@
using System.Collections.Generic;
using System.IO;
namespace AlicizaX
{
/// <summary>
/// 游戏框架序列化器基类。
/// </summary>
/// <typeparam name="T">要序列化的数据类型。</typeparam>
public abstract class GameFrameworkSerializer<T>
{
private readonly Dictionary<byte, SerializeCallback> _serializeCallbacks;
private readonly Dictionary<byte, DeserializeCallback> _deserializeCallbacks;
private readonly Dictionary<byte, TryGetValueCallback> _tryGetValueCallbacks;
private byte _latestSerializeCallbackVersion;
/// <summary>
/// 初始化游戏框架序列化器基类的新实例。
/// </summary>
[UnityEngine.Scripting.Preserve]
public GameFrameworkSerializer()
{
_serializeCallbacks = new Dictionary<byte, SerializeCallback>();
_deserializeCallbacks = new Dictionary<byte, DeserializeCallback>();
_tryGetValueCallbacks = new Dictionary<byte, TryGetValueCallback>();
_latestSerializeCallbackVersion = 0;
}
/// <summary>
/// 序列化回调函数。
/// </summary>
/// <param name="stream">目标流。</param>
/// <param name="data">要序列化的数据。</param>
/// <returns>是否序列化数据成功。</returns>
public delegate bool SerializeCallback(Stream stream, T data);
/// <summary>
/// 反序列化回调函数。
/// </summary>
/// <param name="stream">指定流。</param>
/// <returns>反序列化的数据。</returns>
public delegate T DeserializeCallback(Stream stream);
/// <summary>
/// 尝试从指定流获取指定键的值回调函数。
/// </summary>
/// <param name="stream">指定流。</param>
/// <param name="key">指定键。</param>
/// <param name="value">指定键的值。</param>
/// <returns>是否从指定流获取指定键的值成功。</returns>
public delegate bool TryGetValueCallback(Stream stream, string key, out object value);
/// <summary>
/// 注册序列化回调函数。
/// </summary>
/// <param name="version">序列化回调函数的版本。</param>
/// <param name="callback">序列化回调函数。</param>
public void RegisterSerializeCallback(byte version, SerializeCallback callback)
{
if (callback == null)
{
throw new GameFrameworkException("Serialize callback is invalid.");
}
_serializeCallbacks[version] = callback;
if (version > _latestSerializeCallbackVersion)
{
_latestSerializeCallbackVersion = version;
}
}
/// <summary>
/// 注册反序列化回调函数。
/// </summary>
/// <param name="version">反序列化回调函数的版本。</param>
/// <param name="callback">反序列化回调函数。</param>
public void RegisterDeserializeCallback(byte version, DeserializeCallback callback)
{
if (callback == null)
{
throw new GameFrameworkException("Deserialize callback is invalid.");
}
_deserializeCallbacks[version] = callback;
}
/// <summary>
/// 注册尝试从指定流获取指定键的值回调函数。
/// </summary>
/// <param name="version">尝试从指定流获取指定键的值回调函数的版本。</param>
/// <param name="callback">尝试从指定流获取指定键的值回调函数。</param>
public void RegisterTryGetValueCallback(byte version, TryGetValueCallback callback)
{
if (callback == null)
{
throw new GameFrameworkException("Try get value callback is invalid.");
}
_tryGetValueCallbacks[version] = callback;
}
/// <summary>
/// 序列化数据到目标流中。
/// </summary>
/// <param name="stream">目标流。</param>
/// <param name="data">要序列化的数据。</param>
/// <returns>是否序列化数据成功。</returns>
public bool Serialize(Stream stream, T data)
{
if (_serializeCallbacks.Count <= 0)
{
throw new GameFrameworkException("No serialize callback registered.");
}
return Serialize(stream, data, _latestSerializeCallbackVersion);
}
/// <summary>
/// 序列化数据到目标流中。
/// </summary>
/// <param name="stream">目标流。</param>
/// <param name="data">要序列化的数据。</param>
/// <param name="version">序列化回调函数的版本。</param>
/// <returns>是否序列化数据成功。</returns>
public bool Serialize(Stream stream, T data, byte version)
{
byte[] header = GetHeader();
stream.WriteByte(header[0]);
stream.WriteByte(header[1]);
stream.WriteByte(header[2]);
stream.WriteByte(version);
if (!_serializeCallbacks.TryGetValue(version, out var callback))
{
throw new GameFrameworkException(Utility.Text.Format("Serialize callback '{0}' is not exist.", version));
}
return callback(stream, data);
}
/// <summary>
/// 从指定流反序列化数据。
/// </summary>
/// <param name="stream">指定流。</param>
/// <returns>反序列化的数据。</returns>
public T Deserialize(Stream stream)
{
byte[] header = GetHeader();
byte header0 = (byte)stream.ReadByte();
byte header1 = (byte)stream.ReadByte();
byte header2 = (byte)stream.ReadByte();
if (header0 != header[0] || header1 != header[1] || header2 != header[2])
{
throw new GameFrameworkException(Utility.Text.Format("Header is invalid, need '{0}{1}{2}', current '{3}{4}{5}'.", (char)header[0], (char)header[1], (char)header[2], (char)header0, (char)header1, (char)header2));
}
byte version = (byte)stream.ReadByte();
if (!_deserializeCallbacks.TryGetValue(version, out var callback))
{
throw new GameFrameworkException(Utility.Text.Format("Deserialize callback '{0}' is not exist.", version));
}
return callback(stream);
}
/// <summary>
/// 尝试从指定流获取指定键的值。
/// </summary>
/// <param name="stream">指定流。</param>
/// <param name="key">指定键。</param>
/// <param name="value">指定键的值。</param>
/// <returns>是否从指定流获取指定键的值成功。</returns>
public bool TryGetValue(Stream stream, string key, out object value)
{
value = null;
byte[] header = GetHeader();
byte header0 = (byte)stream.ReadByte();
byte header1 = (byte)stream.ReadByte();
byte header2 = (byte)stream.ReadByte();
if (header0 != header[0] || header1 != header[1] || header2 != header[2])
{
return false;
}
byte version = (byte)stream.ReadByte();
if (!_tryGetValueCallbacks.TryGetValue(version, out var callback))
{
return false;
}
return callback(stream, key, out value);
}
/// <summary>
/// 获取数据头标识。
/// </summary>
/// <returns>数据头标识。</returns>
protected abstract byte[] GetHeader();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2c8469a7db4474e1da10e383bc09e77f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
using UnityEngine;
namespace AlicizaX
{
/// <summary>
/// 标记物体对象为不可销毁
/// </summary>
public sealed class ObjectDontDestroyOnLoad : MonoBehaviour
{
private void Awake()
{
DontDestroyOnLoad(this);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eeb79f91247b409d97a8599f33d97808
timeCreated: 1730723224

View File

@ -0,0 +1,130 @@
using System;
using System.Collections.Generic;
using UnityEngine;
namespace AlicizaX
{
/// <summary>
/// 所有单例的统一管理器。
/// </summary>
[DefaultExecutionOrder(-9999)]
public sealed class SingletonManager : MonoBehaviour
{
private static SingletonManager _instance;
private readonly Dictionary<Type, Component> _singletons = new();
public static SingletonManager Instance
{
get
{
if (_instance == null)
{
// 查找是否已有实例
_instance = FindFirstObjectByType<SingletonManager>();
if (_instance == null)
{
var obj = new GameObject("[SingletonManagers]");
_instance = obj.AddComponent<SingletonManager>();
DontDestroyOnLoad(obj);
}
}
return _instance;
}
}
/// <summary>
/// 注册一个单例。
/// </summary>
internal void Register<T>(T instance) where T : Component
{
var type = typeof(T);
if (!_singletons.ContainsKey(type))
{
_singletons[type] = instance;
instance.transform.SetParent(transform, false);
}
}
/// <summary>
/// 注销单例。
/// </summary>
internal void Unregister<T>(T instance) where T : Component
{
var type = typeof(T);
if (_singletons.TryGetValue(type, out var current) && current == instance)
_singletons.Remove(type);
}
/// <summary>
/// 获取一个已注册的单例(若不存在则返回 null
/// </summary>
public T Get<T>() where T : Component
{
_singletons.TryGetValue(typeof(T), out var result);
return result as T;
}
}
/// <summary>
/// 泛型单例基类。
/// 自动注册到 SingletonManager。
/// 支持动态创建与销毁。
/// </summary>
public abstract class Singleton<T> : MonoBehaviour where T : Component
{
private static T _instance;
private static readonly object _lock = new();
public static T Instance
{
get
{
if (_instance != null) return _instance;
lock (_lock)
{
if (_instance != null) return _instance;
// 检查 SingletonManager 是否存在
var manager = SingletonManager.Instance;
// 查找场景中是否已经存在该类型
_instance = manager.Get<T>();
if (_instance == null)
{
var obj = new GameObject(typeof(T).Name);
_instance = obj.AddComponent<T>();
manager.Register(_instance);
}
return _instance;
}
}
}
protected virtual void Awake()
{
if (_instance == null)
{
_instance = this as T;
SingletonManager.Instance.Register(_instance);
}
else if (_instance != this)
{
Destroy(gameObject);
return;
}
}
protected virtual void OnDestroy()
{
if (_instance == this)
{
SingletonManager.Instance.Unregister(this as T);
_instance = null;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e7535f150dae4dbeb5604b01e21e92ed
timeCreated: 1760162930

View File

@ -0,0 +1,122 @@

using System;
using System.Runtime.InteropServices;
namespace AlicizaX
{
/// <summary>
/// 类型和名称的组合值。
/// </summary>
[StructLayout(LayoutKind.Auto)]
public readonly struct TypeNamePair : IEquatable<TypeNamePair>
{
private readonly Type m_Type;
private readonly string m_Name;
/// <summary>
/// 初始化类型和名称的组合值的新实例。
/// </summary>
/// <param name="type">类型。</param>
public TypeNamePair(Type type) : this(type, string.Empty)
{
}
/// <summary>
/// 初始化类型和名称的组合值的新实例。
/// </summary>
/// <param name="type">类型。</param>
/// <param name="name">名称。</param>
public TypeNamePair(Type type, string name)
{
if (type == null)
{
throw new GameFrameworkException("Type is invalid.");
}
m_Type = type;
m_Name = name ?? string.Empty;
}
/// <summary>
/// 获取类型。
/// </summary>
public Type Type
{
get { return m_Type; }
}
/// <summary>
/// 获取名称。
/// </summary>
public string Name
{
get { return m_Name; }
}
/// <summary>
/// 获取类型和名称的组合值字符串。
/// </summary>
/// <returns>类型和名称的组合值字符串。</returns>
public override string ToString()
{
if (m_Type == null)
{
throw new GameFrameworkException("Type is invalid.");
}
string typeName = m_Type.FullName;
return (string.IsNullOrEmpty(m_Name) ? typeName : Utility.Text.Format("{0}.{1}", typeName, m_Name)) ?? string.Empty;
}
/// <summary>
/// 获取对象的哈希值。
/// </summary>
/// <returns>对象的哈希值。</returns>
public override int GetHashCode()
{
return m_Type.GetHashCode() ^ m_Name.GetHashCode();
}
/// <summary>
/// 比较对象是否与自身相等。
/// </summary>
/// <param name="obj">要比较的对象。</param>
/// <returns>被比较的对象是否与自身相等。</returns>
public override bool Equals(object obj)
{
return obj is TypeNamePair pair && Equals(pair);
}
/// <summary>
/// 比较对象是否与自身相等。
/// </summary>
/// <param name="value">要比较的对象。</param>
/// <returns>被比较的对象是否与自身相等。</returns>
public bool Equals(TypeNamePair value)
{
return m_Type == value.m_Type && m_Name == value.m_Name;
}
/// <summary>
/// 判断两个对象是否相等。
/// </summary>
/// <param name="a">值 a。</param>
/// <param name="b">值 b。</param>
/// <returns>两个对象是否相等。</returns>
public static bool operator ==(TypeNamePair a, TypeNamePair b)
{
return a.Equals(b);
}
/// <summary>
/// 判断两个对象是否不相等。
/// </summary>
/// <param name="a">值 a。</param>
/// <param name="b">值 b。</param>
/// <returns>两个对象是否不相等。</returns>
public static bool operator !=(TypeNamePair a, TypeNamePair b)
{
return !(a == b);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 794a4497a986043dead37960f38aeb52
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6d2a87a6a5a0464580b7732d8291d278
timeCreated: 1736424688

View File

@ -0,0 +1,49 @@
using System;
using System.Runtime.Serialization;
namespace AlicizaX
{
/// <summary>
/// 游戏框架异常类。
/// </summary>
[Serializable]
public class GameFrameworkException : Exception
{
/// <summary>
/// 初始化游戏框架异常类的新实例。
/// </summary>
public GameFrameworkException()
: base()
{
}
/// <summary>
/// 使用指定错误消息初始化游戏框架异常类的新实例。
/// </summary>
/// <param name="message">描述错误的消息。</param>
public GameFrameworkException(string message)
: base(message)
{
}
/// <summary>
/// 使用指定错误消息和对作为此异常原因的内部异常的引用来初始化游戏框架异常类的新实例。
/// </summary>
/// <param name="message">解释异常原因的错误消息。</param>
/// <param name="innerException">导致当前异常的异常。如果 innerException 参数不为空引用,则在处理内部异常的 catch 块中引发当前异常。</param>
public GameFrameworkException(string message, Exception innerException)
: base(message, innerException)
{
}
/// <summary>
/// 用序列化数据初始化游戏框架异常类的新实例。
/// </summary>
/// <param name="info">存有有关所引发异常的序列化的对象数据。</param>
/// <param name="context">包含有关源或目标的上下文信息。</param>
protected GameFrameworkException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ae8e4ebd442c64fbdbbd8bfec15f1962
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,55 @@
using System;
namespace AlicizaX
{
/// <summary>
/// 游戏框架异常静态方法
/// </summary>
public static class GameFrameworkGuard
{
/// <summary>
/// 确保指定的值不为null。
/// </summary>
/// <param name="value">要检查的值。</param>
/// <param name="name">值的名称。</param>
/// <exception cref="ArgumentNullException">当值为null时引发。</exception>
public static void NotNullOrEmpty(string value, string name)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentNullException(name, " can not be null.");
}
}
/// <summary>
/// 确保指定的值不为null。
/// </summary>
/// <typeparam name="T">值的类型。</typeparam>
/// <param name="value">要检查的值。</param>
/// <param name="name">值的名称。</param>
/// <exception cref="ArgumentNullException">当值为null时引发。</exception>
public static void NotNull<T>(T value, string name) where T : class
{
if (value == null)
{
throw new ArgumentNullException(name, " can not be null.");
}
}
/// <summary>
/// 检查值是否在指定范围内,如果不在范围内则抛出 ArgumentOutOfRangeException 异常。
/// </summary>
/// <param name="value">要检查的值。</param>
/// <param name="min">允许的最小值。</param>
/// <param name="max">允许的最大值。</param>
/// <param name="name">值的名称。</param>
/// <exception cref="ArgumentOutOfRangeException">当值不在指定范围内时抛出。</exception>
public static void NotRange(int value, int min, int max, string name)
{
if (value > max || value < min)
{
throw new ArgumentOutOfRangeException(name, "value must between " + min + " and " + max);
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ca46f6f46a474facaebe8c44a049485c
timeCreated: 1702542639

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 68c2b9f042834f45893675afb0ba2ecf
timeCreated: 1742458629

View File

@ -0,0 +1,13 @@
namespace AlicizaX
{
/// <summary>
/// 内存对象Interface。
/// </summary>
public interface IMemory
{
/// <summary>
/// 清理内存对象回收入池。
/// </summary>
void Clear();
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 29d6300317e70b24381120ac8f5b0a92
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
namespace AlicizaX
{
public static partial class MemoryPool
{
/// <summary>
/// 内存池收集器。
/// </summary>
private sealed class MemoryCollection
{
private readonly Queue<IMemory> _memories;
private readonly Type _memoryType;
private int _usingMemoryCount;
private int _acquireMemoryCount;
private int _releaseMemoryCount;
private int _addMemoryCount;
private int _removeMemoryCount;
public MemoryCollection(Type memoryType)
{
_memories = new Queue<IMemory>();
_memoryType = memoryType;
_usingMemoryCount = 0;
_acquireMemoryCount = 0;
_releaseMemoryCount = 0;
_addMemoryCount = 0;
_removeMemoryCount = 0;
}
public Type MemoryType => _memoryType;
public int UnusedMemoryCount => _memories.Count;
public int UsingMemoryCount => _usingMemoryCount;
public int AcquireMemoryCount => _acquireMemoryCount;
public int ReleaseMemoryCount => _releaseMemoryCount;
public int AddMemoryCount => _addMemoryCount;
public int RemoveMemoryCount => _removeMemoryCount;
public T Acquire<T>() where T : class, IMemory, new()
{
if (typeof(T) != _memoryType)
{
throw new Exception("Type is invalid.");
}
_usingMemoryCount++;
_acquireMemoryCount++;
lock (_memories)
{
if (_memories.Count > 0)
{
return (T)_memories.Dequeue();
}
}
_addMemoryCount++;
return new T();
}
public IMemory Acquire()
{
_usingMemoryCount++;
_acquireMemoryCount++;
lock (_memories)
{
if (_memories.Count > 0)
{
return _memories.Dequeue();
}
}
_addMemoryCount++;
return (IMemory)Activator.CreateInstance(_memoryType);
}
public void Release(IMemory memory)
{
memory.Clear();
lock (_memories)
{
if (_enableStrictCheck && _memories.Contains(memory))
{
throw new Exception("The memory has been released.");
}
_memories.Enqueue(memory);
}
_releaseMemoryCount++;
_usingMemoryCount--;
}
public void Add<T>(int count) where T : class, IMemory, new()
{
if (typeof(T) != _memoryType)
{
throw new Exception("Type is invalid.");
}
lock (_memories)
{
_addMemoryCount += count;
while (count-- > 0)
{
_memories.Enqueue(new T());
}
}
}
public void Add(int count)
{
lock (_memories)
{
_addMemoryCount += count;
while (count-- > 0)
{
_memories.Enqueue((IMemory)Activator.CreateInstance(_memoryType));
}
}
}
public void Remove(int count)
{
lock (_memories)
{
if (count > _memories.Count)
{
count = _memories.Count;
}
_removeMemoryCount += count;
while (count-- > 0)
{
_memories.Dequeue();
}
}
}
public void RemoveAll()
{
lock (_memories)
{
_removeMemoryCount += _memories.Count;
_memories.Clear();
}
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e4ec5f33991c55f41bceefbdb8089ae2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
namespace AlicizaX
{
/// <summary>
/// 内存池。
/// </summary>
public static partial class MemoryPool
{
private static readonly Dictionary<Type, MemoryCollection> _memoryCollections = new Dictionary<Type, MemoryCollection>();
private static bool _enableStrictCheck = false;
/// <summary>
/// 获取或设置是否开启强制检查。
/// </summary>
public static bool EnableStrictCheck
{
get => _enableStrictCheck;
set => _enableStrictCheck = value;
}
/// <summary>
/// 获取内存池的数量。
/// </summary>
// ReSharper disable once InconsistentlySynchronizedField
public static int Count => _memoryCollections.Count;
/// <summary>
/// 获取所有内存池的信息。
/// </summary>
/// <returns>所有内存池的信息。</returns>
public static MemoryPoolInfo[] GetAllMemoryPoolInfos()
{
int index = 0;
MemoryPoolInfo[] results = null;
lock (_memoryCollections)
{
results = new MemoryPoolInfo[_memoryCollections.Count];
foreach (KeyValuePair<Type, MemoryCollection> memoryCollection in _memoryCollections)
{
results[index++] = new MemoryPoolInfo(memoryCollection.Key, memoryCollection.Value.UnusedMemoryCount, memoryCollection.Value.UsingMemoryCount, memoryCollection.Value.AcquireMemoryCount, memoryCollection.Value.ReleaseMemoryCount, memoryCollection.Value.AddMemoryCount, memoryCollection.Value.RemoveMemoryCount);
}
}
return results;
}
/// <summary>
/// 清除所有内存池。
/// </summary>
public static void ClearAll()
{
lock (_memoryCollections)
{
foreach (KeyValuePair<Type, MemoryCollection> memoryCollection in _memoryCollections)
{
memoryCollection.Value.RemoveAll();
}
_memoryCollections.Clear();
}
}
/// <summary>
/// 从内存池获取内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <returns>内存对象。</returns>
public static T Acquire<T>() where T : class, IMemory, new()
{
return GetMemoryCollection(typeof(T)).Acquire<T>();
}
/// <summary>
/// 从内存池获取内存对象。
/// </summary>
/// <param name="memoryType">内存对象类型。</param>
/// <returns>内存对象。</returns>
public static IMemory Acquire(Type memoryType)
{
InternalCheckMemoryType(memoryType);
return GetMemoryCollection(memoryType).Acquire();
}
/// <summary>
/// 将内存对象归还内存池。
/// </summary>
/// <param name="memory">内存对象。</param>
public static void Release(IMemory memory)
{
if (memory == null)
{
throw new Exception("Memory is invalid.");
}
Type memoryType = memory.GetType();
InternalCheckMemoryType(memoryType);
GetMemoryCollection(memoryType).Release(memory);
}
/// <summary>
/// 向内存池中追加指定数量的内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <param name="count">追加数量。</param>
public static void Add<T>(int count) where T : class, IMemory, new()
{
GetMemoryCollection(typeof(T)).Add<T>(count);
}
/// <summary>
/// 向内存池中追加指定数量的内存对象。
/// </summary>
/// <param name="memoryType">内存对象类型。</param>
/// <param name="count">追加数量。</param>
public static void Add(Type memoryType, int count)
{
InternalCheckMemoryType(memoryType);
GetMemoryCollection(memoryType).Add(count);
}
/// <summary>
/// 从内存池中移除指定数量的内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <param name="count">移除数量。</param>
public static void Remove<T>(int count) where T : class, IMemory
{
GetMemoryCollection(typeof(T)).Remove(count);
}
/// <summary>
/// 从内存池中移除指定数量的内存对象。
/// </summary>
/// <param name="memoryType">内存对象类型。</param>
/// <param name="count">移除数量。</param>
public static void Remove(Type memoryType, int count)
{
InternalCheckMemoryType(memoryType);
GetMemoryCollection(memoryType).Remove(count);
}
/// <summary>
/// 从内存池中移除所有的内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
public static void RemoveAll<T>() where T : class, IMemory
{
GetMemoryCollection(typeof(T)).RemoveAll();
}
/// <summary>
/// 从内存池中移除所有的内存对象。
/// </summary>
/// <param name="memoryType">内存对象类型。</param>
public static void RemoveAll(Type memoryType)
{
InternalCheckMemoryType(memoryType);
GetMemoryCollection(memoryType).RemoveAll();
}
private static void InternalCheckMemoryType(Type memoryType)
{
if (!_enableStrictCheck)
{
return;
}
if (memoryType == null)
{
throw new Exception("Memory type is invalid.");
}
if (!memoryType.IsClass || memoryType.IsAbstract)
{
throw new Exception("Memory type is not a non-abstract class type.");
}
if (!typeof(IMemory).IsAssignableFrom(memoryType))
{
throw new Exception(string.Format("Memory type '{0}' is invalid.", memoryType.FullName));
}
}
private static MemoryCollection GetMemoryCollection(Type memoryType)
{
if (memoryType == null)
{
throw new Exception("MemoryType is invalid.");
}
MemoryCollection memoryCollection = null;
lock (_memoryCollections)
{
if (!_memoryCollections.TryGetValue(memoryType, out memoryCollection))
{
memoryCollection = new MemoryCollection(memoryType);
_memoryCollections.Add(memoryType, memoryCollection);
}
}
return memoryCollection;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f253bd3093bf78d45ad11b73c134e9ff
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,57 @@
using System;
namespace AlicizaX
{
/// <summary>
/// 内存池对象基类。
/// </summary>
public abstract class MemoryObject : IMemory
{
/// <summary>
/// 清理内存对象回收入池。
/// </summary>
public virtual void Clear()
{
}
/// <summary>
/// 从内存池中初始化。
/// </summary>
public abstract void InitFromPool();
/// <summary>
/// 回收到内存池。
/// </summary>
public abstract void RecycleToPool();
}
public static partial class MemoryPool
{
/// <summary>
/// 从内存池获取内存对象。
/// </summary>
/// <typeparam name="T">内存对象类型。</typeparam>
/// <returns>内存对象。</returns>
public static T Alloc<T>() where T : MemoryObject, new()
{
T memory = Acquire<T>();
memory.InitFromPool();
return memory;
}
/// <summary>
/// 将内存对象归还内存池。
/// </summary>
/// <param name="memory">内存对象。</param>
public static void Dealloc(MemoryObject memory)
{
if (memory == null)
{
throw new Exception("Memory is invalid.");
}
memory.RecycleToPool();
Release(memory);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ad13ef73c22340058c4420733a22b580
timeCreated: 1701273442

View File

@ -0,0 +1,118 @@
using System;
using System.Runtime.InteropServices;
namespace AlicizaX
{
/// <summary>
/// 内存池信息。
/// </summary>
[StructLayout(LayoutKind.Auto)]
public struct MemoryPoolInfo
{
private readonly Type _type;
private readonly int _unusedMemoryCount;
private readonly int _usingMemoryCount;
private readonly int _acquireMemoryCount;
private readonly int _releaseMemoryCount;
private readonly int _addMemoryCount;
private readonly int _removeMemoryCount;
/// <summary>
/// 初始化内存池信息的新实例。
/// </summary>
/// <param name="type">内存池类型。</param>
/// <param name="unusedMemoryCount">未使用内存对象数量。</param>
/// <param name="usingMemoryCount">正在使用内存对象数量。</param>
/// <param name="acquireMemoryCount">获取内存对象数量。</param>
/// <param name="releaseMemoryCount">归还内存对象数量。</param>
/// <param name="addMemoryCount">增加内存对象数量。</param>
/// <param name="removeMemoryCount">移除内存对象数量。</param>
public MemoryPoolInfo(Type type, int unusedMemoryCount, int usingMemoryCount, int acquireMemoryCount, int releaseMemoryCount, int addMemoryCount, int removeMemoryCount)
{
_type = type;
_unusedMemoryCount = unusedMemoryCount;
_usingMemoryCount = usingMemoryCount;
_acquireMemoryCount = acquireMemoryCount;
_releaseMemoryCount = releaseMemoryCount;
_addMemoryCount = addMemoryCount;
_removeMemoryCount = removeMemoryCount;
}
/// <summary>
/// 获取内存池类型。
/// </summary>
public Type Type
{
get
{
return _type;
}
}
/// <summary>
/// 获取未使用内存对象数量。
/// </summary>
public int UnusedMemoryCount
{
get
{
return _unusedMemoryCount;
}
}
/// <summary>
/// 获取正在使用内存对象数量。
/// </summary>
public int UsingMemoryCount
{
get
{
return _usingMemoryCount;
}
}
/// <summary>
/// 获取获取内存对象数量。
/// </summary>
public int AcquireMemoryCount
{
get
{
return _acquireMemoryCount;
}
}
/// <summary>
/// 获取归还内存对象数量。
/// </summary>
public int ReleaseMemoryCount
{
get
{
return _releaseMemoryCount;
}
}
/// <summary>
/// 获取增加内存对象数量。
/// </summary>
public int AddMemoryCount
{
get
{
return _addMemoryCount;
}
}
/// <summary>
/// 获取移除内存对象数量。
/// </summary>
public int RemoveMemoryCount
{
get
{
return _removeMemoryCount;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 444aa83c59c8f0445894e785975b5463
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,78 @@
using UnityEngine;
namespace AlicizaX
{
/// <summary>
/// 内存强制检查类型。
/// </summary>
public enum MemoryStrictCheckType : byte
{
/// <summary>
/// 总是启用。
/// </summary>
AlwaysEnable = 0,
/// <summary>
/// 仅在开发模式时启用。
/// </summary>
OnlyEnableWhenDevelopment,
/// <summary>
/// 仅在编辑器中启用。
/// </summary>
OnlyEnableInEditor,
/// <summary>
/// 总是禁用。
/// </summary>
AlwaysDisable,
}
/// <summary>
/// 内存池模块。
/// </summary>
[DisallowMultipleComponent]
public sealed class MemoryPoolSetting : MonoBehaviour
{
[SerializeField]
private MemoryStrictCheckType m_EnableStrictCheck = MemoryStrictCheckType.OnlyEnableWhenDevelopment;
/// <summary>
/// 获取或设置是否开启强制检查。
/// </summary>
public bool EnableStrictCheck
{
get => MemoryPool.EnableStrictCheck;
set
{
MemoryPool.EnableStrictCheck = value;
if (value)
{
Log.Info("Strict checking is enabled for the Memory Pool. It will drastically affect the performance.");
}
}
}
private void Start()
{
switch (m_EnableStrictCheck)
{
case MemoryStrictCheckType.AlwaysEnable:
EnableStrictCheck = true;
break;
case MemoryStrictCheckType.OnlyEnableWhenDevelopment:
EnableStrictCheck = Debug.isDebugBuild;
break;
case MemoryStrictCheckType.OnlyEnableInEditor:
EnableStrictCheck = Application.isEditor;
break;
default:
EnableStrictCheck = false;
break;
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 61474d279eb27214d9178822796f3b88
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: aeb86cc258fa4378ba3b259a5d38e4df
timeCreated: 1736424998

View File

@ -0,0 +1,42 @@
namespace AlicizaX
{
public interface IModule
{
protected internal void Dispose();
}
public interface IModuleAwake
{
protected internal void Awake();
}
public interface IExecuteSystem
{
abstract int Priority { get; }
}
public interface IModuleUpdate : IExecuteSystem
{
protected internal void Update(float elapseSeconds, float realElapseSeconds);
}
public interface IModuleLateUpdate : IExecuteSystem
{
protected internal void LateUpdate();
}
public interface IModuleFixedUpdate : IExecuteSystem
{
protected internal void FixedUpdate();
}
public interface IModuleDrawGizmos : IExecuteSystem
{
protected internal void DrawGizmos();
}
public interface IModuleGUI : IExecuteSystem
{
protected internal void OnGUI();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0ba192d5e9084d67b16b53e843e103b3
timeCreated: 1736424999

View File

@ -0,0 +1,303 @@
using System;
using System.Buffers;
using System.Collections.Generic;
namespace AlicizaX
{
public static class ModuleSystem
{
internal const int DesignModuleCount = 32;
private static readonly Dictionary<Type, IModule> _moduleMaps = new Dictionary<Type, IModule>(DesignModuleCount);
private static readonly GameFrameworkLinkedList<IModule> _modules = new GameFrameworkLinkedList<IModule>();
// Update systems
private static readonly GameFrameworkLinkedList<IModuleUpdate> _updateModules = new GameFrameworkLinkedList<IModuleUpdate>();
private static readonly IModuleUpdate[] _updateExecuteArray = new IModuleUpdate[DesignModuleCount];
private static int _updateExecuteCount;
// LateUpdate systems
private static readonly GameFrameworkLinkedList<IModuleLateUpdate> _lateUpdateModules = new GameFrameworkLinkedList<IModuleLateUpdate>();
private static readonly IModuleLateUpdate[] _lateUpdateExecuteArray = new IModuleLateUpdate[DesignModuleCount];
private static int _lateUpdateExecuteCount;
// FixedUpdate systems
private static readonly GameFrameworkLinkedList<IModuleFixedUpdate> _fixedUpdateModules = new GameFrameworkLinkedList<IModuleFixedUpdate>();
private static readonly IModuleFixedUpdate[] _fixedUpdateExecuteArray = new IModuleFixedUpdate[DesignModuleCount];
private static int _fixedUpdateExecuteCount;
// Gizmos systems
private static readonly GameFrameworkLinkedList<IModuleDrawGizmos> _gizmosUpdateModules = new GameFrameworkLinkedList<IModuleDrawGizmos>();
private static readonly IModuleDrawGizmos[] _gizmosUpdateExecuteArray = new IModuleDrawGizmos[DesignModuleCount];
private static int _gizmosExecuteCount;
// GUI systems
private static readonly GameFrameworkLinkedList<IModuleGUI> _guiUpdateModules = new GameFrameworkLinkedList<IModuleGUI>();
private static readonly IModuleGUI[] _guiUpdateExecuteArray = new IModuleGUI[DesignModuleCount];
private static int _guiExecuteCount;
// Dirty flags
private static bool _isExecuteListDirty;
private static bool _isLateExecuteListDirty;
private static bool _isFixedExecuteListDirty;
private static bool _isGizmosExecuteListDirty;
private static bool _isGUIExecuteListDirty;
internal static void Dispose()
{
// Clear all modules and execute arrays
_updateModules.Clear();
Array.Clear(_updateExecuteArray, 0, _updateExecuteArray.Length);
_updateExecuteCount = 0;
_lateUpdateModules.Clear();
Array.Clear(_lateUpdateExecuteArray, 0, _lateUpdateExecuteArray.Length);
_lateUpdateExecuteCount = 0;
_fixedUpdateModules.Clear();
Array.Clear(_fixedUpdateExecuteArray, 0, _fixedUpdateExecuteArray.Length);
_fixedUpdateExecuteCount = 0;
_gizmosUpdateModules.Clear();
Array.Clear(_gizmosUpdateExecuteArray, 0, _gizmosUpdateExecuteArray.Length);
_gizmosExecuteCount = 0;
_guiUpdateModules.Clear();
Array.Clear(_guiUpdateExecuteArray, 0, _guiUpdateExecuteArray.Length);
_guiExecuteCount = 0;
// Dispose all modules
for (var current = _modules.Last; current != null; current = current.Previous)
{
current.Value.Dispose();
}
_modules.Clear();
_moduleMaps.Clear();
}
#region Public System
public static T RegisterModule<T, TImple>() where T : IModule where TImple : class, T, new()
{
Type interfaceType = typeof(T);
Type implType = typeof(TImple);
if (!interfaceType.IsInterface)
{
throw new GameFrameworkException(Utility.Text.Format("You must register module by interface, but '{0}' is not.", interfaceType.FullName));
}
if (!implType.IsClass || implType.IsInterface || implType.IsAbstract)
{
throw new GameFrameworkException(Utility.Text.Format("You must register module by Class and not Interface and Abstract, but '{0}' is not.", implType.FullName));
}
if (!typeof(IModule).IsAssignableFrom(interfaceType))
{
throw new GameFrameworkException(Utility.Text.Format("Module must implement IModule.", interfaceType.FullName));
}
if (GetModule(interfaceType) != null)
{
Log.Error("Already Register {0}", interfaceType.FullName);
return default;
}
TImple impl = new TImple();
return (T)SetModuleInstance(interfaceType, impl);
}
public static T GetModule<T>() where T : class
{
Type interfaceType = typeof(T);
if (!interfaceType.IsInterface)
{
throw new GameFrameworkException(Utility.Text.Format("You must get module by interface, but '{0}' is not.", interfaceType.FullName));
}
return GetModule(interfaceType) as T;
}
public static IModule GetModule(Type moduleType)
{
return _moduleMaps.TryGetValue(moduleType, out var ret) ? ret : default;
}
private static IModule SetModuleInstance<TImpl>(Type interfaceType, TImpl impl) where TImpl : class, IModule
{
_moduleMaps[interfaceType] = impl;
_modules.AddLast(impl);
if (impl is IModuleAwake awakeSystem)
{
awakeSystem.Awake();
}
CheckSystemLife(impl);
return impl;
}
#endregion
private static void CheckSystemLife(IModule module)
{
if (module is IModuleUpdate updateSystem)
{
BindSystemLife(updateSystem, _updateModules, ref _isExecuteListDirty);
}
if (module is IModuleLateUpdate lateUpdate)
{
BindSystemLife(lateUpdate, _lateUpdateModules, ref _isLateExecuteListDirty);
}
if (module is IModuleFixedUpdate fixedUpdate)
{
BindSystemLife(fixedUpdate, _fixedUpdateModules, ref _isFixedExecuteListDirty);
}
if (module is IModuleDrawGizmos drawGizmosUpdate)
{
BindSystemLife(drawGizmosUpdate, _gizmosUpdateModules, ref _isGizmosExecuteListDirty);
}
if (module is IModuleGUI guiUpdate)
{
BindSystemLife(guiUpdate, _guiUpdateModules, ref _isGUIExecuteListDirty);
}
}
private static void BindSystemLife<T>(T system, GameFrameworkLinkedList<T> updateModule, ref bool executeDirty) where T : IExecuteSystem
{
var current = updateModule.First;
while (current != null && system.Priority <= current.Value.Priority)
{
current = current.Next;
}
if (current != null)
{
updateModule.AddBefore(current, system);
}
else
{
updateModule.AddLast(system);
}
executeDirty = true;
}
#region BuildExecuteList
private static void BuildExecuteList()
{
if (!_isExecuteListDirty) return;
_isExecuteListDirty = false;
_updateExecuteCount = 0;
foreach (var module in _updateModules)
{
if (_updateExecuteCount >= _updateExecuteArray.Length) break;
_updateExecuteArray[_updateExecuteCount++] = module;
}
}
private static void BuildLateExecuteList()
{
if (!_isLateExecuteListDirty) return;
_isLateExecuteListDirty = false;
_lateUpdateExecuteCount = 0;
foreach (var module in _lateUpdateModules)
{
if (_lateUpdateExecuteCount >= _lateUpdateExecuteArray.Length) break;
_lateUpdateExecuteArray[_lateUpdateExecuteCount++] = module;
}
}
private static void BuildFixedExecuteList()
{
if (!_isFixedExecuteListDirty) return;
_isFixedExecuteListDirty = false;
_fixedUpdateExecuteCount = 0;
foreach (var module in _fixedUpdateModules)
{
if (_fixedUpdateExecuteCount >= _fixedUpdateExecuteArray.Length) break;
_fixedUpdateExecuteArray[_fixedUpdateExecuteCount++] = module;
}
}
private static void BuildGizmosExecuteList()
{
if (!_isGizmosExecuteListDirty) return;
_isGizmosExecuteListDirty = false;
_gizmosExecuteCount = 0;
foreach (var module in _gizmosUpdateModules)
{
if (_gizmosExecuteCount >= _gizmosUpdateExecuteArray.Length) break;
_gizmosUpdateExecuteArray[_gizmosExecuteCount++] = module;
}
}
private static void BuildGUIExecuteList()
{
if (!_isGUIExecuteListDirty) return;
_isGUIExecuteListDirty = false;
_guiExecuteCount = 0;
foreach (var module in _guiUpdateModules)
{
if (_guiExecuteCount >= _guiUpdateExecuteArray.Length) break;
_guiUpdateExecuteArray[_guiExecuteCount++] = module;
}
}
#endregion
#region RunExecuteList
internal static void UpdateExecuteList(float elapseSeconds, float realElapseSeconds)
{
BuildExecuteList();
for (int i = 0; i < _updateExecuteCount; i++)
{
_updateExecuteArray[i].Update(elapseSeconds, realElapseSeconds);
}
}
internal static void UpdateLateExecuteList()
{
BuildLateExecuteList();
for (int i = 0; i < _lateUpdateExecuteCount; i++)
{
_lateUpdateExecuteArray[i].LateUpdate();
}
}
internal static void UpdateFixedExecuteList()
{
BuildFixedExecuteList();
for (int i = 0; i < _fixedUpdateExecuteCount; i++)
{
_fixedUpdateExecuteArray[i].FixedUpdate();
}
}
internal static void UpdateGizmosExecuteList()
{
BuildGizmosExecuteList();
for (int i = 0; i < _gizmosExecuteCount; i++)
{
_gizmosUpdateExecuteArray[i].DrawGizmos();
}
}
internal static void UpdateGUIExecuteList()
{
BuildGUIExecuteList();
for (int i = 0; i < _guiExecuteCount; i++)
{
_guiUpdateExecuteArray[i].OnGUI();
}
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b9fa1d3abb954b57989b94b4c77e45ce
timeCreated: 1736928479

View File

@ -0,0 +1,220 @@
using System;
using AlicizaX.ObjectPool;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace AlicizaX
{
/// <summary>
/// 基础组件。
/// </summary>
[DisallowMultipleComponent]
[UnityEngine.Scripting.Preserve]
public sealed class RootModule : MonoBehaviour
{
private static RootModule _instance = null;
public static RootModule Instance
{
get
{
if (_instance == null)
{
_instance = FindObjectOfType<RootModule>();
}
return _instance;
}
}
private const int DEFAULT_DPI = 96;
private float _gameSpeedBeforePause = 1f;
[SerializeField] private int frameRate = 120;
[SerializeField] private float gameSpeed = 1f;
[SerializeField] private bool runInBackground = true;
[SerializeField] private bool neverSleep = true;
/// <summary>
/// 获取或设置游戏帧率。
/// </summary>
public int FrameRate
{
get => frameRate;
set => Application.targetFrameRate = frameRate = value;
}
/// <summary>
/// 获取或设置游戏速度。
/// </summary>
public float GameSpeed
{
get => gameSpeed;
set => Time.timeScale = gameSpeed = value >= 0f ? value : 0f;
}
/// <summary>
/// 获取游戏是否暂停。
/// </summary>
public bool IsGamePaused => gameSpeed <= 0f;
/// <summary>
/// 获取是否正常游戏速度。
/// </summary>
public bool IsNormalGameSpeed => Math.Abs(gameSpeed - 1f) < 0.01f;
/// <summary>
/// 获取或设置是否允许后台运行。
/// </summary>
public bool RunInBackground
{
get => runInBackground;
set => Application.runInBackground = runInBackground = value;
}
/// <summary>
/// 获取或设置是否禁止休眠。
/// </summary>
public bool NeverSleep
{
get => neverSleep;
set
{
neverSleep = value;
Screen.sleepTimeout = value ? SleepTimeout.NeverSleep : SleepTimeout.SystemSetting;
}
}
/// <summary>
/// 暂停游戏。
/// </summary>
public void PauseGame()
{
if (IsGamePaused)
{
return;
}
_gameSpeedBeforePause = GameSpeed;
GameSpeed = 0f;
}
/// <summary>
/// 恢复游戏。
/// </summary>
public void ResumeGame()
{
if (!IsGamePaused)
{
return;
}
GameSpeed = _gameSpeedBeforePause;
}
/// <summary>
/// 重置为正常游戏速度。
/// </summary>
public void ResetNormalGameSpeed()
{
if (IsNormalGameSpeed)
{
return;
}
GameSpeed = 1f;
}
/// <summary>
/// 游戏框架组件初始化。
/// </summary>
private void Awake()
{
_instance = this;
DontDestroyOnLoad(this);
Utility.Unity.MakeEntity(transform);
Log.Init();
Log.Info("Game Version: {0}, Unity Version: {1}", AppVersion.GameVersion, Application.unityVersion);
Utility.Converter.ScreenDpi = Screen.dpi;
if (Utility.Converter.ScreenDpi <= 0)
{
Utility.Converter.ScreenDpi = DEFAULT_DPI;
}
Application.targetFrameRate = frameRate;
Time.timeScale = gameSpeed;
Application.runInBackground = runInBackground;
Screen.sleepTimeout = neverSleep ? SleepTimeout.NeverSleep : SleepTimeout.SystemSetting;
Application.lowMemory += OnLowMemory;
}
private void OnApplicationQuit()
{
Application.lowMemory -= OnLowMemory;
StopAllCoroutines();
Shutdown();
}
internal void Shutdown()
{
Destroy(gameObject);
Utility.Unity.Shutdown();
MemoryPool.ClearAll();
Utility.Marshal.FreeCachedHGlobal();
}
private void Update()
{
ModuleSystem.UpdateExecuteList(Time.deltaTime, Time.unscaledDeltaTime);
}
private void LateUpdate()
{
ModuleSystem.UpdateLateExecuteList();
}
private void FixedUpdate()
{
ModuleSystem.UpdateFixedExecuteList();
}
private void OnDrawGizmos()
{
ModuleSystem.UpdateGizmosExecuteList();
}
private void OnGUI()
{
ModuleSystem.UpdateGUIExecuteList();
}
private async void OnDestroy()
{
await UniTask.Yield();
ModuleSystem.Dispose();
}
private void OnLowMemory()
{
Log.Warning("Low memory reported...");
IObjectPoolModule objectPoolModule = ModuleSystem.GetModule<IObjectPoolModule>();
if (objectPoolModule != null)
{
objectPoolModule.ReleaseAllUnused();
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 252fa1bb9e36411fb4582d0656b987bf
timeCreated: 1737362886

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e3e833a043cfd47c1ba1b3fbff22109c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,28 @@
using UnityEngine;
namespace AlicizaX
{
/// <summary>
/// 版本号类。
/// </summary>
public static partial class AppVersion
{
private const string GameFrameworkVersionString = "1.0.0";
/// <summary>
/// 获取游戏框架版本号。
/// </summary>
public static string GameFrameworkVersion
{
get { return GameFrameworkVersionString; }
}
/// <summary>
/// 获取游戏版本号。
/// </summary>
public static string GameVersion
{
get { return Application.version; }
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9d3aeaaff9b2447338611e06c6eca6f6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

3
Runtime/ABase/Event.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 421689177e8f4c58ae0ce91e5ea9f9c3
timeCreated: 1736415505

View File

@ -0,0 +1,36 @@
using System;
using System.Runtime.CompilerServices;
using Unity.IL2CPP.CompilerServices;
namespace AlicizaX
{
public interface IEventArgs { }
[AttributeUsage(AttributeTargets.Struct)]
public sealed class PrewarmAttribute : Attribute
{
public int Capacity { get; }
public PrewarmAttribute(int capacity) => Capacity = capacity;
}
[Il2CppSetOption(Option.NullChecks, false)]
[Il2CppSetOption(Option.DivideByZeroChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
public readonly struct EventRuntimeHandle
{
private readonly Action<int,int> _unsubscribe;
private readonly int _index;
private readonly int _version;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EventRuntimeHandle(Action<int,int> unsubscribe, int index, int version)
{
_unsubscribe = unsubscribe;
_index = index;
_version = version;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() => _unsubscribe?.Invoke(_index, _version);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d85feee75c2d4b54bd6b593fe55145d7
timeCreated: 1740488369

View File

@ -0,0 +1,74 @@
using System;
namespace Unity.IL2CPP.CompilerServices
{
/// <summary>
/// The code generation options available for IL to C++ conversion.
/// Enable or disabled these with caution.
/// </summary>
public enum Option
{
/// <summary>
/// Enable or disable code generation for null checks.
///
/// Global null check support is enabled by default when il2cpp.exe
/// is launched from the Unity editor.
///
/// Disabling this will prevent NullReferenceException exceptions from
/// being thrown in generated code. In *most* cases, code that dereferences
/// a null pointer will crash then. Sometimes the point where the crash
/// happens is later than the location where the null reference check would
/// have been emitted though.
/// </summary>
NullChecks = 1,
/// <summary>
/// Enable or disable code generation for array bounds checks.
///
/// Global array bounds check support is enabled by default when il2cpp.exe
/// is launched from the Unity editor.
///
/// Disabling this will prevent IndexOutOfRangeException exceptions from
/// being thrown in generated code. This will allow reading and writing to
/// memory outside of the bounds of an array without any runtime checks.
/// Disable this check with extreme caution.
/// </summary>
ArrayBoundsChecks = 2,
/// <summary>
/// Enable or disable code generation for divide by zero checks.
///
/// Global divide by zero check support is disabled by default when il2cpp.exe
/// is launched from the Unity editor.
///
/// Enabling this will cause DivideByZeroException exceptions to be
/// thrown in generated code. Most code doesn't need to handle this
/// exception, so it is probably safe to leave it disabled.
/// </summary>
DivideByZeroChecks = 3,
}
/// <summary>
/// Use this attribute on an assembly, struct, class, method, or property to inform the IL2CPP code conversion utility to override the
/// global setting for one of a few different runtime checks.
///
/// Example:
///
/// [Il2CppSetOption(Option.NullChecks, false)]
/// public static string MethodWithNullChecksDisabled()
/// {
/// var tmp = new Object();
/// return tmp.ToString();
/// }
/// </summary>
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate, Inherited = false, AllowMultiple = true)]
public class Il2CppSetOptionAttribute : Attribute
{
public Option Option { get; private set; }
public object Value { get; private set; }
public Il2CppSetOptionAttribute(Option option, object value)
{
Option = option;
Value = value;
}
}
}

View File

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 997668ad76a5387428e679240d659155

Some files were not shown because too many files have changed in this diff Show More