This commit is contained in:
陈思海 2025-07-29 13:34:19 +08:00
parent 57f0375e47
commit bc63b41e23
90 changed files with 4 additions and 3489 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "Client/Packages/com.alicizax.unity.ui.extension"]
path = Client/Packages/com.alicizax.unity.ui.extension
url = http://101.34.252.46:3000/AlicizaX/com.alicizax.unity.ui.extension.git

@ -0,0 +1 @@
Subproject commit 555a0cbd4f829a38c6231681f42eeb6c3a756c9d

View File

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

View File

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

View File

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

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: d9940ca83fab4087ab1116f5a2ea01a4
timeCreated: 1739274233

View File

@ -1,21 +0,0 @@
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.UI.Editor
{
public static class UIGenerateEditorTool
{
[MenuItem("GameObject/UI工具/热更工程UI代码", priority = -1)]
public static void GenerateHotfixUIScript()
{
UIGenerateWindow.ShowWindow(Selection.gameObjects.FirstOrDefault(),UIGenerateConfiguration.Instance.UIScriptGenerateConfig.HotFixProjectUIScriptGenerateData);
}
[MenuItem("GameObject/UI工具/主工程UI代码", priority = -1)]
public static void GenerateMainUIScript()
{
UIGenerateWindow.ShowWindow(Selection.gameObjects.FirstOrDefault(),UIGenerateConfiguration.Instance.UIScriptGenerateConfig.MainProjectUIScriptGenerateData);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8df5224864804bc3a206ab20e5024e5c
timeCreated: 1739274266

View File

@ -1,42 +0,0 @@
using UnityEditor;
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AlicizaX.UI.Editor;
using AlicizaX.UI.Runtime;
using UnityEngine.UIElements;
public class UIGenerateWindow : EditorWindow
{
private GameObject targetObject;
public static GameObject GetTargetObject()
{
return GetWindow<UIGenerateWindow>().targetObject;
}
public static void ShowWindow(GameObject target, UIScriptGenerateData scriptGenerateData)
{
var window = GetWindow<UIGenerateWindow>(false, "UI Config Editor", false);
window.maxSize = Vector2.zero;
window.minSize = Vector2.zero;
window.rootVisualElement.style.display = DisplayStyle.None;
window.rootVisualElement.parent.style.display = DisplayStyle.None;
window.Initlize(target, scriptGenerateData);
}
public static void CloseWindow()
{
var window = GetWindow<UIGenerateWindow>(false, "UI Config Editor");
window.Close();
}
private void Initlize(GameObject target, UIScriptGenerateData scriptGenerateData)
{
targetObject = target;
UIScriptGeneratorHelper.GenerateAndAttachScript(targetObject, scriptGenerateData);
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 7bfa0d164c11486cb3a133829d53dccf
timeCreated: 1739339632

View File

@ -1,469 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using AlicizaX.UI.Editor;
using AlicizaX.UI.Runtime;
using Sirenix.Utilities.Editor;
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEngine.UI;
namespace AlicizaX.UI.Editor
{
enum EBindType
{
None,
Widget,
ListCom,
}
[Serializable]
class UIBindData
{
public string Name;
public List<Component> BindCom;
public EBindType BindType;
public UIBindData(string name, List<Component> bindCom, EBindType bindType = EBindType.None)
{
Name = name;
BindCom = bindCom;
BindType = bindType;
}
public UIBindData(string name, Component bindCom, EBindType bindType = EBindType.None)
{
Name = name;
BindCom = new List<Component>() { bindCom };
BindType = bindType;
}
}
public static class UIScriptGeneratorHelper
{
private static UIGenerateConfiguration _uiGenerateConfiguration;
static UIGenerateConfiguration UIGenerateConfiguration
{
get
{
if (_uiGenerateConfiguration == null)
{
_uiGenerateConfiguration = UIGenerateConfiguration.Instance;
}
return _uiGenerateConfiguration;
}
}
private static string GetVerType(string uiName)
{
foreach (var pair in UIGenerateConfiguration.UIElementRegexConfigs)
{
if (uiName.StartsWith(pair.uiElementRegex))
{
return pair.componentType;
}
}
return string.Empty;
}
private static string[] SplitComName(string name)
{
bool hasCom = name.Contains(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName);
if (!hasCom) return null;
string comStr = name.Substring(0, name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName));
return comStr.Split(UIGenerateConfiguration.UIGenerateCommonData.ComCheckSplitName);
}
private static string GetKeyName(string key, string componentName)
{
return $"{key}{componentName.Substring(componentName.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName) + 1)}";
}
private static List<UIBindData> UIBindDatas = new List<UIBindData>();
private static string GenerateNameSpace = string.Empty;
private static List<string> ArrayComs = new List<string>();
private static void GetBindData(Transform root)
{
for (int i = 0; i < root.childCount; ++i)
{
Transform child = root.GetChild(i);
bool hasWdiget = child.GetComponent<UIHolderObjectBase>() != null;
if (UIGenerateConfiguration.UIGenerateCommonData.ExcludeKeywords.Any(k => child.name.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0)) continue;
bool isArrayComs = child.name.StartsWith(UIGenerateConfiguration.UIGenerateCommonData.ArrayComSplitName);
if (hasWdiget)
{
CollectWidget(child);
}
else if (isArrayComs)
{
string splitCode = UIGenerateConfiguration.UIGenerateCommonData.ArrayComSplitName;
int lastIndex = child.name.LastIndexOf(splitCode);
string text = child.name.Substring(child.name.IndexOf(splitCode) + 1, lastIndex - 1);
if (ArrayComs.Contains(text)) continue;
ArrayComs.Add(text);
List<Transform> arrayComs = new List<Transform>();
for (int j = 0; j < root.childCount; j++)
{
if (root.GetChild(j).name.Contains(text))
{
arrayComs.Add(root.GetChild(j));
}
}
CollectArrayComponent(arrayComs, text);
}
else if (!isArrayComs && !hasWdiget)
{
CollectComponent(child);
GetBindData(child);
}
}
}
private static void CollectComponent(Transform node)
{
string[] comArray = SplitComName(node.name);
if (comArray != null)
{
foreach (var com in comArray)
{
string typeName = GetVerType(com);
if (string.IsNullOrEmpty(typeName)) continue;
Component component = node.GetComponent(typeName);
if (component != null)
{
string keyName = GetKeyName(com, node.name);
if (UIBindDatas.Exists(a => a.Name == keyName))
{
Debug.LogError($"Duplicate key found: {keyName}");
continue;
}
UIBindDatas.Add(new UIBindData(keyName, component));
}
else
{
Debug.LogError($"{node.name} does not have component of type {typeName}");
}
}
}
}
private static void CollectWidget(Transform node)
{
if (node.name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckEndName) != -1 && node.name.IndexOf(UIGenerateConfiguration.UIGenerateCommonData.ComCheckSplitName) != -1)
{
Debug.LogWarning($"{node.name} 子组件不能包含规则定义符号!");
return;
}
UIHolderObjectBase component = node.GetComponent<UIHolderObjectBase>();
string keyName = node.name;
if (UIBindDatas.Exists(a => a.Name == keyName))
{
Debug.LogError($"Duplicate key found: {keyName}");
return;
}
UIBindDatas.Add(new UIBindData(keyName, component, EBindType.Widget));
}
private static void CollectArrayComponent(List<Transform> arrayNode, string nodeName)
{
string[] comArray = SplitComName(nodeName);
arrayNode = arrayNode.OrderBy(s => int.Parse(s.name.Split('*').Last())).ToList();
List<UIBindData> tempBindDatas = new List<UIBindData>(comArray.Length);
if (comArray != null)
{
int index = 0;
foreach (var com in comArray)
{
foreach (var node in arrayNode)
{
string typeName = GetVerType(com);
if (string.IsNullOrEmpty(typeName)) continue;
Component component = node.GetComponent(typeName);
if (component != null)
{
string keyName = GetKeyName(com, nodeName) + "List";
if (tempBindDatas.Count - 1 < index) tempBindDatas.Add(new UIBindData(keyName, new List<Component>(), EBindType.ListCom));
tempBindDatas[index].BindCom.Add(component);
}
else
{
Debug.LogError($"{node.name} does not have component of type {typeName}");
}
}
index++;
}
}
UIBindDatas.AddRange(tempBindDatas.ToArray());
}
private static string GetRefrenceNameSpace()
{
StringBuilder refrenceNameSpaceBuilder = new StringBuilder();
HashSet<string> nameSpaces = new HashSet<string>();
nameSpaces.Add("UnityEngine");
refrenceNameSpaceBuilder.Append($"using UnityEngine;\n");
foreach (var bindData in UIBindDatas)
{
string nameSpace = bindData.BindCom.FirstOrDefault().GetType().Namespace;
if (bindData.BindType == EBindType.ListCom)
{
if (!nameSpaces.Contains("using System.Collections.Generic;"))
{
refrenceNameSpaceBuilder.Append("using System.Collections.Generic;\n");
}
}
if (!nameSpaces.Contains(nameSpace) && !string.IsNullOrEmpty(nameSpace))
{
nameSpaces.Add(nameSpace);
refrenceNameSpaceBuilder.Append($"using {nameSpace};\n");
}
}
return refrenceNameSpaceBuilder.ToString();
}
private static string GetVarText(List<UIBindData> uiBindDatas)
{
StringBuilder varTextBuilder = new StringBuilder();
foreach (var bindData in uiBindDatas)
{
var varName = bindData.Name;
varTextBuilder.Append("\t\t[SerializeField]\n");
varTextBuilder.Append("\t\t[ReadOnly]\n");
varTextBuilder.Append("\t\t[HideLabel]\n");
if (bindData.BindType == EBindType.None)
{
varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} m{varName};\n");
varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} {varName} => m{varName};\n\n");
}
else if (bindData.BindType == EBindType.ListCom)
{
varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} [] m{varName} = new {bindData.BindCom.FirstOrDefault().GetType().Name}[{bindData.BindCom.Count}];\n");
varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} [] {varName} => m{varName};\n\n");
}
else if (bindData.BindType == EBindType.Widget)
{
varTextBuilder.Append($"\t\tprivate {bindData.BindCom.FirstOrDefault().GetType().Name} m{varName};\n");
varTextBuilder.Append($"\t\tpublic {bindData.BindCom.FirstOrDefault().GetType().Name} {varName} => m{varName};\n\n");
}
}
return varTextBuilder.ToString();
}
private static string GenerateScript(string className)
{
StringBuilder scriptBuilder = new StringBuilder();
scriptBuilder.Append(GetRefrenceNameSpace());
scriptBuilder.Append("using Sirenix.OdinInspector;\n");
scriptBuilder.Append("using AlicizaX.UI.Runtime;\n");
scriptBuilder.Append($"namespace {GenerateNameSpace}\n");
scriptBuilder.Append("{\n");
scriptBuilder.Append($"\t#Attribute#\n");
scriptBuilder.Append($"\tpublic class {className} : UIHolderObjectBase\n");
scriptBuilder.Append("\t{\n");
scriptBuilder.Append($"\t\tpublic const string ResTag = #Tag#;\n");
scriptBuilder.Append("\t\t#region Generated by Script Tool\n\n");
scriptBuilder.Append(GetVarText(UIBindDatas));
scriptBuilder.Append("\n\t\t#endregion\n");
scriptBuilder.Append("\t}\n");
scriptBuilder.Append("}\n");
return scriptBuilder.ToString();
}
public static void GenerateAndAttachScript(GameObject targetObject, UIScriptGenerateData scriptGenerateData)
{
UIBindDatas.Clear();
GenerateNameSpace = scriptGenerateData.NameSpace;
ArrayComs.Clear();
string className = $"{UIGenerateConfiguration.UIGenerateCommonData.GeneratePrefix}_{targetObject.name}";
string scriptSavePath = Path.Combine(scriptGenerateData.GenerateHolderCodePath, className + ".cs");
GetBindData(targetObject.transform);
string scriptContent = GenerateScript(className);
string TagName = $"\"{targetObject.name}\"";
if (scriptGenerateData.LoadType == EUIResLoadType.Resources)
{
string matchWords = string.Empty;
UIGenerateConfiguration.UIGenerateCommonData.CombineWords.Any(t =>
{
if (targetObject.name.IndexOf(t.Key) >= 0)
{
matchWords = t.Value;
}
return targetObject.name.IndexOf(t.Key) >= 0;
});
string finalPath = Path.Combine(scriptGenerateData.UIPrefabRootPath.Replace("Assets/", "").Replace("Resources/", ""), matchWords);
string didc = Path.Combine(scriptGenerateData.UIPrefabRootPath, matchWords);
if (!Directory.Exists(didc))
{
Directory.CreateDirectory(didc);
}
finalPath = Utility.Path.GetRegularPath(finalPath);
TagName = $"\"{finalPath}/{targetObject.name}\"";
}
scriptContent = scriptContent.Replace("#Tag#", TagName);
//#Attribute#
string uiAttribute = $"[UIRes({className}.ResTag, EUIResLoadType.{scriptGenerateData.LoadType.ToString()})]";
scriptContent = scriptContent.Replace("#Attribute#", uiAttribute);
if (File.Exists(scriptSavePath))
{
string oldText = File.ReadAllText(scriptSavePath);
if (oldText.Equals(scriptContent))
{
EditorPrefs.SetString("Generate", className);
CheckHasAttach();
return;
}
}
File.WriteAllText(scriptSavePath, scriptContent);
EditorPrefs.SetString("Generate", className);
AssetDatabase.Refresh();
}
[DidReloadScripts]
private static void CheckHasAttach()
{
bool has = EditorPrefs.HasKey("Generate");
if (has)
{
UIBindDatas.Clear();
ArrayComs.Clear();
var className = EditorPrefs.GetString("Generate");
var targetObject = UIGenerateWindow.GetTargetObject();
EditorPrefs.DeleteKey("Generate");
GetBindData(targetObject.transform);
AttachScriptToGameObject(targetObject, className);
Debug.Log($"Generate {className} Successfully attached to game object");
UIGenerateWindow.CloseWindow();
}
}
private static void AttachScriptToGameObject(GameObject targetObject, string scriptClassName)
{
Type scriptType = null;
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
if (assembly.FullName.Contains("Editor")) continue;
var types = assembly.GetTypes();
foreach (var type in types)
{
if (type.IsClass && !type.IsAbstract && type.Name.Contains(scriptClassName))
{
scriptType = type;
}
}
}
if (scriptType != null)
{
Component component = targetObject.GetOrAddComponent(scriptType);
FieldInfo[] fields = scriptType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (FieldInfo field in fields)
{
List<Component> componentInObjects = UIBindDatas.Find(data => "m" + data.Name == field.Name)?.BindCom;
if (componentInObjects != null)
{
if (field.FieldType.IsArray)
{
// 获取数组元素类型
Type elementType = field.FieldType.GetElementType();
// 创建对应类型的数组
Array array = Array.CreateInstance(elementType, componentInObjects.Count);
for (int i = 0; i < componentInObjects.Count; i++)
{
Component comp = componentInObjects[i];
// 检查元素类型是否匹配
if (elementType.IsInstanceOfType(comp))
{
array.SetValue(comp, i);
}
else
{
Debug.LogError($"元素 {i} 类型不匹配,期望 {elementType.Name},实际为 {comp.GetType().Name}");
// 处理错误,如跳过或终止赋值
}
}
field.SetValue(component, array);
}
else
{
// 非数组字段取第一个元素
if (componentInObjects.Count > 0)
{
// 同样检查类型兼容性
if (field.FieldType.IsInstanceOfType(componentInObjects[0]))
{
field.SetValue(component, componentInObjects[0]);
}
else
{
Debug.LogError($"字段 {field.Name} 类型不匹配,无法赋值");
}
}
}
}
else
{
Debug.LogError($"字段 {field.Name} 未找到匹配的组件绑定");
}
}
}
else
{
Debug.LogError($"Could not find the class: {scriptClassName}");
}
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: f42fd09682fc40898bf7176dbb24d085
timeCreated: 1739273282

View File

@ -1,104 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Sirenix.OdinInspector;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.Windows;
namespace AlicizaX.UI.Editor
{
public class UISettingEditorWindow : OdinEditorWindow
{
[MenuItem("Tools/AlicizaX/UI Setting Window")]
private static void OpenWindow()
{
GetWindow<UISettingEditorWindow>().Show();
}
protected override void Initialize()
{
uiGenerateConfiguration = UIGenerateConfiguration.Instance;
UIGenerateCommonData = uiGenerateConfiguration.UIGenerateCommonData;
UIScriptGenerateConfig = uiGenerateConfiguration.UIScriptGenerateConfig;
UIElementRegexConfigs = uiGenerateConfiguration.UIElementRegexConfigs;
RefreshLabel();
}
// [Required] [InlineEditor(InlineEditorObjectFieldModes.CompletelyHidden)] [DisableInPlayMode] [HideLabel]
private UIGenerateConfiguration uiGenerateConfiguration;
[Required] [DisableInPlayMode] [HideLabel] [TabGroup("UI基础设置")] [SerializeField]
public UIGenerateCommonData UIGenerateCommonData;
[TabGroup("UI基础设置")] [LabelText("脚本生成预览")] [ShowInInspector] [ReadOnly] [OnValueChanged("RefreshLabel")]
private string previewLabel;
[TabGroup("UI基础设置")] [LabelText("组件生成预览")] [ShowInInspector] [ReadOnly] [OnValueChanged("RefreshLabel")] [SuffixLabel("(下标0开始)")]
private string previewCompLabel;
[Required] [DisableInPlayMode] [HideLabel] [TabGroup("UI构建配置")] [SerializeField]
public UIScriptGenerateConfig UIScriptGenerateConfig;
[Required] [DisableInPlayMode] [HideLabel] [TabGroup("UI元素映射")] [SerializeField] [TableList(ShowIndexLabels = false, DrawScrollView = true, AlwaysExpanded = true)]
public List<UIEelementRegexData> UIElementRegexConfigs;
private void RefreshLabel()
{
previewLabel = $"{UIGenerateCommonData.GeneratePrefix}_UITestWindow";
previewCompLabel = $"{UIGenerateCommonData.ArrayComSplitName}Text{UIGenerateCommonData.ComCheckSplitName}Img" +
$"{UIGenerateCommonData.ComCheckEndName}Test{UIGenerateCommonData.ArrayComSplitName}0";
}
[TabGroup("UI元素映射")]
[Sirenix.OdinInspector.Button("加载默认")]
private void LoadDefaultConfig()
{
const string Path = "Packages/com.alicizax.unity.ui/Editor/Res/default.txt";
string text = System.IO.File.ReadAllText(Path);
UIElementRegexConfigs = JsonConvert.DeserializeObject<List<UIEelementRegexData>>(text);
}
[TabGroup("UI元素映射")]
[Sirenix.OdinInspector.Button("导出")]
private void ExportConfig()
{
var json = JsonConvert.SerializeObject(UIElementRegexConfigs);
System.IO.File.WriteAllText("Assets/uielementconfig.txt", json);
AssetDatabase.Refresh();
Debug.Log("Export UIElements Finished");
}
[TabGroup("UI元素映射")]
[Sirenix.OdinInspector.Button("导入")]
private void ImportConfig(TextAsset text)
{
UIElementRegexConfigs = JsonConvert.DeserializeObject<List<UIEelementRegexData>>(text.text);
Debug.Log("Import UIElements Finished");
}
protected override void OnDisable()
{
base.OnDisable();
uiGenerateConfiguration.UIGenerateCommonData = UIGenerateCommonData;
uiGenerateConfiguration.UIScriptGenerateConfig = UIScriptGenerateConfig;
uiGenerateConfiguration.UIElementRegexConfigs = UIElementRegexConfigs;
EditorUtility.SetDirty(uiGenerateConfiguration);
AssetDatabase.SaveAssets();
UIGenerateConfiguration.Save();
}
protected override void OnDestroy()
{
base.OnDestroy();
uiGenerateConfiguration.UIGenerateCommonData = UIGenerateCommonData;
uiGenerateConfiguration.UIScriptGenerateConfig = UIScriptGenerateConfig;
uiGenerateConfiguration.UIElementRegexConfigs = UIElementRegexConfigs;
EditorUtility.SetDirty(uiGenerateConfiguration);
AssetDatabase.SaveAssets();
UIGenerateConfiguration.Save();
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: d6e004459b3645d4b3022bdde89795b9
timeCreated: 1741340568

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: c775eb6a6aca4533bc75c89ef7a219a1
timeCreated: 1737704983

View File

@ -1,62 +0,0 @@
using System;
using AlicizaX.Editor;
using AlicizaX.UI.Runtime;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.UI.Editor
{
[CustomEditor(typeof(UIComponent))]
internal sealed class UIComponentInspector : GameFrameworkInspector
{
private SerializedProperty uiRoot;
private SerializedProperty _isOrthographic;
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
serializedObject.Update();
EditorGUI.BeginDisabledGroup(EditorApplication.isPlayingOrWillChangePlaymode);
{
if (uiRoot.objectReferenceValue == null)
{
EditorGUILayout.HelpBox("uiroot can not be null!", MessageType.Error);
}
EditorGUILayout.BeginHorizontal();
GameObject rootPrefab = (GameObject)EditorGUILayout.ObjectField("UI根预设", uiRoot.objectReferenceValue, typeof(GameObject), false);
if (rootPrefab != uiRoot.objectReferenceValue)
{
uiRoot.objectReferenceValue = rootPrefab;
}
if (uiRoot.objectReferenceValue == null)
{
if (GUILayout.Button("设置默认"))
{
GameObject defaultPrefab = AssetDatabase.LoadAssetAtPath<GameObject>("Packages/com.alicizax.unity.ui/Editor/Res/UIRoot.prefab");
uiRoot.objectReferenceValue = defaultPrefab;
}
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.PropertyField(_isOrthographic);
}
EditorGUI.EndDisabledGroup();
serializedObject.ApplyModifiedProperties();
Repaint();
}
private void OnEnable()
{
uiRoot = serializedObject.FindProperty("uiRoot");
_isOrthographic = serializedObject.FindProperty("_isOrthographic");
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 6c8dd72e28584576864a33af24255060
timeCreated: 1737704960

View File

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

View File

@ -1,312 +0,0 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &500891838716286123
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 5373281001294974998}
- component: {fileID: 743195302492612381}
- component: {fileID: 7227605137039161963}
m_Layer: 0
m_Name: UICamera
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &5373281001294974998
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 500891838716286123}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 86062277508691697}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!20 &743195302492612381
Camera:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 500891838716286123}
m_Enabled: 1
serializedVersion: 2
m_ClearFlags: 3
m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0}
m_projectionMatrixMode: 1
m_GateFitMode: 2
m_FOVAxisMode: 0
m_Iso: 200
m_ShutterSpeed: 0.005
m_Aperture: 16
m_FocusDistance: 10
m_FocalLength: 50
m_BladeCount: 5
m_Curvature: {x: 2, y: 11}
m_BarrelClipping: 0.25
m_Anamorphism: 0
m_SensorSize: {x: 36, y: 24}
m_LensShift: {x: 0, y: 0}
m_NormalizedViewPortRect:
serializedVersion: 2
x: 0
y: 0
width: 1
height: 1
near clip plane: 10
far clip plane: 1000
field of view: 60
orthographic: 1
orthographic size: 5
m_Depth: 2
m_CullingMask:
serializedVersion: 2
m_Bits: 32
m_RenderingPath: -1
m_TargetTexture: {fileID: 0}
m_TargetDisplay: 0
m_TargetEye: 3
m_HDR: 0
m_AllowMSAA: 0
m_AllowDynamicResolution: 0
m_ForceIntoRT: 0
m_OcclusionCulling: 1
m_StereoConvergence: 10
m_StereoSeparation: 0.022
--- !u!114 &7227605137039161963
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 500891838716286123}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a79441f348de89743a2939f4d699eac1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_RenderShadows: 0
m_RequiresDepthTextureOption: 0
m_RequiresOpaqueTextureOption: 0
m_CameraType: 0
m_Cameras: []
m_RendererIndex: -1
m_VolumeLayerMask:
serializedVersion: 2
m_Bits: 0
m_VolumeTrigger: {fileID: 0}
m_VolumeFrameworkUpdateModeOption: 2
m_RenderPostProcessing: 0
m_Antialiasing: 0
m_AntialiasingQuality: 2
m_StopNaN: 0
m_Dithering: 0
m_ClearDepth: 1
m_AllowXRRendering: 1
m_AllowHDROutput: 1
m_UseScreenCoordOverride: 0
m_ScreenSizeOverride: {x: 0, y: 0, z: 0, w: 0}
m_ScreenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0}
m_RequiresDepthTexture: 0
m_RequiresColorTexture: 0
m_Version: 2
m_TaaSettings:
m_Quality: 3
m_FrameInfluence: 0.1
m_JitterScale: 1
m_MipBias: 0
m_VarianceClampScale: 0.9
m_ContrastAdaptiveSharpening: 0
--- !u!1 &1735351593002053547
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3110666966229274110}
- component: {fileID: 4697108639102116370}
- component: {fileID: 3475475550181341661}
- component: {fileID: 6941144161958937340}
m_Layer: 5
m_Name: UICanvas
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3110666966229274110
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1735351593002053547}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 86062277508691697}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0, y: 0}
--- !u!223 &4697108639102116370
Canvas:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1735351593002053547}
m_Enabled: 1
serializedVersion: 3
m_RenderMode: 1
m_Camera: {fileID: 743195302492612381}
m_PlaneDistance: 100
m_PixelPerfect: 0
m_ReceivesEvents: 1
m_OverrideSorting: 0
m_OverridePixelPerfect: 0
m_SortingBucketNormalizedSize: 0
m_VertexColorAlwaysGammaSpace: 1
m_AdditionalShaderChannelsFlag: 27
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 0
m_TargetDisplay: 0
--- !u!114 &3475475550181341661
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1735351593002053547}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3}
m_Name:
m_EditorClassIdentifier:
m_UiScaleMode: 1
m_ReferencePixelsPerUnit: 100
m_ScaleFactor: 1
m_ReferenceResolution: {x: 1920, y: 1080}
m_ScreenMatchMode: 0
m_MatchWidthOrHeight: 0
m_PhysicalUnit: 3
m_FallbackScreenDPI: 96
m_DefaultSpriteDPI: 96
m_DynamicPixelsPerUnit: 1
m_PresetInfoIsWorld: 0
--- !u!114 &6941144161958937340
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1735351593002053547}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3}
m_Name:
m_EditorClassIdentifier:
m_IgnoreReversedGraphics: 1
m_BlockingObjects: 0
m_BlockingMask:
serializedVersion: 2
m_Bits: 55
--- !u!1 &4612363183729467837
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 86062277508691697}
- component: {fileID: 2601148299080526511}
- component: {fileID: 4177649269856392400}
m_Layer: 0
m_Name: UIRoot
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &86062277508691697
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4612363183729467837}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 1000, y: 1000, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 3110666966229274110}
- {fileID: 5373281001294974998}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2601148299080526511
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4612363183729467837}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3}
m_Name:
m_EditorClassIdentifier:
m_FirstSelected: {fileID: 0}
m_sendNavigationEvents: 1
m_DragThreshold: 10
--- !u!114 &4177649269856392400
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4612363183729467837}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 01614664b831546d2ae94a42149d80ac, type: 3}
m_Name:
m_EditorClassIdentifier:
m_SendPointerHoverToParent: 1
m_MoveRepeatDelay: 0.5
m_MoveRepeatRate: 0.1
m_XRTrackingOrigin: {fileID: 0}
m_ActionsAsset: {fileID: -944628639613478452, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_PointAction: {fileID: -1654692200621890270, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_MoveAction: {fileID: -8784545083839296357, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_SubmitAction: {fileID: 392368643174621059, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_CancelAction: {fileID: 7727032971491509709, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_LeftClickAction: {fileID: 3001919216989983466, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_MiddleClickAction: {fileID: -2185481485913320682, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_RightClickAction: {fileID: -4090225696740746782, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_ScrollWheelAction: {fileID: 6240969308177333660, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_TrackedDevicePositionAction: {fileID: 6564999863303420839, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_TrackedDeviceOrientationAction: {fileID: 7970375526676320489, guid: ca9f5fa95ffab41fb9a615ab714db018, type: 3}
m_DeselectOnBackgroundClick: 1
m_PointerBehavior: 0
m_CursorLockBehavior: 0
m_ScrollDeltaPerTick: 6

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 9368ff38b2090b2468f8358242026e4b
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1 +0,0 @@
[{"uiElementRegex":"Rect","componentType":"RectTransform"},{"uiElementRegex":"Obj","componentType":"GameObject"},{"uiElementRegex":"Tf","componentType":"Transform"},{"uiElementRegex":"Btn","componentType":"UXButton"},{"uiElementRegex":"Slider","componentType":"Slider"},{"uiElementRegex":"Img","componentType":"UXImage"},{"uiElementRegex":"RImg","componentType":"RawImage"},{"uiElementRegex":"Scrollbar","componentType":"Scrollbar"},{"uiElementRegex":"ScrollRect","componentType":"ScrollRect"},{"uiElementRegex":"GLayout","componentType":"GridLayoutGroup"},{"uiElementRegex":"HLayout","componentType":"HorizontalLayoutGroup"},{"uiElementRegex":"VLayout","componentType":"VerticalLayoutGroup"},{"uiElementRegex":"Text","componentType":"TMPro.TextMeshProUGUI"},{"uiElementRegex":"TogGroup","componentType":"UXGroup"},{"uiElementRegex":"Mask2D","componentType":"RectMask2D"},{"uiElementRegex":"Video","componentType":"Video.VideoPlayer"},{"uiElementRegex":"Input","componentType":"TMPro.TMP_InputField"},{"uiElementRegex":"CanvasGroup","componentType":"CanvasGroup"},{"uiElementRegex":"ScrollView","componentType":"RecyclerView"},{"uiElementRegex":"Drag","componentType":"UXDraggable"}]

View File

@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 59f4e0b608fbfde488c05a9ab77746b8
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 2f54df36513f40bd80b00f7149741495
timeCreated: 1737704936

View File

@ -1,99 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using AlicizaX.Editor.Setting;
using AlicizaX;
using AlicizaX.UI.Runtime;
using Newtonsoft.Json;
using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.Serialization;
namespace AlicizaX.UI.Editor
{
[AlicizaX.Editor.Setting.FilePath("ProjectSettings/UIGenerateConfiguration.asset")]
internal class UIGenerateConfiguration : ScriptableSingleton<UIGenerateConfiguration>
{
public UIGenerateCommonData UIGenerateCommonData = new UIGenerateCommonData();
public UIScriptGenerateConfig UIScriptGenerateConfig = new UIScriptGenerateConfig();
public List<UIEelementRegexData> UIElementRegexConfigs = new List<UIEelementRegexData>();
}
[System.Serializable]
public class UIGenerateCommonData
{
[LabelText("组件检查分隔符")] public string ComCheckSplitName = "#";
[LabelText("组件结尾分隔符")] public string ComCheckEndName = "@";
[LabelText("数组组件检查分隔符")] public string ArrayComSplitName = "*";
[LabelText("生成脚本前缀")] public string GeneratePrefix = "ui";
[LabelText("排除表")] public string[] ExcludeKeywords = { "ViewHolder" };
[ShowInInspector] [LabelText("生成路径拼接")]
public Dictionary<string, string> CombineWords = new Dictionary<string, string>
{
{ "Window", "Window" },
{ "ViewHolder", "ViewHolder" },
{ "Widget", "Widget" },
};
}
[System.Serializable]
public class UIEelementRegexData
{
public string uiElementRegex;
[ShowInInspector] [ValueDropdown("GetFilteredTypeList", ExpandAllMenuItems = false)]
public string componentType;
private static List<string> cacheFilterType;
public IEnumerable<string> GetFilteredTypeList()
{
if (cacheFilterType == null)
{
cacheFilterType = AlicizaX.Utility.Assembly.GetTypes()
.Where(m => !m.FullName.Contains("Editor"))
.Where(x => !x.IsAbstract || x.IsInterface)
.Where(x => !x.IsGenericTypeDefinition)
.Where(x => !x.IsSubclassOf(typeof(UIHolderObjectBase)))
.Where(x => x.IsSubclassOf(typeof(Component)))
.Where(x => !x.FullName.Contains("YooAsset"))
.Where(x => !x.FullName.Contains(("Unity.VisualScripting")))
.Where(x => !x.FullName.Contains(("Cysharp.Threading")))
.Where(x => !x.FullName.Contains(("UnityEngine.Rendering.UI.Debug")))
.Where(x => !x.FullName.Contains(("Unity.PerformanceTesting")))
.Where(x => !x.FullName.Contains(("UnityEngine.TestTools")))
.Select(x => x.Name).ToList();
cacheFilterType.Add(typeof(GameObject).Name);
}
return cacheFilterType;
}
}
[System.Serializable]
public class UIScriptGenerateConfig
{
[BoxGroup("主工程")] public UIScriptGenerateData MainProjectUIScriptGenerateData = new UIScriptGenerateData();
[BoxGroup("热更工程")] public UIScriptGenerateData HotFixProjectUIScriptGenerateData = new UIScriptGenerateData();
}
[System.Serializable]
public class UIScriptGenerateData
{
public string NameSpace;
[Sirenix.OdinInspector.FolderPath(RequireExistingPath = true, AbsolutePath = false)]
public string GenerateHolderCodePath;
[Sirenix.OdinInspector.FolderPath(RequireExistingPath = true, AbsolutePath = false)]
public string UIPrefabRootPath;
public EUIResLoadType LoadType;
}
}

View File

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

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2023] [ALianBlank of copyright owner][alianblank@outlook.com][https://alianblank.com/]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

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

View File

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

View File

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

View File

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

View File

@ -1,22 +0,0 @@
{
"name": "AlicizaX.UI.Runtime",
"rootNamespace": "AlicizaX.UI.Runtime",
"references": [
"GUID:75b6f2078d190f14dbda4a5b747d709c",
"GUID:56115dc8e38144842823f4ee0dcad88b",
"GUID:be2f20a77f3232f44b9711ef43234aac",
"GUID:f51ebe6a0ceec4240a699833d6309b23",
"GUID:189d55e03d78888459720d730f4d2424",
"GUID:4041d17782e62754ba8777fe2dfb6b27",
"GUID:2393d135922de5c4ab12e74f1e397920"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

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

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 003872fc9d1944708cac097b45a8382a
timeCreated: 1737703086

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: f783b53bbb3de8e48a9127ffd6463454
timeCreated: 1724221459

View File

@ -1,44 +0,0 @@
namespace AlicizaX.UI.Runtime
{
public enum UILayer
{
/// <summary>
/// 最低层
/// 要显示的这个就是最低的层级
/// </summary>
Background = 0,
/// <summary>
/// 场景层
/// 比如 血条飘字不是做在3D时 用2D实现时的层
/// 比如 头像 ...
/// </summary>
Scene = 1,
/// <summary>
/// 普通面板层
/// 全屏界面
/// </summary>
UI = 2,
/// <summary>
/// 弹窗层
/// 一般是非全屏界面,可同时存在的
/// </summary>
Popup = 3,
/// <summary>
/// 提示层
/// 一般 提示飘字 确认弹窗 跑马灯之类的
/// </summary>
Tips = 4,
/// <summary>
/// 最高层
/// 一般新手引导之类的
/// </summary>
Top = 5,
All = 6,
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 2d4172fa9d8337340899edb34207e108
timeCreated: 1724221472

View File

@ -1,31 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
public static class InstanceFactory
{
private static readonly Dictionary<Type, Func<object>> _constructorCache = new();
public static object CreateInstanceOptimized(Type type)
{
if (!_constructorCache.TryGetValue(type, out var constructor))
{
// 验证是否存在公共无参构造函数
var ctor = type.GetConstructor(Type.EmptyTypes);
if (ctor == null)
{
throw new MissingMethodException($"类型 {type.Name} 缺少公共无参构造函数");
}
// 构建表达式树new T()
var newExpr = Expression.New(ctor);
var lambda = Expression.Lambda<Func<object>>(newExpr);
constructor = lambda.Compile();
// 缓存委托
_constructorCache[type] = constructor;
}
return constructor();
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 195eee24a3e6423fbe4b9b537099a2d9
timeCreated: 1744176921

View File

@ -1,78 +0,0 @@
using System;
using System.Linq;
using AlicizaX.Resource.Runtime;
using AlicizaX;
using Cysharp.Threading.Tasks;
using UnityEngine;
using Object = UnityEngine.Object;
namespace AlicizaX.UI.Runtime
{
public static class UIHolderFactory
{
private static readonly IResourceModule ResourceModule;
static UIHolderFactory()
{
ResourceModule = ModuleSystem.GetModule<IResourceModule>();
}
public static async UniTask<T> CreateUIHolder<T>(Transform parent) where T : UIHolderObjectBase
{
if (UIResRegistry.TryGet(typeof(T).TypeHandle, out UIResRegistry.UIResInfo resInfo))
{
GameObject obj = await LoadUIResourcesAsync(resInfo, parent);
return obj.GetComponent<T>();
}
return null;
}
internal static async UniTask<GameObject> LoadUIResourcesAsync(UIResRegistry.UIResInfo resInfo, Transform parent)
{
return resInfo.LoadType == EUIResLoadType.AssetBundle
? await ResourceModule.LoadGameObjectAsync(resInfo.Location, parent)
: await InstantiateResourceAsync(resInfo.Location, parent);
}
internal static async UniTask CreateUIResource(UIMetadata meta, Transform parent, UIBase owner = null)
{
if (meta.State != UIState.CreatedUI) return;
GameObject obj = await LoadUIResourcesAsync(meta.ResInfo, parent);
ValidateAndBind(meta, obj, owner);
}
internal static void LoadUIResourcesSync(UIMetadata meta, Transform parent, UIBase owner = null)
{
if (meta.State != UIState.CreatedUI) return;
GameObject obj = meta.ResInfo.LoadType == EUIResLoadType.AssetBundle
? ResourceModule.LoadGameObject(meta.ResInfo.Location, parent)
: Object.Instantiate(Resources.Load<GameObject>(meta.ResInfo.Location), parent);
ValidateAndBind(meta, obj, owner);
}
private static void ValidateAndBind(UIMetadata meta, GameObject holderObject, UIBase owner)
{
if (!holderObject) throw new NullReferenceException($"UI resource load failed: {meta.ResInfo.Location}");
var holder = (UIHolderObjectBase)holderObject.GetComponent(meta.View.UIHolderType);
if (holder == null)
{
throw new InvalidCastException($"资源{holderObject.name}上不存在{meta.View.UIHolderType.FullName}");
}
meta.View?.BindUIHolder(holder, owner);
}
private static async UniTask<GameObject> InstantiateResourceAsync(string location, Transform parent)
{
GameObject prefab = (GameObject)await Resources.LoadAsync<GameObject>(location);
return Object.Instantiate(prefab, parent);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: d3cb20524d8440198708e90c56c76e38
timeCreated: 1739847047

View File

@ -1,56 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace AlicizaX.UI.Runtime
{
public static class UIMetaRegistry
{
public readonly struct UIMetaInfo
{
public readonly RuntimeTypeHandle RuntimeTypeHandle;
public readonly RuntimeTypeHandle HolderRuntimeTypeHandle;
public readonly int UILayer;
public readonly bool FullScreen;
public readonly int CacheTime;
public UIMetaInfo(RuntimeTypeHandle runtimeTypeHandle, RuntimeTypeHandle holderRuntimeTypeHandle, UILayer windowLayer, bool fullScreen, int cacheTime)
{
RuntimeTypeHandle = runtimeTypeHandle;
HolderRuntimeTypeHandle = holderRuntimeTypeHandle;
UILayer = (int)windowLayer;
FullScreen = fullScreen;
CacheTime = cacheTime;
}
}
private static readonly Dictionary<RuntimeTypeHandle, UIMetaInfo> _typeHandleMap = new();
private static readonly Dictionary<string, RuntimeTypeHandle> _stringHandleMap = new();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Register(Type uiType, Type holderType, UILayer layer = UILayer.UI, bool fullScreen = false, int cacheTime = 0)
{
var holderHandle = holderType.TypeHandle;
var uiHandle = uiType.TypeHandle;
_typeHandleMap[uiHandle] = new UIMetaInfo(uiHandle, holderHandle, layer, fullScreen, cacheTime);
_stringHandleMap[uiType.Name] = uiHandle;
}
public static bool TryGet(RuntimeTypeHandle handle, out UIMetaInfo info)
{
return _typeHandleMap.TryGetValue(handle, out info);
}
public static bool TryGet(string type, out UIMetaInfo info)
{
RuntimeTypeHandle typeHandle;
if (_stringHandleMap.TryGetValue(type, out typeHandle))
{
}
return _typeHandleMap.TryGetValue(typeHandle, out info);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: a9872b7a45da4d28b699bdf36ad792ca
timeCreated: 1744116849

View File

@ -1,57 +0,0 @@
using System;
using System.Runtime.CompilerServices;
using AlicizaX;
using Cysharp.Threading.Tasks;
namespace AlicizaX.UI.Runtime
{
internal sealed class UIMetadata
{
public UIBase View { get; private set; }
public readonly UIMetaRegistry.UIMetaInfo MetaInfo;
public readonly UIResRegistry.UIResInfo ResInfo;
public readonly Type UILogicType;
public bool InCache = false;
public UIState State
{
get
{
if (View == null) return UIState.Uninitialized;
return View.State;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CreateUI()
{
if (View is null)
{
View = (UIBase)InstanceFactory.CreateInstanceOptimized(UILogicType);
}
}
public void Dispose()
{
DisposeAsync().Forget();
}
private async UniTask DisposeAsync()
{
if (State != UIState.Uninitialized && State != UIState.Destroying)
{
await View.InternalDestroy();
View = null;
}
}
public UIMetadata(Type uiType)
{
UILogicType = uiType;
UIMetaRegistry.TryGet(UILogicType.TypeHandle, out MetaInfo);
UIResRegistry.TryGet(MetaInfo.HolderRuntimeTypeHandle, out ResInfo);
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 9f48f37976fb440479f1d76953347817
timeCreated: 1724230882

View File

@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using AlicizaX;
namespace AlicizaX.UI.Runtime
{
internal static class MetaTypeCache<T> where T : UIBase
{
public static readonly UIMetadata Metadata;
static MetaTypeCache()
{
var type = typeof(T);
Metadata = UIMetadataFactory.GetMetadata(type.TypeHandle);
}
}
internal static class UIMetadataFactory
{
private static readonly Dictionary<RuntimeTypeHandle, UIMetadata> UIWindowMetadata = new();
internal static UIMetadata GetMetadata(RuntimeTypeHandle handle)
{
if (!UIWindowMetadata.TryGetValue(handle, out var meta))
{
meta = new UIMetadata(Type.GetTypeFromHandle(handle));
UIWindowMetadata[handle] = meta;
}
return meta;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 908f3c05a3c74cc3aa0c35ec046e934d
timeCreated: 1739845718

View File

@ -1,37 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using UnityEditor;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
public static class UIResRegistry
{
private static readonly Dictionary<RuntimeTypeHandle, UIResInfo> _typeHandleMap = new();
public readonly struct UIResInfo
{
public readonly string Location;
public readonly EUIResLoadType LoadType;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UIResInfo(string location, EUIResLoadType loadType)
{
Location = location;
LoadType = loadType;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Register(Type holderType, string location, EUIResLoadType loadType)
{
var handle = holderType.TypeHandle;
_typeHandleMap[handle] = new UIResInfo(location, loadType);
}
public static bool TryGet(RuntimeTypeHandle handle, out UIResInfo info)
=> _typeHandleMap.TryGetValue(handle, out info);
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 0132d28e3a4943a4acd10fbc7d35ad51
timeCreated: 1744113525

View File

@ -1,57 +0,0 @@
using System;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
[AttributeUsage(AttributeTargets.Class)]
public class WindowAttribute : Attribute
{
/// <summary>
/// 窗口层级
/// </summary>
public readonly UILayer WindowLayer;
/// <summary>
/// 全屏窗口标记。
/// </summary>
public readonly bool FullScreen;
/// <summary>
/// 延时关闭
/// </summary>
public readonly int CacheTime;
/// <summary>
///
/// </summary>
/// <param name="windowLayer">显示层级</param>
/// <param name="fullScreen">是否全屏遮挡</param>
/// <param name="cacheTime">缓存时间/s -1永久 0不 >=1生效</param>
public WindowAttribute(UILayer windowLayer, bool fullScreen = false, int cacheTime = 0)
{
WindowLayer = windowLayer;
FullScreen = fullScreen;
CacheTime = cacheTime;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class UIResAttribute : Attribute
{
public readonly string ResLocation;
public readonly EUIResLoadType ResLoadType;
public UIResAttribute(string location, EUIResLoadType loadType)
{
ResLocation = location;
ResLoadType = loadType;
}
}
public enum EUIResLoadType:byte
{
Resources,
AssetBundle
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8144120881e676f4cbdb6209550c7097
timeCreated: 1680511434

View File

@ -1,66 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using AlicizaX.Event.Runtime;
using AlicizaX.EventKit;
namespace AlicizaX
{
/// <summary>
/// 游戏事件管理器。
/// </summary>
public class EventListenerProxy : IMemory
{
private readonly bool _isInit = false;
private List<(string, EventHandler<GameEventArgs>)> eventMaps;
private List<EventRuntimeHandle> eventHandles;
public EventListenerProxy()
{
if (!_isInit)
{
eventMaps = new();
eventHandles = new();
}
_isInit = true;
}
/// <summary>
/// 清理内存对象回收入池。
/// </summary>
public void Clear()
{
if (!_isInit)
{
return;
}
for (int i = 0; i < eventMaps.Count; ++i)
{
ModuleSystem.GetModule<IEventModule>().Unsubscribe(eventMaps[i].Item1, eventMaps[i].Item2);
}
for (int i = eventHandles.Count - 1; i > 0; i++)
{
eventHandles[i].Dispose();
}
eventHandles.Clear();
eventMaps.Clear();
}
public void Subscribe(string id, EventHandler<GameEventArgs> handler)
{
eventMaps.Add((id, handler));
ModuleSystem.GetModule<IEventModule>().Subscribe(id, handler);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Subscribe<T>(Action<T> handler) where T : struct, IEventArgs
{
EventRuntimeHandle handle = EventContainer<T>.Subscribe(handler);
eventHandles.Add(handle);
}
}
}

View File

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

View File

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

View File

@ -1,24 +0,0 @@
using System;
using AlicizaX.Resource.Runtime;
using AlicizaX;
using AlicizaX.Timer.Runtime;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
public interface IUIModule : IModule, IModuleUpdate
{
void Initlize(Transform root,bool isOrthographic);
Camera UICamera { get; set; }
Transform UICanvasRoot { get; set; }
UniTask<UIBase> ShowUI<T>(params System.Object[] userDatas) where T : UIBase;
UniTask<UIBase>? ShowUI(string type, params object[] userDatas);
UniTask<T> ShowUIAsync<T>(params System.Object[] userDatas) where T : UIBase;
void CloseUI<T>(bool force = false) where T : UIBase;
T GetUI<T>() where T : UIBase;
void CloseUI(RuntimeTypeHandle handle, bool force = false);
protected internal void SetTimerManager(ITimerModule timerModule);
}
}

View File

@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 4fc14a8ed26f89443b0844936e492514

View File

@ -1,80 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
internal sealed partial class UIModule
{
private GameObject m_LayerBlock; //内部屏蔽对象 显示时之下的所有UI将不可操作
private int m_LastCountDownGuid; //倒计时的唯一ID
private void InitUIBlock()
{
m_LayerBlock = new GameObject("LayerBlock");
var rect = m_LayerBlock.AddComponent<RectTransform>();
m_LayerBlock.AddComponent<CanvasRenderer>();
m_LayerBlock.AddComponent<UIBlock>();
rect.SetParent(UICanvasRoot);
rect.SetAsLastSibling();
rect.ResetToFullScreen();
SetLayerBlockOption(false);
}
/// <summary>
/// 设置UI遮挡
/// </summary>
/// <param name="timeDuration">倒计时/s</param>
public void SetUIBlock(float timeDuration)
{
if (m_LastCountDownGuid != 0)
{
_timerModule.RemoveTimer(m_LastCountDownGuid);
}
SetLayerBlockOption(true);
m_LastCountDownGuid = _timerModule.AddTimer(OnBlockCountDown, timeDuration);
}
/// <summary>
/// 强制退出UI遮挡
/// </summary>
public void ForceExitBlock()
{
if (m_LastCountDownGuid != 0)
{
_timerModule.RemoveTimer(m_LastCountDownGuid);
}
RecoverLayerOptionAll();
}
private void OnBlockCountDown(object[] args)
{
RecoverLayerOptionAll();
}
/// <summary>
/// 设置UI是否可以操作
/// 不能提供此API对外操作
/// 因为有人设置过后就会忘记恢复
/// 如果你确实需要你可以设置 禁止无限时间
/// 之后调用恢复操作也可以做到
/// </summary>
/// <param name="value">true = 可以操作 = 屏蔽层会被隐藏</param>
private void SetLayerBlockOption(bool value)
{
m_LayerBlock.SetActive(value);
}
/// <summary>
/// 强制恢复层级到可操作状态
/// 此方法会强制打断倒计时 根据需求调用
/// </summary>
public void RecoverLayerOptionAll()
{
SetLayerBlockOption(false);
m_LastCountDownGuid = 0;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: e880f39de986cc142a161d678fd7fc84
timeCreated: 1724223150

View File

@ -1,58 +0,0 @@
using System;
using System.Collections.Generic;
using AlicizaX;
namespace AlicizaX.UI.Runtime
{
internal sealed partial class UIModule
{
private readonly Dictionary<RuntimeTypeHandle, (UIMetadata, int)> m_CacheWindow = new();
private void CacheWindow(UIMetadata uiMetadata, bool force)
{
if (uiMetadata == null || uiMetadata.View == null)
{
Log.Error(" ui not exist!");
return;
}
if (force || uiMetadata.MetaInfo.CacheTime == 0)
{
uiMetadata.Dispose();
return;
}
RemoveFromCache(uiMetadata.MetaInfo.RuntimeTypeHandle);
int tiemrId = -1;
uiMetadata.View.Holder.transform.SetParent(UICacheLayer);
if (uiMetadata.MetaInfo.CacheTime > 0)
{
tiemrId = _timerModule.AddTimer(OnTimerDiposeWindow, uiMetadata.MetaInfo.CacheTime, false, true, uiMetadata);
}
uiMetadata.InCache = true;
m_CacheWindow.Add(uiMetadata.MetaInfo.RuntimeTypeHandle, (uiMetadata, tiemrId));
}
private void OnTimerDiposeWindow(object[] args)
{
UIMetadata meta = args[0] as UIMetadata;
meta?.Dispose();
RemoveFromCache(meta.MetaInfo.RuntimeTypeHandle);
}
private void RemoveFromCache(RuntimeTypeHandle typeHandle)
{
if (m_CacheWindow.TryGetValue(typeHandle, out var result))
{
m_CacheWindow.Remove(typeHandle);
result.Item1.InCache = false;
if (result.Item2 > 0)
{
_timerModule.RemoveTimer(result.Item2);
}
}
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: ae149a329f7523044a804e3fe6769cd6
timeCreated: 1724230178

View File

@ -1,81 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
internal sealed partial class UIModule
{
public Camera UICamera { get; set; }
public Canvas UICanvas;
public Transform UICanvasRoot { get; set; }
public Transform UIRoot;
private const int UI_ROOT_OFFSET = 1000;
private const int LAYER_DISTANCE = 1000;
private const int LAYER_DEEP = 2000;
private const int WINDOW_DEEP = 100;
private readonly RectTransform[] m_AllWindowLayer = new RectTransform[(int)UILayer.All];
private RectTransform UICacheLayer;
private bool _isOrthographic;
public void Initlize(Transform root, bool isOrthographic)
{
UIRoot = root;
Object.DontDestroyOnLoad(root.gameObject);
UIRoot.transform.position = new Vector3(UI_ROOT_OFFSET, UI_ROOT_OFFSET, 0);
UICanvas = UIRoot.GetComponentInChildren<Canvas>();
UICamera = UICanvas.worldCamera;
UICanvasRoot = UICanvas.transform;
_isOrthographic = isOrthographic;
UICamera.orthographic = isOrthographic;
if (!isOrthographic)
{
UICamera.nearClipPlane = 10;
UICamera.farClipPlane = 1000;
}
const int len = (int)UILayer.All;
for (var i = len - 1; i >= 0; i--)
{
AddLayer(i);
}
AddLayer((int)UILayer.All);
InitUIBlock();
}
private void AddLayer(int layer)
{
var layerObject = new GameObject($"Layer{layer}-{(UILayer)layer}");
var rect = layerObject.AddComponent<RectTransform>();
rect.SetParent(UICanvasRoot);
rect.localScale = Vector3.one;
rect.pivot = new Vector2(0.5f, 0.5f);
rect.anchorMax = Vector2.one;
rect.anchorMin = Vector2.zero;
rect.sizeDelta = Vector2.zero;
rect.localRotation = Quaternion.identity;
rect.localPosition = new Vector3(0, 0, layer * (_isOrthographic ? LAYER_DISTANCE : 0));
if (layer == (int)UILayer.All)
{
UICacheLayer = rect;
return;
}
m_AllWindowLayer[layer] = rect;
_openUI[layer] = new LayerData(16);
}
public RectTransform GetLayerRect(int layer)
{
return m_AllWindowLayer[layer];
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: a857b714234e34545ac83be63c96147b
timeCreated: 1724154426

View File

@ -1,167 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using AlicizaX;
using Cysharp.Threading.Tasks;
namespace AlicizaX.UI.Runtime
{
readonly struct LayerData
{
public readonly List<UIMetadata> OrderList; // 维护插入顺序
public readonly HashSet<RuntimeTypeHandle> HandleSet; // O(1)存在性检查
public LayerData(int initialCapacity)
{
OrderList = new List<UIMetadata>(initialCapacity);
HandleSet = new HashSet<RuntimeTypeHandle>();
}
}
internal sealed partial class UIModule
{
private readonly LayerData[] _openUI = new LayerData[(int)UILayer.All];
private async UniTask<UIBase> ShowUIImplAsync(UIMetadata meta, params object[] userDatas)
{
var metaInfo = GetOrCreateMeta(meta);
await UIHolderFactory.CreateUIResource(metaInfo, UICacheLayer);
return await FinalizeShow(metaInfo, userDatas);
}
private async UniTask CloseUIImpl(UIMetadata meta, bool force)
{
if (meta.State == UIState.Uninitialized || meta.State == UIState.CreatedUI)
{
return;
}
await meta.View.InternalClose();
Pop(meta);
SortWindowVisible(meta.MetaInfo.UILayer);
SortWindowDepth(meta.MetaInfo.UILayer);
CacheWindow(meta, force);
}
private UIBase GetUIImpl(UIMetadata meta)
{
return meta.View;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private UIMetadata GetOrCreateMeta(UIMetadata meta)
{
if (meta.State == UIState.Uninitialized) meta.CreateUI();
return meta;
}
private async UniTask<UIBase> FinalizeShow(UIMetadata meta, object[] userDatas)
{
if (meta.InCache)
{
RemoveFromCache(meta.MetaInfo.RuntimeTypeHandle);
Push(meta);
}
else
{
switch (meta.State)
{
case UIState.Loaded:
Push(meta);
break;
case UIState.Opened:
MoveToTop(meta);
break;
}
}
meta.View.RefreshParams(userDatas);
await UpdateVisualState(meta);
return meta.View;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Push(UIMetadata meta)
{
ref var layer = ref _openUI[meta.MetaInfo.UILayer];
if (layer.HandleSet.Add(meta.MetaInfo.RuntimeTypeHandle))
{
layer.OrderList.Add(meta);
UpdateLayerParent(meta);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Pop(UIMetadata meta)
{
ref var layer = ref _openUI[meta.MetaInfo.UILayer];
if (layer.HandleSet.Remove(meta.MetaInfo.RuntimeTypeHandle))
{
layer.OrderList.Remove(meta);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateLayerParent(UIMetadata meta)
{
if (meta.View?.Holder)
{
var layerRect = GetLayerRect(meta.MetaInfo.UILayer);
meta.View.Holder.transform.SetParent(layerRect);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void MoveToTop(UIMetadata meta)
{
ref var layer = ref _openUI[meta.MetaInfo.UILayer];
int lastIdx = layer.OrderList.Count - 1;
int currentIdx = layer.OrderList.IndexOf(meta);
if (currentIdx != lastIdx && currentIdx >= 0)
{
layer.OrderList.RemoveAt(currentIdx);
layer.OrderList.Add(meta);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private async UniTask UpdateVisualState(UIMetadata meta)
{
SortWindowVisible(meta.MetaInfo.UILayer);
SortWindowDepth(meta.MetaInfo.UILayer);
if (meta.State == UIState.Loaded)
{
await meta.View.InternalInitlized();
}
await meta.View.InternalOpen();
}
private void SortWindowVisible(int layer)
{
var list = _openUI[layer].OrderList;
bool shouldHide = false;
// 反向遍历避免GC分配
for (int i = list.Count - 1; i >= 0; i--)
{
var meta = list[i];
meta.View.Visible = !shouldHide;
shouldHide |= meta.MetaInfo.FullScreen && meta.State == UIState.Opened;
}
}
private void SortWindowDepth(int layer)
{
var list = _openUI[layer].OrderList;
int baseDepth = layer * LAYER_DEEP;
for (int i = 0; i < list.Count; i++)
{
list[i].View.Depth = baseDepth + i * WINDOW_DEEP;
}
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 5374bef7b6c357f4ba05ae372ff262f6
timeCreated: 1724224258

View File

@ -1,107 +0,0 @@
using System;
using AlicizaX.Resource.Runtime;
using AlicizaX;
using AlicizaX.Timer.Runtime;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
internal sealed partial class UIModule : IUIModule
{
public int Priority { get; }
private ITimerModule _timerModule;
void IModule.Dispose()
{
}
void IModuleUpdate.Update(float elapseSeconds, float realElapseSeconds)
{
// 遍历所有层级
for (int layerIndex = 0; layerIndex < _openUI.Length; layerIndex++)
{
var layer = _openUI[layerIndex];
int count = layer.OrderList.Count;
if (count == 0) continue; // 跳过空层级
for (int i = 0; i < count; i++)
{
if (layer.OrderList.Count != count)
{
break;
}
var window = layer.OrderList[i];
window.View.InternalUpdate();
}
}
}
public UniTask<UIBase>? ShowUI(string type, params object[] userDatas)
{
if (UIMetaRegistry.TryGet(type, out var metaRegistry))
{
UIMetadata metadata = UIMetadataFactory.GetMetadata(metaRegistry.RuntimeTypeHandle);
return ShowUI(metadata, userDatas);
}
return null;
}
public UniTask<UIBase> ShowUI<T>(params System.Object[] userDatas) where T : UIBase
{
return ShowUI(MetaTypeCache<T>.Metadata, userDatas);
}
public async UniTask<T> ShowUIAsync<T>(params System.Object[] userDatas) where T : UIBase
{
return (T)await ShowUIAsync(MetaTypeCache<T>.Metadata, userDatas);
}
public void CloseUI<T>(bool force = false) where T : UIBase
{
CloseUIImpl(MetaTypeCache<T>.Metadata, force).Forget();
}
public T GetUI<T>() where T : UIBase
{
return (T)GetUI(MetaTypeCache<T>.Metadata);
}
private UniTask<UIBase> ShowUI(UIMetadata meta, params System.Object[] userDatas)
{
return ShowUIImplAsync(meta, userDatas);
}
private async UniTask<UIBase> ShowUIAsync(UIMetadata meta, params System.Object[] userDatas)
{
return await ShowUIImplAsync(meta, userDatas);
}
public void CloseUI(RuntimeTypeHandle handle, bool force = false)
{
var metadata = UIMetadataFactory.GetMetadata(handle);
if (metadata.State != UIState.Uninitialized && metadata.State != UIState.Destroying)
{
CloseUIImpl(metadata, force).Forget();
}
}
private UIBase GetUI(UIMetadata meta)
{
return (UIBase)GetUIImpl(meta);
}
void IUIModule.SetTimerManager(ITimerModule timerModule)
{
_timerModule = timerModule;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: ae16df1037186a64b922d60f431440ba
timeCreated: 1724154184

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: b963d3fe706c8a04cb734b28bac7f23a
timeCreated: 1724223096

View File

@ -1,62 +0,0 @@
using UnityEngine;
using UnityEngine.UI;
namespace AlicizaX.UI.Runtime
{
/// <summary>
/// 不可见的一个图用来阻挡UI的投射。
/// </summary>
public class UIBlock : Graphic, ICanvasRaycastFilter
{
public override bool raycastTarget
{
get => true;
set { }
}
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"StyleCop.CSharp.NamingRules",
"SA1300:ElementMustBeginWithUpperCaseLetter",
Justification = "Reviewed. Suppression is OK here.")]
public override Texture mainTexture
{
get { return null; }
}
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"StyleCop.CSharp.NamingRules",
"SA1300:ElementMustBeginWithUpperCaseLetter",
Justification = "Reviewed. Suppression is OK here.")]
public override Material materialForRendering
{
get { return null; }
}
public bool IsRaycastLocationValid(
Vector2 screenPoint, Camera eventCamera)
{
return true;
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
}
public override void SetAllDirty()
{
}
public override void SetLayoutDirty()
{
}
public override void SetVerticesDirty()
{
}
public override void SetMaterialDirty()
{
}
}
}

View File

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

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 1a64352099e341a0bedaf1a456d028a6
timeCreated: 1732264981

View File

@ -1,124 +0,0 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using AlicizaX;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Pool;
namespace AlicizaX.UI.Runtime
{
public abstract partial class UIBase
{
private readonly Dictionary<UIBase, UIMetadata> _children = new();
private void UpdateChildren()
{
var values = _children.Values;
foreach (var meta in values)
{
if (meta.View.State == UIState.Opened)
{
meta.View.InternalUpdate();
}
}
}
private async UniTask DestroyAllChildren()
{
var temp = ArrayPool<UIMetadata>.Shared.Rent(_children.Count);
try
{
int i = 0;
foreach (var kvp in _children)
{
temp[i++] = kvp.Value;
}
for (int j = 0; j < i; j++)
{
if (temp[j].View.Visible) await temp[j].View.InternalClose();
temp[j].Dispose();
}
}
finally
{
ArrayPool<UIMetadata>.Shared.Return(temp);
}
_children.Clear();
}
private void ChildVisible(bool value)
{
foreach (var meta in _children.Values)
{
var view = meta.View;
if (view.State == UIState.Opened)
{
view.Visible = value;
}
}
}
internal async UniTask<UIBase> CreateWidget(UIMetadata metadata, Transform parent, bool visible)
{
metadata.CreateUI();
await UIHolderFactory.CreateUIResource(metadata, parent, this);
await ProcessWidget(metadata, visible);
return (UIBase)metadata.View;
}
protected async UniTask<UIBase> CreateWidget(string typeName, Transform parent, bool visible = true)
{
UIMetaRegistry.TryGet(typeName, out var metaRegistry);
UIMetadata metadata = UIMetadataFactory.GetMetadata(metaRegistry.RuntimeTypeHandle);
return await CreateWidget(metadata, parent, visible);
}
protected async UniTask<T> CreateWidget<T>(Transform parent, bool visible = true) where T : UIBase
{
UIMetadata metadata = MetaTypeCache<T>.Metadata;
return (T)await CreateWidget(metadata, parent, visible);
}
protected async UniTask<T> CreateWidget<T>(UIHolderObjectBase holder) where T : UIBase
{
UIMetadata metadata = MetaTypeCache<T>.Metadata;
metadata.CreateUI();
UIBase widget = (UIBase)metadata.View;
widget.BindUIHolder(holder, this);
await ProcessWidget(metadata, true);
return (T)widget;
}
private async UniTask ProcessWidget(UIMetadata meta, bool visible)
{
AddWidget(meta);
await meta.View.InternalInitlized();
meta.View.Visible = visible;
if (meta.View.Visible)
{
await meta.View.InternalOpen();
}
}
private void AddWidget(UIMetadata meta)
{
if (!_children.TryAdd(meta.View, meta))
{
Log.Warning("Already has widget:{0}", meta.View);
meta.Dispose();
}
}
public async UniTask RemoveWidget(UIBase widget)
{
if (_children.Remove(widget, out var meta))
{
await widget.InternalClose();
meta.Dispose();
}
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 5f25caf536134515b80acdc6709ad14d
timeCreated: 1739429626

View File

@ -1,254 +0,0 @@
using System;
using System.Collections.Generic;
using AlicizaX;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace AlicizaX.UI.Runtime
{
public abstract partial class UIBase : IDisposable
{
protected UIBase()
{
_state = UIState.CreatedUI;
}
~UIBase() => Dispose(false);
private bool _disposed;
internal Canvas _canvas;
internal GraphicRaycaster _raycaster;
internal UIState _state = UIState.Uninitialized;
internal UIState State => _state;
private System.Object[] _userDatas;
protected System.Object UserData => _userDatas != null && _userDatas.Length >= 1 ? _userDatas[0] : null;
protected System.Object[] UserDatas => _userDatas;
private RuntimeTypeHandle _runtimeTypeHandle;
internal RuntimeTypeHandle RuntimeTypeHandler
{
get
{
if (_runtimeTypeHandle.Value == IntPtr.Zero)
{
_runtimeTypeHandle = GetType().TypeHandle;
}
return _runtimeTypeHandle;
}
}
protected virtual void OnInitialize()
{
}
protected virtual void OnDestroy()
{
}
protected virtual void OnOpen()
{
}
protected virtual void OnClose()
{
}
protected virtual void OnUpdate()
{
}
/// <summary>
/// 如果重写当前方法 则同步OnInitialize不会调用
/// </summary>
protected virtual async UniTask OnInitializeAsync()
{
await UniTask.CompletedTask;
OnInitialize();
}
/// <summary>
/// 如果重写当前方法 则同步OnOpen不会调用
/// </summary>
protected virtual async UniTask OnOpenAsync()
{
await UniTask.CompletedTask;
OnOpen();
}
/// <summary>
/// 如果重写当前方法 则同步OnClose不会调用
/// </summary>
protected virtual async UniTask OnCloseAsync()
{
await UniTask.CompletedTask;
OnClose();
}
/// <summary>
/// 事件在窗口销毁后会自动移除
/// </summary>
/// <param name="proxy"></param>
protected virtual void OnRegisterEvent(EventListenerProxy proxy)
{
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// 托管资源释放
_canvas = null;
_raycaster = null;
}
_userDatas = null;
// 非托管资源释放
if (Holder != null)
{
if (Application.isPlaying)
Object.Destroy(Holder.gameObject);
else
Object.DestroyImmediate(Holder.gameObject);
}
_disposed = true;
}
internal bool Visible
{
get => _canvas != null && _canvas.gameObject.layer == UIComponent.UIShowLayer;
set
{
if (_canvas != null)
{
int setLayer = value ? UIComponent.UIShowLayer : UIComponent.UIHideLayer;
if (_canvas.gameObject.layer == setLayer)
return;
_canvas.gameObject.layer = setLayer;
ChildVisible(value);
Interactable = value;
}
}
}
private bool Interactable
{
get => _raycaster.enabled && _raycaster != null;
set
{
if (_raycaster != null && _raycaster.enabled != value)
{
_raycaster.enabled = value;
}
}
}
/// <summary>
/// 窗口深度值。
/// </summary>
internal int Depth
{
get => _canvas != null ? _canvas.sortingOrder : 0;
set
{
if (_canvas != null && _canvas.sortingOrder != value)
{
// 设置父类
_canvas.sortingOrder = value;
}
}
}
#region Event
private EventListenerProxy _eventListenerProxy;
private EventListenerProxy EventListenerProxy => _eventListenerProxy ??= MemoryPool.Acquire<EventListenerProxy>();
private void ReleaseEventListenerProxy()
{
if (!_eventListenerProxy.IsNull())
{
MemoryPool.Release(_eventListenerProxy);
}
}
#endregion
#region
internal UIHolderObjectBase Holder;
internal abstract Type UIHolderType { get; }
internal abstract void BindUIHolder(UIHolderObjectBase holder,UIBase owner);
internal async UniTask InternalInitlized()
{
_state = UIState.Initialized;
await OnInitializeAsync();
OnRegisterEvent(EventListenerProxy);
}
internal async UniTask InternalOpen()
{
_state = UIState.Opened;
Visible = true;
await OnOpenAsync();
}
internal async UniTask InternalClose()
{
if (_state == UIState.Opened)
{
await OnCloseAsync();
_state = UIState.Closed;
Visible = false;
}
}
internal void InternalUpdate()
{
if (_state != UIState.Opened) return;
OnUpdate();
UpdateChildren();
}
internal async UniTask InternalDestroy()
{
_state = UIState.Destroying;
await DestroyAllChildren();
OnDestroy();
ReleaseEventListenerProxy();
Dispose();
_state = UIState.Destroyed;
}
internal void RefreshParams(params System.Object[] userDatas)
{
this._userDatas = userDatas;
}
#endregion
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 478527286cb74364a452b02eee2059e6
timeCreated: 1739272697

View File

@ -1,80 +0,0 @@
using Sirenix.OdinInspector;
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
namespace AlicizaX.UI.Runtime
{
[DisallowMultipleComponent]
[HideMonoScript]
public abstract class UIHolderObjectBase : UnityEngine.MonoBehaviour
{
#if UNITY_EDITOR
private void SetAnimtionFlow()
{
// 编辑器模式下自动重置状态
AnimationFlow = GetComponent<AnimationFlow.Runtime.AnimationFlow>();
}
#endif
public async UniTask PlayAnimtion(string name)
{
await AnimationFlow.PlayAsync(name);
}
private GameObject _target;
/// <summary>
/// UI实例资源对象。
/// </summary>
public GameObject Target => _target ??= gameObject;
private RectTransform _rectTransform;
/// <summary>
/// 窗口矩阵位置组件。
/// </summary>
public RectTransform RectTransform => _rectTransform ??= _target.transform as RectTransform;
/// <summary>
/// 可见性
/// </summary>
public bool Visible
{
get => Target.activeSelf;
internal set { _target.SetActive(value); }
}
#if UNITY_EDITOR
[InlineButton("SetAnimtionFlow")]
#endif
[SerializeField]
[HideLabel]
private AnimationFlow.Runtime.AnimationFlow AnimationFlow;
private void Awake()
{
_target = gameObject;
}
private bool IsAlive = true;
public static implicit operator bool(UIHolderObjectBase exists)
{
// 先检查Unity对象是否被销毁
if (exists == null) return false;
// 再返回自定义的生命状态
return exists.IsAlive;
}
private void OnDestroy()
{
IsAlive = false;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: f8df68ea1a364571a8c7b9ae2e2cb29b
timeCreated: 1732171853

View File

@ -1,14 +0,0 @@
namespace AlicizaX.UI.Runtime
{
public enum UIState:byte
{
Uninitialized,
CreatedUI,
Loaded,
Initialized,
Opened,
Closed,
Destroying,
Destroyed,
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: cadb58b2c31543a0aa9db1e995ab6dfe
timeCreated: 1739450978

View File

@ -1,142 +0,0 @@
using System;
using System.Collections.Generic;
using AlicizaX;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
namespace AlicizaX.UI.Runtime
{
public abstract class UITabWindow<T> : UIBase where T : UIHolderObjectBase
{
// 当前激活的Tab页
private UIWidget _activeTab;
// 类型顺序索引(根据初始化顺序)
private readonly List<RuntimeTypeHandle> _typeOrder = new();
// 页面缓存字典(类型 - 父节点)
private readonly Dictionary<RuntimeTypeHandle, Transform> _tabCache = new();
// 已加载的Tab实例缓存
private readonly Dictionary<RuntimeTypeHandle, UIWidget> _loadedTabs = new();
// 加载状态字典
private readonly Dictionary<RuntimeTypeHandle, bool> _loadingFlags = new();
protected T baseui => (T)Holder;
internal sealed override Type UIHolderType => typeof(T);
protected void CloseSelf(bool forceClose = false)
{
ModuleSystem.GetModule<IUIModule>().CloseUI(RuntimeTypeHandler, forceClose);
}
internal sealed override void BindUIHolder(UIHolderObjectBase holder, UIBase owner)
{
if (_state != UIState.CreatedUI)
throw new InvalidOperationException("UI already Created");
Holder = holder;
_canvas = Holder.transform.GetComponent<Canvas>();
_canvas.overrideSorting = true;
_raycaster = Holder.transform.GetComponent<GraphicRaycaster>();
Holder.RectTransform.localPosition = Vector3.zero;
Holder.RectTransform.pivot = new Vector2(0.5f, 0.5f);
Holder.RectTransform.anchorMin = Vector2.zero;
Holder.RectTransform.anchorMax = Vector2.one;
Holder.RectTransform.offsetMin = Vector2.zero;
Holder.RectTransform.offsetMax = Vector2.zero;
Holder.RectTransform.localScale = Vector3.one;
_state = UIState.Loaded;
}
// 初始化方法(泛型版本)
protected void InitTabVirtuallyView<TTab>(Transform parent = null) where TTab : UIWidget
{
var metadata = MetaTypeCache<TTab>.Metadata;
CacheTabMetadata(metadata, parent);
}
// 初始化方法(类型名版本)
protected void InitTabVirtuallyView(string typeName, Transform parent = null)
{
if (UIMetaRegistry.TryGet(typeName, out var metaRegistry))
{
var metadata = UIMetadataFactory.GetMetadata(metaRegistry.RuntimeTypeHandle);
CacheTabMetadata(metadata, parent);
}
}
private void CacheTabMetadata(UIMetadata metadata, Transform parent)
{
var typeHandle = metadata.MetaInfo.RuntimeTypeHandle;
if (!_tabCache.ContainsKey(typeHandle))
{
_typeOrder.Add(typeHandle);
_tabCache[typeHandle] = parent ?? baseui.RectTransform;
}
}
public void SwitchTab(int index, params System.Object[] userDatas)
{
if (!ValidateIndex(index)) return;
var typeHandle = _typeOrder[index];
if (_loadingFlags.TryGetValue(typeHandle, out var isLoading) && isLoading) return;
if (_loadedTabs.TryGetValue(typeHandle, out var loadedTab))
{
SwitchToLoadedTab(loadedTab, userDatas);
return;
}
StartAsyncLoading(typeHandle, userDatas).Forget();
}
private async UniTaskVoid StartAsyncLoading(RuntimeTypeHandle typeHandle, params System.Object[] userDatas)
{
_loadingFlags[typeHandle] = true;
try
{
var metadata = UIMetadataFactory.GetMetadata(typeHandle);
var parent = _tabCache[typeHandle];
var widget = await CreateWidget(metadata, parent, false);
if (widget is not UIWidget tabWidget) return;
_loadedTabs[typeHandle] = tabWidget;
SwitchToLoadedTab(tabWidget, userDatas);
}
catch (Exception e)
{
Debug.LogError($"Tab load failed: {e}");
}
finally
{
_loadingFlags.Remove(typeHandle);
}
}
private void SwitchToLoadedTab(UIWidget targetTab, params System.Object[] userDatas)
{
if (_activeTab == targetTab) return;
_activeTab?.Close();
_activeTab = targetTab;
targetTab.Open(userDatas);
}
private bool ValidateIndex(int index)
{
if (index >= 0 && index < _typeOrder.Count) return true;
Debug.LogError($"Invalid tab index: {index}");
return false;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 8e10ab6cf87043638060ea5f933f6d80
timeCreated: 1744200054

View File

@ -1,48 +0,0 @@
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
namespace AlicizaX.UI.Runtime
{
public abstract class UIWidget : UIBase
{
internal UIBase _parent;
internal UIBase Parent => _parent;
public void Open(params System.Object[] userDatas)
{
RefreshParams(userDatas);
InternalOpen().Forget();
}
public void Close()
{
InternalClose().Forget();
}
public void Destroy()
{
Parent.RemoveWidget(this).Forget();
}
}
public abstract class UIWidget<T> : UIWidget where T : UIHolderObjectBase
{
protected T baseui => (T)Holder;
internal sealed override Type UIHolderType => typeof(T);
internal sealed override void BindUIHolder(UIHolderObjectBase holder, UIBase owner)
{
if (_state != UIState.CreatedUI)
throw new InvalidOperationException("UI already Created");
Holder = holder;
_parent = owner;
_canvas = Holder.transform.GetComponent<Canvas>();
_raycaster = Holder.transform.GetComponent<GraphicRaycaster>();
Depth = owner.Depth + 5;
_state = UIState.Loaded;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: ed07f96c90414e28bb7ed820e88157ca
timeCreated: 1732240802

View File

@ -1,38 +0,0 @@
using System;
using System.Reflection;
using AlicizaX;
using UnityEngine;
using UnityEngine.UI;
namespace AlicizaX.UI.Runtime
{
public abstract class UIWindow<T> : UIBase where T : UIHolderObjectBase
{
protected T baseui => (T)Holder;
internal sealed override Type UIHolderType => typeof(T);
protected void CloseSelf(bool forceClose = false)
{
ModuleSystem.GetModule<IUIModule>().CloseUI(RuntimeTypeHandler, forceClose);
}
internal sealed override void BindUIHolder(UIHolderObjectBase holder, UIBase owner)
{
if (_state != UIState.CreatedUI)
throw new InvalidOperationException("UI already Created");
Holder = holder;
_canvas = Holder.transform.GetComponent<Canvas>();
_canvas.overrideSorting = true;
_raycaster = Holder.transform.GetComponent<GraphicRaycaster>();
Holder.RectTransform.localPosition = Vector3.zero;
Holder.RectTransform.pivot = new Vector2(0.5f, 0.5f);
Holder.RectTransform.anchorMin = Vector2.zero;
Holder.RectTransform.anchorMax = Vector2.one;
Holder.RectTransform.offsetMin = Vector2.zero;
Holder.RectTransform.offsetMax = Vector2.zero;
Holder.RectTransform.localScale = Vector3.one;
_state = UIState.Loaded;
}
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 43a865d7e0cd461aa06c458815dce483
timeCreated: 1732172584

View File

@ -1,112 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using AlicizaX.Resource.Runtime;
using AlicizaX;
using AlicizaX.Timer.Runtime;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using Object = UnityEngine.Object;
namespace AlicizaX.UI.Runtime
{
[DisallowMultipleComponent]
[AddComponentMenu("Game Framework/UI")]
[UnityEngine.Scripting.Preserve]
public sealed partial class UIComponent : MonoBehaviour
{
[SerializeField] private GameObject uiRoot = null;
[SerializeField] private bool _isOrthographic = true;
private Transform _instanceRoot = null;
private IUIModule _uiModule;
public const int UIHideLayer = 2; // Ignore Raycast
public const int UIShowLayer = 5; // UI
private void Awake()
{
_uiModule = ModuleSystem.RegisterModule<IUIModule,UIModule>();
if (uiRoot == null)
{
throw new GameFrameworkException("UIRoot Prefab is invalid.");
}
GameObject obj = Instantiate(uiRoot, Vector3.zero, Quaternion.identity);
obj.name = "------UI Root------";
_instanceRoot = obj.transform;
Object.DontDestroyOnLoad(_instanceRoot);
_uiModule.Initlize(_instanceRoot,_isOrthographic);
}
private void Start()
{
_uiModule.SetTimerManager(ModuleSystem.GetModule<ITimerModule>());
}
#region
/// <summary>
/// 设置屏幕安全区域(异形屏支持)。
/// </summary>
/// <param name="safeRect">安全区域</param>
public void ApplyScreenSafeRect(Rect safeRect)
{
CanvasScaler scaler = _uiModule.UICanvasRoot.GetComponent<CanvasScaler>();
if (scaler == null)
{
Log.Error($"Not found {nameof(CanvasScaler)} !");
return;
}
// Convert safe area rectangle from absolute pixels to UGUI coordinates
float rateX = scaler.referenceResolution.x / Screen.width;
float rateY = scaler.referenceResolution.y / Screen.height;
float posX = (int)(safeRect.position.x * rateX);
float posY = (int)(safeRect.position.y * rateY);
float width = (int)(safeRect.size.x * rateX);
float height = (int)(safeRect.size.y * rateY);
float offsetMaxX = scaler.referenceResolution.x - width - posX;
float offsetMaxY = scaler.referenceResolution.y - height - posY;
// 注意:安全区坐标系的原点为左下角
var rectTrans = _uiModule.UICanvasRoot.transform as RectTransform;
if (rectTrans != null)
{
rectTrans.offsetMin = new Vector2(posX, posY); //锚框状态下的屏幕左下角偏移向量
rectTrans.offsetMax = new Vector2(-offsetMaxX, -offsetMaxY); //锚框状态下的屏幕右上角偏移向量
}
}
/// <summary>
/// 模拟IPhoneX异形屏
/// </summary>
public void SimulateIPhoneXNotchScreen()
{
Rect rect;
if (Screen.height > Screen.width)
{
// 竖屏Portrait
float deviceWidth = 1125;
float deviceHeight = 2436;
rect = new Rect(0f / deviceWidth, 102f / deviceHeight, 1125f / deviceWidth, 2202f / deviceHeight);
}
else
{
// 横屏Landscape
float deviceWidth = 2436;
float deviceHeight = 1125;
rect = new Rect(132f / deviceWidth, 63f / deviceHeight, 2172f / deviceWidth, 1062f / deviceHeight);
}
Rect safeArea = new Rect(Screen.width * rect.x, Screen.height * rect.y, Screen.width * rect.width, Screen.height * rect.height);
ApplyScreenSafeRect(safeArea);
}
#endregion
}
}

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: 028204b1d2277bd4782816ee91aeed81
timeCreated: 1724156799

View File

@ -1,3 +0,0 @@
<linker>
<assembly fullname="AlicizaX.UI.Runtime" preserve="all" />
</linker>

View File

@ -1,3 +0,0 @@
fileFormatVersion: 2
guid: cb214b5e4d2dd094b95d111669559268
timeCreated: 1737098208

View File

@ -1,20 +0,0 @@
{
"name": "com.alicizax.unity.ui",
"displayName": "Aliciza X UI",
"category": "Aliciza X",
"description": "Aliciza X UI Component",
"version": "1.0.1",
"unity": "2025.1",
"keywords": [
"Aliciza X"
],
"repository": {
"name": "com.alicizax.unity",
"url": "http://101.34.252.46:3000/AlicizaX/",
"type": "git"
},
"author": {
"name": "Yuliuren",
"email": "yuliuren00@gmail.com"
}
}

View File

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