This commit is contained in:
陈思海 2025-02-07 16:04:12 +08:00
commit bf0d8340af
403 changed files with 28109 additions and 0 deletions

8
Editor.meta Normal file
View File

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

View File

@ -0,0 +1,19 @@
{
"name": "AlicizaX.Editor",
"rootNamespace": "",
"references": [
"GUID:75b6f2078d190f14dbda4a5b747d709c",
"GUID:2373f786d14518f44b0f475db77ba4de"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: acfef7cabed3b0a42b25edb1cd4fa259
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

3
Editor/Constant.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1478a6e22f3d4f5e8ff645e5272c8b34
timeCreated: 1736931429

View File

@ -0,0 +1,15 @@
using System;
namespace AlicizaX.Editor
{
[AttributeUsage(AttributeTargets.Class)]
public class ComponentTargetAttribute : Attribute
{
public Type Type;
public ComponentTargetAttribute(Type type)
{
Type = type;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 27f61e6762d146a0a331a670a8c203fc
timeCreated: 1737090681

View File

@ -0,0 +1,16 @@
using System;
namespace AlicizaX.Editor
{
[AttributeUsage(AttributeTargets.Class)]
public class DisplayNameAttribute:Attribute
{
public string DisplayName;
public DisplayNameAttribute(string displayName)
{
DisplayName = displayName;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 15a53446355a45549c7060c581d194ac
timeCreated: 1737013039

3
Editor/EditorWindow.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 99e99734386545c192a5493105e66c77
timeCreated: 1737524069

View File

@ -0,0 +1,96 @@
using UnityEditor;
using UnityEngine;
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;
using AlicizaX.Runtime;
using Sirenix.OdinInspector.Editor;
namespace AlicizaX.Editor
{
public class GameFrameworkPreferenceWindow : OdinMenuEditorWindow
{
private List<Type> m_TabTypes;
private readonly Dictionary<Type, GameFrameworkTabBase> _tabBases = new();
[MenuItem("AlicizaX/Preference Window")]
public static void ShowWindow()
{
GetWindow<GameFrameworkPreferenceWindow>(true, "Preference Window");
}
private void OnEnable()
{
this.position = new Rect(this.position.x, this.position.y, 800, 600);
var assemblies = Utility.Assembly.GetAssemblies();
m_TabTypes = new List<Type>();
foreach (var assembly in assemblies)
{
var types = assembly.GetTypes();
foreach (var type in types)
{
if (typeof(GameFrameworkTabBase).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
{
var displayNameAttr = (DisplayNameAttribute)Attribute.GetCustomAttribute(type, typeof(DisplayNameAttribute));
if (displayNameAttr != null)
{
m_TabTypes.Add(type);
}
}
}
}
m_TabTypes = m_TabTypes.OrderBy(t => ((DisplayNameAttribute)Attribute.GetCustomAttribute(t, typeof(DisplayNameAttribute))).DisplayName).ToList();
}
protected override void OnDestroy()
{
base.OnDestroy();
if (this.MenuTree != null && this.MenuTree.Selection.SelectedValue != null)
{
var selectTarget = this.MenuTree.Selection.SelectedValue;
if (selectTarget != null)
{
var selectType = selectTarget.GetType();
if (_tabBases.TryGetValue(selectType, out GameFrameworkTabBase tab))
{
tab.Save();
}
}
}
}
protected override OdinMenuTree BuildMenuTree()
{
var tree = new OdinMenuTree();
foreach (var tabType in m_TabTypes)
{
DisplayNameAttribute displayNameAttribute = (DisplayNameAttribute)Attribute.GetCustomAttribute(tabType, typeof(DisplayNameAttribute));
GameFrameworkTabBase instance = (GameFrameworkTabBase)Activator.CreateInstance(tabType);
_tabBases.Add(tabType, instance);
tree.Add(displayNameAttribute.DisplayName, instance);
}
tree.Selection.SelectionChanged += SelectionOnSelectionChanged;
return tree;
}
private void SelectionOnSelectionChanged(SelectionChangedType obj)
{
var selectTarget = this.MenuTree.Selection.SelectedValue;
if (selectTarget != null)
{
var selectType = selectTarget.GetType();
if (_tabBases.TryGetValue(selectType, out GameFrameworkTabBase tab))
{
tab.Save();
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 305213ea3645462caff0daecd6be32d3
timeCreated: 1737012834

View File

@ -0,0 +1,12 @@
using UnityEditor;
namespace AlicizaX.Editor
{
public abstract class GameFrameworkTabBase
{
protected internal virtual void Save()
{
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: df5db70793c4475d9de5654c637a7ea1
timeCreated: 1737010059

8
Editor/HybridCLR.meta Normal file
View File

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

View File

@ -0,0 +1,106 @@
#if ENABLE_HYBRIDCLR
using HybridCLR.Editor;
using HybridCLR.Editor.Commands;
#endif
using AlicizaX.Editor;
using UnityEditor;
using UnityEngine;
[InitializeOnLoad]
public static class BuildDLLCommand
{
private const string EnableHybridClrScriptingDefineSymbol = "ENABLE_HYBRIDCLR";
static BuildDLLCommand()
{
}
/// <summary>
/// 禁用HybridCLR宏定义。
/// </summary>
[MenuItem("AlicizaX/HybridCLR/Define Symbols/Disable HybridCLR", false, 30)]
public static void Disable()
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(EnableHybridClrScriptingDefineSymbol);
HybridCLR.Editor.SettingsUtil.Enable = false;
SyncAssemblyContent.RefreshAssembly();
}
/// <summary>
/// 开启HybridCLR宏定义。
/// </summary>
[MenuItem("AlicizaX/HybridCLR/Define Symbols/Enable HybridCLR", false, 31)]
public static void Enable()
{
ScriptingDefineSymbols.RemoveScriptingDefineSymbol(EnableHybridClrScriptingDefineSymbol);
ScriptingDefineSymbols.AddScriptingDefineSymbol(EnableHybridClrScriptingDefineSymbol);
HybridCLR.Editor.SettingsUtil.Enable = true;
SyncAssemblyContent.RefreshAssembly();
}
#if ENABLE_HYBRIDCLR
[MenuItem("AlicizaX/HybridCLR/Build/BuildAssets And CopyTo AssemblyTextAssetPath")]
public static void BuildAndCopyDlls()
{
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
CompileDllCommand.CompileDll(target);
CopyAOTHotUpdateDlls(target);
}
#endif
public static void BuildAndCopyDlls(BuildTarget target)
{
#if ENABLE_HYBRIDCLR
CompileDllCommand.CompileDll(target);
CopyAOTHotUpdateDlls(target);
#endif
}
public static void CopyAOTHotUpdateDlls(BuildTarget target)
{
CopyAOTAssembliesToAssetPath();
CopyHotUpdateAssembliesToAssetPath();
AssetDatabase.Refresh();
}
public static void CopyAOTAssembliesToAssetPath()
{
#if ENABLE_HYBRIDCLR
var target = EditorUserBuildSettings.activeBuildTarget;
string aotAssembliesSrcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(target);
string aotAssembliesDstDir = Application.dataPath + "/" + ScriptableSingletonUtil.Get<FrameworkHotUpdateSettings>().AssemblyTextAssetPath;
foreach (var dll in ScriptableSingletonUtil.Get<FrameworkHotUpdateSettings>().AOTMetaAssemblies)
{
string srcDllPath = $"{aotAssembliesSrcDir}/{dll}";
if (!System.IO.File.Exists(srcDllPath))
{
Debug.LogError($"ab中添加AOT补充元数据dll:{srcDllPath} 时发生错误,文件不存在。裁剪后的AOT dll在BuildPlayer时才能生成因此需要你先构建一次游戏App后再打包。");
continue;
}
string dllBytesPath = $"{aotAssembliesDstDir}/{dll}.bytes";
System.IO.File.Copy(srcDllPath, dllBytesPath, true);
Debug.Log($"[CopyAOTAssembliesToStreamingAssets] copy AOT dll {srcDllPath} -> {dllBytesPath}");
}
#endif
}
public static void CopyHotUpdateAssembliesToAssetPath()
{
#if ENABLE_HYBRIDCLR
var target = EditorUserBuildSettings.activeBuildTarget;
string hotfixDllSrcDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target);
string hotfixAssembliesDstDir = Application.dataPath + "/" + ScriptableSingletonUtil.Get<FrameworkHotUpdateSettings>().AssemblyTextAssetPath;
foreach (var dll in SettingsUtil.HotUpdateAssemblyFilesExcludePreserved)
{
string dllPath = $"{hotfixDllSrcDir}/{dll}";
string dllBytesPath = $"{hotfixAssembliesDstDir}/{dll}.bytes";
System.IO.File.Copy(dllPath, dllBytesPath, true);
Debug.Log($"[CopyHotUpdateAssembliesToStreamingAssets] copy hotfix dll {dllPath} -> {dllBytesPath}");
}
#endif
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0761954ba63648b8b73f8ebb736bb9eb
timeCreated: 1737524294

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using AlicizaX.Editor;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using Sirenix.Serialization;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Runtime
{
[DisplayName("热更设置")]
[Serializable]
internal sealed class HybridCLRSettingTab : GameFrameworkTabBase
{
[Required][InlineEditor(InlineEditorObjectFieldModes.CompletelyHidden)][DisableInPlayMode] [HideLabel]
public FrameworkHotUpdateSettings FrameworkHotUpdateSettings;
public HybridCLRSettingTab()
{
FrameworkHotUpdateSettings = ScriptableSingletonUtil.Get<FrameworkHotUpdateSettings>();
}
[Sirenix.OdinInspector.Button(ButtonSizes.Large)]
protected void RefreshAssembly()
{
SyncAssemblyContent.RefreshAssembly();
}
protected internal override void Save()
{
base.Save();
EditorUtility.SetDirty(FrameworkHotUpdateSettings);
AssetDatabase.SaveAssets();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ab21809ce8ad4d5ebe950b70107deb00
timeCreated: 1737525094

View File

@ -0,0 +1,15 @@
using UnityEditor;
namespace AlicizaX.Editor
{
public static class SyncAssemblyContent
{
public static void RefreshAssembly()
{
ScriptableSingletonUtil.Get<FrameworkHotUpdateSettings>().HotUpdateAssemblies = HybridCLR.Editor.SettingsUtil.HotUpdateAssemblyFilesIncludePreserved;
ScriptableSingletonUtil.Get<FrameworkHotUpdateSettings>().AOTMetaAssemblies = HybridCLR.Editor.SettingsUtil.AOTAssemblyNames;
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c08b751d385441058a634280266803eb
timeCreated: 1737524480

3
Editor/Inspector.meta Normal file
View File

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

View File

@ -0,0 +1,139 @@
using AlicizaX;
using System.Collections.Generic;
using AlicizaX.Runtime;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
[CustomEditor(typeof(BaseComponent))]
internal sealed class BaseComponentInspector : 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 m_FrameRate = null;
private SerializedProperty m_GameSpeed = null;
private SerializedProperty m_RunInBackground = null;
private SerializedProperty m_NeverSleep = null;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
BaseComponent t = (BaseComponent) target;
int frameRate = EditorGUILayout.IntSlider("Frame Rate", m_FrameRate.intValue, 1, 120);
if (frameRate != m_FrameRate.intValue)
{
if (EditorApplication.isPlaying)
{
t.FrameRate = frameRate;
}
else
{
m_FrameRate.intValue = frameRate;
}
}
EditorGUILayout.BeginVertical("box");
{
float gameSpeed = EditorGUILayout.Slider("Game Speed", m_GameSpeed.floatValue, 0f, 8f);
int selectedGameSpeed = GUILayout.SelectionGrid(GetSelectedGameSpeed(gameSpeed), GameSpeedForDisplay, 5);
if (selectedGameSpeed >= 0)
{
gameSpeed = GetGameSpeed(selectedGameSpeed);
}
if (gameSpeed != m_GameSpeed.floatValue)
{
if (EditorApplication.isPlaying)
{
t.GameSpeed = gameSpeed;
}
else
{
m_GameSpeed.floatValue = gameSpeed;
}
}
}
EditorGUILayout.EndVertical();
bool runInBackground = EditorGUILayout.Toggle("Run in Background", m_RunInBackground.boolValue);
if (runInBackground != m_RunInBackground.boolValue)
{
if (EditorApplication.isPlaying)
{
t.RunInBackground = runInBackground;
}
else
{
m_RunInBackground.boolValue = runInBackground;
}
}
bool neverSleep = EditorGUILayout.Toggle("Never Sleep", m_NeverSleep.boolValue);
if (neverSleep != m_NeverSleep.boolValue)
{
if (EditorApplication.isPlaying)
{
t.NeverSleep = neverSleep;
}
else
{
m_NeverSleep.boolValue = neverSleep;
}
}
serializedObject.ApplyModifiedProperties();
}
protected override void OnCompileComplete()
{
base.OnCompileComplete();
}
private void OnEnable()
{
m_FrameRate = serializedObject.FindProperty("m_FrameRate");
m_GameSpeed = serializedObject.FindProperty("m_GameSpeed");
m_RunInBackground = serializedObject.FindProperty("m_RunInBackground");
m_NeverSleep = serializedObject.FindProperty("m_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 (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,79 @@
using System.Collections.Generic;
using AlicizaX.Runtime;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
public abstract class ComponentTypeComponentInspector : GameFrameworkInspector
{
protected SerializedProperty ComponentType = null;
protected string[] ComponentTypeNames = null;
protected int ComponentTypeNameIndex = 0;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
{
int componentTypeSelectedIndex = EditorGUILayout.Popup("ComponentType", ComponentTypeNameIndex, ComponentTypeNames);
if (componentTypeSelectedIndex != ComponentTypeNameIndex)
{
ComponentTypeNameIndex = componentTypeSelectedIndex;
ComponentType.stringValue = componentTypeSelectedIndex <= 0 ? null : ComponentTypeNames[componentTypeSelectedIndex];
}
}
EditorGUI.EndDisabledGroup();
serializedObject.ApplyModifiedProperties();
Repaint();
}
protected override void OnCompileComplete()
{
base.OnCompileComplete();
RefreshTypeNames();
}
protected virtual void Enable()
{
}
private void OnEnable()
{
ComponentType = serializedObject.FindProperty("componentType");
Enable();
RefreshTypeNames();
}
protected abstract void RefreshTypeNames();
protected void RefreshComponentTypeNames(System.Type type)
{
List<string> managerTypeNames = new List<string>
{
NoneOptionName
};
managerTypeNames.AddRange(Utility.Assembly.GetRuntimeTypeNames(type));
ComponentTypeNames = managerTypeNames.ToArray();
ComponentTypeNameIndex = 0;
if (!ComponentType.stringValue.IsNullOrEmpty())
{
ComponentTypeNameIndex = managerTypeNames.IndexOf(ComponentType.stringValue);
if (ComponentTypeNameIndex <= 0)
{
ComponentTypeNameIndex = 0;
ComponentType.stringValue = null;
}
}
serializedObject.ApplyModifiedProperties();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: dd05e24936f94181ad58fb5c7b3c1e38
timeCreated: 1712812404

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,145 @@
using AlicizaX;
using AlicizaX.ObjectPool;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using AlicizaX.Runtime;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
[CustomEditor(typeof(ObjectPoolComponent))]
internal sealed class ObjectPoolComponentInspector : ComponentTypeComponentInspector
{
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();
}
protected override void RefreshTypeNames()
{
RefreshComponentTypeNames(typeof(IObjectPoolManager));
}
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

@ -0,0 +1,150 @@
using AlicizaX;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using AlicizaX.Runtime;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
[CustomEditor(typeof(ReferencePoolComponent))]
internal sealed class ReferencePoolComponentInspector : GameFrameworkInspector
{
private readonly Dictionary<string, List<ReferencePoolInfo>> m_ReferencePoolInfos = new Dictionary<string, List<ReferencePoolInfo>>(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();
ReferencePoolComponent t = (ReferencePoolComponent)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", ReferencePool.Count.ToString());
m_ShowFullClassName = EditorGUILayout.Toggle("Show Full Class Name", m_ShowFullClassName);
m_ReferencePoolInfos.Clear();
ReferencePoolInfo[] referencePoolInfos = ReferencePool.GetAllReferencePoolInfos();
foreach (ReferencePoolInfo referencePoolInfo in referencePoolInfos)
{
string assemblyName = referencePoolInfo.Type.Assembly.GetName().Name;
List<ReferencePoolInfo> results = null;
if (!m_ReferencePoolInfos.TryGetValue(assemblyName, out results))
{
results = new List<ReferencePoolInfo>();
m_ReferencePoolInfos.Add(assemblyName, results);
}
results.Add(referencePoolInfo);
}
foreach (KeyValuePair<string, List<ReferencePoolInfo>> 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 (ReferencePoolInfo 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 (ReferencePoolInfo referencePoolInfo in assemblyReferencePoolInfo.Value)
{
data[index++] = Utility.Text.Format("{0},{1},{2},{3},{4},{5},{6},{7}", referencePoolInfo.Type.Name, referencePoolInfo.Type.FullName, referencePoolInfo.UnusedReferenceCount, referencePoolInfo.UsingReferenceCount, referencePoolInfo.AcquireReferenceCount, referencePoolInfo.ReleaseReferenceCount, referencePoolInfo.AddReferenceCount, referencePoolInfo.RemoveReferenceCount);
}
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(ReferencePoolInfo 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.UnusedReferenceCount, referencePoolInfo.UsingReferenceCount, referencePoolInfo.AcquireReferenceCount, referencePoolInfo.ReleaseReferenceCount, referencePoolInfo.AddReferenceCount, referencePoolInfo.RemoveReferenceCount));
#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.UnusedReferenceCount, referencePoolInfo.UsingReferenceCount, referencePoolInfo.AcquireReferenceCount, referencePoolInfo.ReleaseReferenceCount, referencePoolInfo.AddReferenceCount, referencePoolInfo.RemoveReferenceCount));
#endif
}
private int Comparison(ReferencePoolInfo a, ReferencePoolInfo 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:

3
Editor/Misc.meta Normal file
View File

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

91
Editor/Misc/HelperInfo.cs Normal file
View File

@ -0,0 +1,91 @@
using AlicizaX;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using AlicizaX.Runtime;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
public sealed class HelperInfo<T> where T : MonoBehaviour
{
private const string CustomOptionName = "<Custom>";
private readonly string m_Name;
private SerializedProperty m_HelperTypeName;
private SerializedProperty m_CustomHelper;
private string[] m_HelperTypeNames;
private int m_HelperTypeNameIndex;
public HelperInfo(string name)
{
m_Name = name;
m_HelperTypeName = null;
m_CustomHelper = null;
m_HelperTypeNames = null;
m_HelperTypeNameIndex = 0;
}
public void Init(SerializedObject serializedObject)
{
m_HelperTypeName = serializedObject.FindProperty(Utility.Text.Format("m_{0}HelperTypeName", m_Name));
m_CustomHelper = serializedObject.FindProperty(Utility.Text.Format("m_Custom{0}Helper", m_Name));
}
public void Draw()
{
string displayName = FieldNameForDisplay(m_Name);
int selectedIndex = EditorGUILayout.Popup(Utility.Text.Format("{0} Helper", displayName), m_HelperTypeNameIndex, m_HelperTypeNames);
if (selectedIndex != m_HelperTypeNameIndex)
{
m_HelperTypeNameIndex = selectedIndex;
m_HelperTypeName.stringValue = selectedIndex <= 0 ? null : m_HelperTypeNames[selectedIndex];
}
if (m_HelperTypeNameIndex <= 0)
{
EditorGUILayout.PropertyField(m_CustomHelper);
if (m_CustomHelper.objectReferenceValue == null)
{
EditorGUILayout.HelpBox(Utility.Text.Format("You must set Custom {0} Helper.", displayName), MessageType.Error);
}
}
}
public void Refresh()
{
List<string> helperTypeNameList = new List<string>
{
CustomOptionName
};
helperTypeNameList.AddRange(Utility.Assembly.GetRuntimeTypeNames(typeof(T)));
m_HelperTypeNames = helperTypeNameList.ToArray();
m_HelperTypeNameIndex = 0;
if (!string.IsNullOrEmpty(m_HelperTypeName.stringValue))
{
m_HelperTypeNameIndex = helperTypeNameList.IndexOf(m_HelperTypeName.stringValue);
if (m_HelperTypeNameIndex <= 0)
{
m_HelperTypeNameIndex = 0;
m_HelperTypeName.stringValue = null;
}
}
}
private string FieldNameForDisplay(string fieldName)
{
if (string.IsNullOrEmpty(fieldName))
{
return string.Empty;
}
string str = Regex.Replace(fieldName, @"^m_", string.Empty);
str = Regex.Replace(str, @"((?<=[a-z])[A-Z]|[A-Z](?=[a-z]))", @" $1").TrimStart();
return str;
}
}
}

View File

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

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("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("AlicizaX/Scripting Define Symbols/Enable All Logs", false, 31)]
public static void EnableAllLogs()
{
DisableAllLogs();
ScriptingDefineSymbols.AddScriptingDefineSymbol(EnableLogScriptingDefineSymbol);
}
/// <summary>
/// 开启调试及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("AlicizaX/Scripting Define Symbols/Enable Debug And Above Logs", false, 32)]
public static void EnableDebugAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableDebugAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启信息及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("AlicizaX/Scripting Define Symbols/Enable Info And Above Logs", false, 33)]
public static void EnableInfoAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableInfoAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启警告及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("AlicizaX/Scripting Define Symbols/Enable Warning And Above Logs", false, 34)]
public static void EnableWarningAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableWarningAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启错误及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("AlicizaX/Scripting Define Symbols/Enable Error And Above Logs", false, 35)]
public static void EnableErrorAndAboveLogs()
{
SetAboveLogScriptingDefineSymbol(EnableErrorAndAboveLogScriptingDefineSymbol);
}
/// <summary>
/// 开启严重错误及以上级别的日志脚本宏定义。
/// </summary>
[MenuItem("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:

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

@ -0,0 +1,85 @@
using AlicizaX;
using System.Diagnostics;
using AlicizaX.Runtime;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.Editor
{
/// <summary>
/// 打开文件夹相关的实用函数。
/// </summary>
public static class OpenFolder
{
/// <summary>
/// 打开 Data Path 文件夹。
/// </summary>
[MenuItem("AlicizaX/Open Folder/Data Path", false, 10)]
public static void OpenFolderDataPath()
{
Execute(Application.dataPath);
}
/// <summary>
/// 打开 Persistent Data Path 文件夹。
/// </summary>
[MenuItem("AlicizaX/Open Folder/Persistent Data Path", false, 11)]
public static void OpenFolderPersistentDataPath()
{
Execute(Application.persistentDataPath);
}
/// <summary>
/// 打开 Streaming Assets Path 文件夹。
/// </summary>
[MenuItem("AlicizaX/Open Folder/Streaming Assets Path", false, 12)]
public static void OpenFolderStreamingAssetsPath()
{
Execute(Application.streamingAssetsPath);
}
/// <summary>
/// 打开 Temporary Cache Path 文件夹。
/// </summary>
[MenuItem("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("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,40 @@
using System;
using AlicizaX.Runtime;
using UnityEngine;
namespace AlicizaX.Editor
{
public static class ScriptableSingletonUtil
{
public static T Get<T>() where T : ScriptableObject
{
string assetType = typeof(T).Name;
T globalSetting = default;
string[] globalAssetPaths = UnityEditor.AssetDatabase.FindAssets($"t:{assetType}");
if (globalAssetPaths.Length > 1)
{
foreach (var assetPath in globalAssetPaths)
{
Debug.LogError($"Could not had Multiple {assetType}. Repeated Path: {UnityEditor.AssetDatabase.GUIDToAssetPath(assetPath)}");
}
throw new Exception($"Could not had Multiple {assetType}");
}
if (globalAssetPaths.Length == 1)
{
string path = UnityEditor.AssetDatabase.GUIDToAssetPath(globalAssetPaths[0]);
globalSetting = UnityEditor.AssetDatabase.LoadAssetAtPath<T>(path);
}
if (globalSetting == null)
{
Debug.LogError($"Could not found {assetType} asset");
return null;
}
return globalSetting;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7544b657bf904330859e64362e5098a8
timeCreated: 1737536609

View File

@ -0,0 +1,150 @@
using System.Collections.Generic;
using UnityEditor;
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)
{
return PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup).Split(';');
}
/// <summary>
/// 设置指定平台的脚本宏定义。
/// </summary>
/// <param name="buildTargetGroup">要设置脚本宏定义的平台。</param>
/// <param name="scriptingDefineSymbols">要设置的脚本宏定义。</param>
public static void SetScriptingDefineSymbols(BuildTargetGroup buildTargetGroup, string[] scriptingDefineSymbols)
{
PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, string.Join(";", scriptingDefineSymbols));
}
}
}

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.Runtime;
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:

3
Editor/Publisher.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 33e7b1423d7c447a984510c0d398311f
timeCreated: 1737530322

View File

@ -0,0 +1,26 @@
using AlicizaX.Runtime;
using Sirenix.OdinInspector;
using UnityEditor;
namespace AlicizaX.Editor
{
[System.Serializable]
[DisplayName("发布设置")]
internal sealed class FrameworkPublisherSettingTab : GameFrameworkTabBase
{
[Required] [InlineEditor(InlineEditorObjectFieldModes.CompletelyHidden)] [DisableInPlayMode] [HideLabel]
public FrameworkPublishSettings FrameworkPublishSettings;
public FrameworkPublisherSettingTab()
{
FrameworkPublishSettings = ScriptableSingletonUtil.Get<FrameworkPublishSettings>();
}
protected internal override void Save()
{
base.Save();
EditorUtility.SetDirty(FrameworkPublishSettings);
AssetDatabase.SaveAssets();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a6011b6ff89947deb4077e2578ad897e
timeCreated: 1737536018

8
Plugins.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 01e1cc22fd7cf9b4db27fb012d38717b
folderAsset: yes
DefaultImporter:
externalObjects: {}
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:

8
Resources.meta Normal file
View File

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

View File

@ -0,0 +1,30 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7c0f00a264c34df985e6c6b6e3f8b353, type: 3}
m_Name: FrameworkHotUpdateSettings
m_EditorClassIdentifier:
HotUpdateAssemblies:
- GameLib.dll
- GameProto.dll
- GameBase.dll
- GameLogic.dll
AOTMetaAssemblies:
- mscorlib.dll
- System.dll
- System.Core.dll
- UnityEngine.CoreModule.dll
- YooAsset.Runtime.dll
- UniTask.Runtime.dll
- ZString.dll
LogicMainDllName: GameLogic.dll
AssemblyTextAssetExtension: .bytes
AssemblyTextAssetPath: AssetRaw/DLL

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: efd2da5560bdcde42a91ff632fccad20
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,22 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7f734b9c80c9475690a0edad9a7626bd, type: 3}
m_Name: FrameworkPublishSettings
m_EditorClassIdentifier:
Configs:
- Version_Type: Release
CheckUrl: http://127.0.0.1:8080/UpdateData.json
- Version_Type: Dev
CheckUrl: http://127.0.0.1:8080/UpdateData.json
- Version_Type: Test
CheckUrl: http://127.0.0.1:8080/UpdateData.json
AppStageType: Dev

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: db8426664259b2946b313fa434c0e847
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

8
Runtime.meta Normal file
View File

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

View File

@ -0,0 +1,17 @@
{
"name": "AlicizaX.Runtime",
"rootNamespace": "",
"references": [
"GUID:33661e06c33d31b4c9223810bf503247",
"GUID:f51ebe6a0ceec4240a699833d6309b23"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 75b6f2078d190f14dbda4a5b747d709c
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

3
Runtime/Base.meta Normal file
View File

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

View File

@ -0,0 +1,167 @@
using System;
using UnityEngine;
namespace AlicizaX.Runtime
{
/// <summary>
/// 基础组件。
/// </summary>
[DisallowMultipleComponent]
[AddComponentMenu("Game Framework/Base")]
[UnityEngine.Scripting.Preserve]
public sealed class BaseComponent : GameFrameworkComponent
{
private const int DefaultDpi = 96;
private float m_GameSpeedBeforePause = 1f;
[SerializeField] private int m_FrameRate = 30;
[SerializeField] private float m_GameSpeed = 1f;
[SerializeField] private bool m_RunInBackground = true;
[SerializeField] private bool m_NeverSleep = true;
/// <summary>
/// 获取或设置游戏帧率。
/// </summary>
public int FrameRate
{
get { return m_FrameRate; }
set { Application.targetFrameRate = m_FrameRate = value; }
}
/// <summary>
/// 获取或设置游戏速度。
/// </summary>
public float GameSpeed
{
get { return m_GameSpeed; }
set { Time.timeScale = m_GameSpeed = value >= 0f ? value : 0f; }
}
/// <summary>
/// 获取游戏是否暂停。
/// </summary>
public bool IsGamePaused
{
get { return m_GameSpeed <= 0f; }
}
/// <summary>
/// 获取是否正常游戏速度。
/// </summary>
public bool IsNormalGameSpeed
{
get { return m_GameSpeed == 1f; }
}
/// <summary>
/// 获取或设置是否允许后台运行。
/// </summary>
public bool RunInBackground
{
get { return m_RunInBackground; }
set { Application.runInBackground = m_RunInBackground = value; }
}
/// <summary>
/// 获取或设置是否禁止休眠。
/// </summary>
public bool NeverSleep
{
get { return m_NeverSleep; }
set
{
m_NeverSleep = value;
Screen.sleepTimeout = value ? SleepTimeout.NeverSleep : SleepTimeout.SystemSetting;
}
}
/// <summary>
/// 游戏框架组件初始化。
/// </summary>
protected override void Awake()
{
IsAutoRegister = false;
base.Awake();
GameModuleMonoProxy _monoProxyProxy = new GameObject("GameModuleMonoProxy").AddComponent<GameModuleMonoProxy>();
_monoProxyProxy.gameObject.hideFlags = HideFlags.HideInHierarchy | HideFlags.HideInInspector;
DontDestroyOnLoad(this);
Utility.Unity.MakeEntity(transform);
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 = DefaultDpi;
}
Application.targetFrameRate = m_FrameRate;
Time.timeScale = m_GameSpeed;
Application.runInBackground = m_RunInBackground;
Screen.sleepTimeout = m_NeverSleep ? SleepTimeout.NeverSleep : SleepTimeout.SystemSetting;
}
private void OnApplicationQuit()
{
StopAllCoroutines();
Shutdown();
}
/// <summary>
/// 暂停游戏。
/// </summary>
public void PauseGame()
{
if (IsGamePaused)
{
return;
}
m_GameSpeedBeforePause = GameSpeed;
GameSpeed = 0f;
}
/// <summary>
/// 恢复游戏。
/// </summary>
public void ResumeGame()
{
if (!IsGamePaused)
{
return;
}
GameSpeed = m_GameSpeedBeforePause;
}
/// <summary>
/// 重置为正常游戏速度。
/// </summary>
public void ResetNormalGameSpeed()
{
if (IsNormalGameSpeed)
{
return;
}
GameSpeed = 1f;
}
internal void Shutdown()
{
Destroy(gameObject);
Utility.Unity.Shutdown();
ReferencePool.ClearAll();
Utility.Marshal.FreeCachedHGlobal();
}
}
}

View File

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

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.Runtime
{
/// <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.Runtime
{
/// <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,47 @@
using UnityEngine;
namespace AlicizaX.Runtime
{
/// <summary>
/// 游戏框架单例
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class GameFrameworkMonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
private static T _instance;
[UnityEngine.Scripting.Preserve]
protected GameFrameworkMonoSingleton()
{
}
/// <summary>
/// 单例对象
/// </summary>
[UnityEngine.Scripting.Preserve]
public static T Instance
{
get
{
if (_instance == null)
{
#if UNITY_6000_0_OR_NEWER
_instance = Object.FindFirstObjectByType<T>();
#else
_instance = (T)Object.FindObjectOfType(typeof(T));
#endif
}
if (_instance == null)
{
var insObj = new GameObject();
_instance = insObj.AddComponent<T>();
_instance.name = "[Singleton]" + typeof(T);
Object.DontDestroyOnLoad(insObj);
}
return _instance;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5901cab8f8ec41e1a15c88e7200aa2de
timeCreated: 1716261821

View File

@ -0,0 +1,276 @@
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace AlicizaX.Runtime
{
/// <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.Runtime
{
/// <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,32 @@
namespace AlicizaX.Runtime
{
/// <summary>
/// 游戏框架单例
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class GameFrameworkSingleton<T> where T : class, new()
{
private static T _instance;
[UnityEngine.Scripting.Preserve]
protected GameFrameworkSingleton()
{
}
/// <summary>
/// 单例对象
/// </summary>
public static T Instance
{
get
{
if (_instance == null)
{
_instance = new T();
}
return _instance;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: df1019efa8544a009c5d62d42e0eb753
timeCreated: 1715511521

View File

@ -0,0 +1,15 @@
using UnityEngine;
namespace AlicizaX.Runtime
{
/// <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,122 @@

using System;
using System.Runtime.InteropServices;
namespace AlicizaX.Runtime
{
/// <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:

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

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 71df78781abe4068a2c8a8fbedca8acf
timeCreated: 1736415662

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
namespace AlicizaX.Runtime
{
/// <summary>
/// 游戏事件管理器。
/// </summary>
public class EventListenerProxy : IReference
{
private readonly bool _isInit = false;
private readonly List<Action<Delegate>> clearHandlers;
private readonly List<Delegate> actionCallBack;
/// <summary>
/// 游戏事件管理器构造函数。
/// </summary>
public EventListenerProxy()
{
if (_isInit)
{
return;
}
_isInit = true;
clearHandlers = new List<Action<Delegate>>();
actionCallBack = new List<Delegate>();
}
/// <summary>
/// 清理内存对象回收入池。
/// </summary>
public void Clear()
{
if (!_isInit)
{
return;
}
for (int i = 0; i < clearHandlers.Count; ++i)
{
var clearAction = clearHandlers[i];
var handle = actionCallBack[i];
clearAction(handle);
}
clearHandlers.Clear();
actionCallBack.Clear();
}
public void AddEventImp(Action<Delegate> handler,Delegate action)
{
clearHandlers.Add(handler);
actionCallBack.Add(action);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eb346bc3be28f89468e2c071e7ab9594
timeCreated: 1680273952

View File

@ -0,0 +1,8 @@
using System;
namespace AlicizaX.Runtime
{
public interface IEvent
{
}
}

View File

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

View File

@ -0,0 +1,7 @@
namespace AlicizaX.Runtime
{
public interface IEventListenerProxy
{
public EventListenerProxy EventListener { get; }
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4798c5e2685443cd96fc39a7dc5e9df3
timeCreated: 1730786771

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 48751c35576345908f4dda6ef1133f4a
timeCreated: 1737376697

View File

@ -0,0 +1,16 @@
namespace AlicizaX.Runtime
{
/// <summary>
/// 事件基类。
/// </summary>
public abstract class BaseEventArgs : GameFrameworkEventArgs
{
/// <summary>
/// 获取事件ID。
/// </summary>
public abstract string Id
{
get;
}
}
}

View File

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

View File

@ -0,0 +1,50 @@
namespace AlicizaX.Runtime
{
public sealed partial class EventPool<T> where T : BaseEventArgs
{
/// <summary>
/// 事件结点。
/// </summary>
private sealed class EventNode : IReference
{
private object _sender = null;
private T _eventArgs = null;
/// <summary>
/// 发送者
/// </summary>
public object Sender
{
get { return _sender; }
}
/// <summary>
/// 事件参数
/// </summary>
public T EventArgs
{
get { return _eventArgs; }
}
/// <summary>
/// 创建事件节点
/// </summary>
/// <param name="sender"></param>
/// <param name="eventArgs"></param>
/// <returns></returns>
public static EventNode Create(object sender, T eventArgs)
{
EventNode eventNodeNode = ReferencePool.Acquire<EventNode>();
eventNodeNode._sender = sender;
eventNodeNode._eventArgs = eventArgs;
return eventNodeNode;
}
public void Clear()
{
_sender = null;
_eventArgs = null;
}
}
}
}

View File

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

View File

@ -0,0 +1,319 @@
using System;
using System.Collections.Generic;
namespace AlicizaX.Runtime
{
/// <summary>
/// 事件池。
/// </summary>
/// <typeparam name="T">事件类型。</typeparam>
public sealed partial class EventPool<T> where T : BaseEventArgs
{
private readonly object _lock = new object();
private readonly GameFrameworkMultiDictionary<string, EventHandler<T>> _eventHandlers;
private readonly Queue<EventNode> _events;
private readonly Dictionary<object, LinkedListNode<EventHandler<T>>> _cachedNodes;
private readonly Dictionary<object, LinkedListNode<EventHandler<T>>> _tempNodes;
private readonly EventPoolMode _eventPoolMode;
private EventHandler<T> _defaultHandler;
/// <summary>
/// 初始化事件池的新实例。
/// </summary>
/// <param name="mode">事件池模式。</param>
public EventPool(EventPoolMode mode)
{
_eventHandlers = new GameFrameworkMultiDictionary<string, EventHandler<T>>();
_events = new Queue<EventNode>();
_cachedNodes = new Dictionary<object, LinkedListNode<EventHandler<T>>>();
_tempNodes = new Dictionary<object, LinkedListNode<EventHandler<T>>>();
_eventPoolMode = mode;
_defaultHandler = null;
}
/// <summary>
/// 获取事件处理函数的数量。
/// </summary>
public int EventHandlerCount
{
get
{
lock (_lock)
{
return _eventHandlers.Count;
}
}
}
/// <summary>
/// 获取事件数量。
/// </summary>
public int EventCount
{
get
{
lock (_lock)
{
return _events.Count;
}
}
}
/// <summary>
/// 事件池轮询。
/// </summary>
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
public void Update(float elapseSeconds, float realElapseSeconds)
{
lock (_lock)
{
while (_events.Count > 0)
{
EventNode eventNodeNode = _events.Dequeue();
HandleEvent(eventNodeNode.Sender, eventNodeNode.EventArgs);
ReferencePool.Release(eventNodeNode);
}
}
}
/// <summary>
/// 关闭并清理事件池。
/// </summary>
public void Shutdown()
{
Clear();
_eventHandlers.Clear();
_cachedNodes.Clear();
_tempNodes.Clear();
_defaultHandler = null;
}
/// <summary>
/// 清理事件。
/// </summary>
public void Clear()
{
lock (_lock)
{
_events.Clear();
}
}
/// <summary>
/// 获取事件处理函数的数量。
/// </summary>
/// <param name="id">事件类型编号。</param>
/// <returns>事件处理函数的数量。</returns>
public int Count(string id)
{
lock (_lock)
{
if (_eventHandlers.TryGetValue(id, out var listRange))
{
return listRange.Count;
}
return 0;
}
}
/// <summary>
/// 检查是否存在事件处理函数。
/// </summary>
/// <param name="id">事件类型编号。</param>
/// <param name="handler">要检查的事件处理函数。</param>
/// <returns>是否存在事件处理函数。</returns>
public bool Check(string id, EventHandler<T> handler)
{
lock (_lock)
{
if (handler == null)
{
throw new GameFrameworkException("Event handler is invalid.");
}
return _eventHandlers.Contains(id, handler);
}
}
/// <summary>
/// 订阅事件处理函数。
/// </summary>
/// <param name="id">事件类型编号。</param>
/// <param name="handler">要订阅的事件处理函数。</param>
public void Subscribe(string id, EventHandler<T> handler)
{
lock (_lock)
{
if (handler == null)
{
throw new GameFrameworkException("Event handler is invalid.");
}
if (!_eventHandlers.Contains(id))
{
_eventHandlers.Add(id, handler);
}
else if ((_eventPoolMode & EventPoolMode.AllowMultiHandler) != EventPoolMode.AllowMultiHandler)
{
throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow multi handler.", id));
}
else if ((_eventPoolMode & EventPoolMode.AllowDuplicateHandler) != EventPoolMode.AllowDuplicateHandler && Check(id, handler))
{
throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow duplicate handler.", id));
}
else
{
_eventHandlers.Add(id, handler);
}
}
}
/// <summary>
/// 取消订阅事件处理函数。
/// </summary>
/// <param name="id">事件类型编号。</param>
/// <param name="handler">要取消订阅的事件处理函数。</param>
public void Unsubscribe(string id, EventHandler<T> handler)
{
lock (_lock)
{
if (handler == null)
{
throw new GameFrameworkException("Event handler is invalid.");
}
if (_cachedNodes.Count > 0)
{
foreach (KeyValuePair<object, LinkedListNode<EventHandler<T>>> cachedNode in _cachedNodes)
{
if (cachedNode.Value != null && cachedNode.Value.Value == handler)
{
_tempNodes.Add(cachedNode.Key, cachedNode.Value.Next);
}
}
if (_tempNodes.Count > 0)
{
foreach (KeyValuePair<object, LinkedListNode<EventHandler<T>>> cachedNode in _tempNodes)
{
_cachedNodes[cachedNode.Key] = cachedNode.Value;
}
_tempNodes.Clear();
}
}
if (!_eventHandlers.Remove(id, handler))
{
throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not exists specified handler.", id));
}
}
}
/// <summary>
/// 设置默认事件处理函数。
/// </summary>
/// <param name="handler">要设置的默认事件处理函数。</param>
public void SetDefaultHandler(EventHandler<T> handler)
{
_defaultHandler = handler;
}
/// <summary>
/// 抛出事件,这个操作是线程安全的,即使不在主线程中抛出,也可保证在主线程中回调事件处理函数,但事件会在抛出后的下一帧分发。
/// </summary>
/// <param name="sender">事件源。</param>
/// <param name="e">事件参数。</param>
public void Fire(object sender, T e)
{
lock (_lock)
{
if (e == null)
{
throw new GameFrameworkException("Event is invalid.");
}
var eventNodeNode = EventNode.Create(sender, e);
lock (_events)
{
_events.Enqueue(eventNodeNode);
}
}
}
/// <summary>
/// 抛出事件立即模式,这个操作不是线程安全的,事件会立刻分发。
/// </summary>
/// <param name="sender">事件源。</param>
/// <param name="e">事件参数。</param>
public void FireNow(object sender, T e)
{
lock (_lock)
{
if (e == null)
{
throw new GameFrameworkException("Event is invalid.");
}
HandleEvent(sender, e);
}
}
/// <summary>
/// 处理事件结点。
/// </summary>
/// <param name="sender">事件源。</param>
/// <param name="e">事件参数。</param>
private void HandleEvent(object sender, T e)
{
lock (_lock)
{
bool noHandlerException = false;
if (_eventHandlers.TryGetValue(e.Id, out var range))
{
LinkedListNode<EventHandler<T>> current = range.First;
while (current != null && current != range.Terminal)
{
_cachedNodes[e] = current.Next != range.Terminal ? current.Next : null;
try
{
current.Value?.Invoke(sender, e);
}
catch (Exception exception)
{
Log.Fatal(exception);
}
current = _cachedNodes[e];
}
_cachedNodes.Remove(e);
}
else if (_defaultHandler != null)
{
try
{
_defaultHandler(sender, e);
}
catch (Exception exception)
{
Log.Fatal(exception);
}
}
else if ((_eventPoolMode & EventPoolMode.AllowNoHandler) == 0)
{
noHandlerException = true;
}
ReferencePool.Release(e);
if (noHandlerException)
{
throw new GameFrameworkException(Utility.Text.Format("Event '{0}' not allow no handler.", e.Id));
}
}
}
}
}

View File

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

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