diff --git a/Assets/Scenes/main.unity b/Assets/Scenes/main.unity
index e3e2b1db..1f04538d 100644
--- a/Assets/Scenes/main.unity
+++ b/Assets/Scenes/main.unity
@@ -123,6 +123,126 @@ NavMeshSettings:
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
+--- !u!1 &23410074
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 23410075}
+ m_Layer: 0
+ m_Name: Sound
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &23410075
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 23410074}
+ 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_Children: []
+ m_Father: {fileID: 2061060682}
+ m_RootOrder: 3
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &43232119
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 43232120}
+ m_Layer: 0
+ m_Name: UI
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &43232120
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 43232119}
+ 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_Children: []
+ m_Father: {fileID: 2061060682}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &230270237
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 230270238}
+ m_Layer: 0
+ m_Name: Entity
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &230270238
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 230270237}
+ 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_Children: []
+ m_Father: {fileID: 2061060682}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1513514422
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1513514423}
+ m_Layer: 0
+ m_Name: Setting
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1513514423
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 1513514422}
+ 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_Children: []
+ m_Father: {fileID: 2061060682}
+ m_RootOrder: 4
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &2003211381
GameObject:
m_ObjectHideFlags: 0
@@ -215,6 +335,7 @@ GameObject:
serializedVersion: 6
m_Component:
- component: {fileID: 2061060682}
+ - component: {fileID: 2061060683}
m_Layer: 0
m_Name: GameMain
m_TagString: Untagged
@@ -232,7 +353,54 @@ Transform:
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_Children: []
+ m_Children:
+ - {fileID: 2108959336}
+ - {fileID: 230270238}
+ - {fileID: 43232120}
+ - {fileID: 23410075}
+ - {fileID: 1513514423}
m_Father: {fileID: 0}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2061060683
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2061060681}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: 9b7cdcfd6cb40fe489faae0b85038cb9, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+--- !u!1 &2108959335
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2108959336}
+ m_Layer: 0
+ m_Name: Debugger
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &2108959336
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2108959335}
+ 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_Children: []
+ m_Father: {fileID: 2061060682}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/TEngine/Editor/Inspector.meta b/Assets/TEngine/Editor/Inspector.meta
new file mode 100644
index 00000000..e9cd2a19
--- /dev/null
+++ b/Assets/TEngine/Editor/Inspector.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d09573e4f0fcda5449f01b4c2a126a0e
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Editor/Inspector/GameFrameworkInspector.cs b/Assets/TEngine/Editor/Inspector/GameFrameworkInspector.cs
new file mode 100644
index 00000000..078713d8
--- /dev/null
+++ b/Assets/TEngine/Editor/Inspector/GameFrameworkInspector.cs
@@ -0,0 +1,57 @@
+using UnityEditor;
+
+namespace TEngine.Editor.Inspector
+{
+ ///
+ /// 游戏框架 Inspector 抽象类。
+ ///
+ public abstract class GameFrameworkInspector : UnityEditor.Editor
+ {
+ private bool m_IsCompiling = false;
+
+ ///
+ /// 绘制事件。
+ ///
+ public override void OnInspectorGUI()
+ {
+ if (m_IsCompiling && !EditorApplication.isCompiling)
+ {
+ m_IsCompiling = false;
+ OnCompileComplete();
+ }
+ else if (!m_IsCompiling && EditorApplication.isCompiling)
+ {
+ m_IsCompiling = true;
+ OnCompileStart();
+ }
+ }
+
+ ///
+ /// 编译开始事件。
+ ///
+ protected virtual void OnCompileStart()
+ {
+ }
+
+ ///
+ /// 编译完成事件。
+ ///
+ 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
+ }
+ }
+}
diff --git a/Assets/TEngine/Editor/Inspector/GameFrameworkInspector.cs.meta b/Assets/TEngine/Editor/Inspector/GameFrameworkInspector.cs.meta
new file mode 100644
index 00000000..84266bac
--- /dev/null
+++ b/Assets/TEngine/Editor/Inspector/GameFrameworkInspector.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 334848ba90c47054b9886b13b01a44dd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Editor/TEngine.Editor.asmdef b/Assets/TEngine/Editor/TEngine.Editor.asmdef
index 17b20a86..e8c93355 100644
--- a/Assets/TEngine/Editor/TEngine.Editor.asmdef
+++ b/Assets/TEngine/Editor/TEngine.Editor.asmdef
@@ -1,6 +1,6 @@
{
"name": "TEngine.Editor",
- "rootNamespace": "",
+ "rootNamespace": "TEngine.Editor",
"references": [
"TEngine.Runtime"
],
@@ -13,6 +13,12 @@
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
- "versionDefines": [],
+ "versionDefines": [
+ {
+ "name": "com.unity.textmeshpro",
+ "expression": "",
+ "define": "ENABLE_TEXTMESHPRO"
+ }
+ ],
"noEngineReferences": false
}
\ No newline at end of file
diff --git a/Assets/TEngine/Editor/UI.meta b/Assets/TEngine/Editor/UI.meta
new file mode 100644
index 00000000..a5e92465
--- /dev/null
+++ b/Assets/TEngine/Editor/UI.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 08548058a26b3dd4683d641d88d8b6d8
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Editor/UI/ScriptGenerator.cs b/Assets/TEngine/Editor/UI/ScriptGenerator.cs
new file mode 100644
index 00000000..f7a289dc
--- /dev/null
+++ b/Assets/TEngine/Editor/UI/ScriptGenerator.cs
@@ -0,0 +1,408 @@
+using System.Collections.Generic;
+using System.Text;
+using UnityEditor;
+using UnityEngine;
+
+namespace TEngine.Editor.UI
+{
+ public class ScriptGenerator
+ {
+ private const string Gap = "/";
+
+ [MenuItem("GameObject/ScriptGenerator/UIProperty", priority = 49)]
+ public static void MemberProperty()
+ {
+ Generate(false);
+ }
+
+ [MenuItem("GameObject/ScriptGenerator/UIProperty UniTask", priority = 49)]
+ public static void MemberPropertyUniTask()
+ {
+ Generate(false, true);
+ }
+
+ [MenuItem("GameObject/ScriptGenerator/UIPropertyAndListener", priority = 49)]
+ public static void MemberPropertyAndListener()
+ {
+ Generate(true);
+ }
+
+ [MenuItem("GameObject/ScriptGenerator/UIPropertyAndListener UniTask", priority = 49)]
+ public static void MemberPropertyAndListenerUniTask()
+ {
+ Generate(true, true);
+ }
+
+ [MenuItem("GameObject/ScriptGenerator/UISwitchGroup", priority = 49)]
+ public static void UISwitchGroup()
+ {
+ var root = Selection.activeTransform;
+ if (root == null)
+ {
+ return;
+ }
+
+ var content = SwitchGroupGenerator.Instance.Process(root);
+ TextEditor te = new TextEditor();
+ te.text = content;
+ te.SelectAll();
+ te.Copy();
+ }
+
+ private static void Generate(bool includeListener, bool isUniTask = false)
+ {
+ var root = Selection.activeTransform;
+ if (root != null)
+ {
+ StringBuilder strVar = new StringBuilder();
+ StringBuilder strBind = new StringBuilder();
+ StringBuilder strOnCreate = new StringBuilder();
+ StringBuilder strCallback = new StringBuilder();
+ Ergodic(root, root, ref strVar, ref strBind, ref strOnCreate, ref strCallback, isUniTask);
+ StringBuilder strFile = new StringBuilder();
+
+ if (includeListener)
+ {
+#if ENABLE_TEXTMESHPRO
+ strFile.Append("using TMPro;\n");
+#endif
+ if (isUniTask)
+ {
+ strFile.Append("using Cysharp.Threading.Tasks;\n");
+ }
+
+ strFile.Append("using UnityEngine;\n");
+ strFile.Append("using UnityEngine.UI;\n");
+ strFile.Append("using UnityGameFramework.Runtime;\n\n");
+ strFile.Append("namespace GameMain\n");
+ strFile.Append("{\n");
+ strFile.Append("\tclass " + root.name + " : UIFormLogic\n");
+ strFile.Append("\t{\n");
+ }
+
+ // 脚本工具生成的代码
+ strFile.Append("\t\t#region 脚本工具生成的代码\n");
+ strFile.Append(strVar);
+ strFile.Append("\t\tpublic override void ScriptGenerator()\n");
+ strFile.Append("\t\t{\n");
+ strFile.Append(strBind);
+ strFile.Append(strOnCreate);
+ strFile.Append("\t\t}\n");
+ strFile.Append("\t\t#endregion");
+
+ if (includeListener)
+ {
+ strFile.Append("\n\n");
+ // #region 事件
+ strFile.Append("\t\t#region 事件\n");
+ strFile.Append(strCallback);
+ strFile.Append("\t\t#endregion\n\n");
+
+ strFile.Append("\t}\n");
+ strFile.Append("}\n");
+ }
+
+ TextEditor te = new TextEditor();
+ te.text = strFile.ToString();
+ te.SelectAll();
+ te.Copy();
+ }
+ }
+
+ private static void Ergodic(Transform root, Transform transform, ref StringBuilder strVar, ref StringBuilder strBind, ref StringBuilder strOnCreate,
+ ref StringBuilder strCallback, bool isUniTask)
+ {
+ for (int i = 0; i < transform.childCount; ++i)
+ {
+ Transform child = transform.GetChild(i);
+ WriteScript(root, child, ref strVar, ref strBind, ref strOnCreate, ref strCallback, isUniTask);
+ if (child.name.StartsWith("m_item"))
+ {
+ // 子 Item 不再往下遍历
+ continue;
+ }
+
+ Ergodic(root, child, ref strVar, ref strBind, ref strOnCreate, ref strCallback, isUniTask);
+ }
+ }
+
+ private static string GetRelativePath(Transform child, Transform root)
+ {
+ StringBuilder path = new StringBuilder();
+ path.Append(child.name);
+ while (child.parent != null && child.parent != root)
+ {
+ child = child.parent;
+ path.Insert(0, Gap);
+ path.Insert(0, child.name);
+ }
+
+ return path.ToString();
+ }
+
+ private static string GetBtnFuncName(string varName)
+ {
+ return "OnClick" + varName.Replace("m_btn", string.Empty) + "Btn";
+ }
+
+ private static string GetToggleFuncName(string varName)
+ {
+ return "OnToggle" + varName.Replace("m_toggle", string.Empty) + "Change";
+ }
+
+ private static string GetSliderFuncName(string varName)
+ {
+ return "OnSlider" + varName.Replace("m_slider", string.Empty) + "Change";
+ }
+
+ public static Dictionary dicWidget = new Dictionary()
+ {
+ { "m_go", "GameObject" },
+ { "m_item", "GameObject" },
+ { "m_tf", "Transform" },
+ { "m_rect", "RectTransform" },
+#if ENABLE_TEXTMESHPRO
+ {"m_text","TextMeshProUGUI"},
+#else
+ { "m_text", "Text" },
+#endif
+ { "m_richText", "RichTextItem" },
+ { "m_tbtn", "TextButtonItem" },
+ { "m_btn", "Button" },
+ { "m_img", "Image" },
+ { "m_rimg", "RawImage" },
+ { "m_scrollBar", "Scrollbar" },
+ { "m_scroll", "ScrollRect" },
+ { "m_input", "InputField" },
+ { "m_grid", "GridLayoutGroup" },
+ { "m_clay", "CircleLayoutGroup" },
+ { "m_hlay", "HorizontalLayoutGroup" },
+ { "m_vlay", "VerticalLayoutGroup" },
+ { "m_red", "RedNoteBehaviour" },
+ { "m_switch", "SwitchTabItem" },
+ { "m_slider", "Slider" },
+ { "m_group", "ToggleGroup" },
+ { "m_curve", "AnimationCurve" },
+ { "m_fightWidget", "CommonFightWidget" },
+ { "m_canvasGroup", "CanvasGroup" },
+#if ENABLE_TEXTMESHPRO
+ {"m_tmp","TextMeshProUGUI"},
+#endif
+ };
+
+ private static void WriteScript(Transform root, Transform child, ref StringBuilder strVar, ref StringBuilder strBind, ref StringBuilder strOnCreate,
+ ref StringBuilder strCallback, bool isUniTask)
+ {
+ string varName = child.name;
+ string varType = string.Empty;
+ foreach (var pair in dicWidget)
+ {
+ var key = pair.Key;
+ if (varName.StartsWith(key))
+ {
+ varType = pair.Value;
+ }
+ }
+
+ if (varType == string.Empty)
+ {
+ return;
+ }
+
+ string varPath = GetRelativePath(child, root);
+ if (!string.IsNullOrEmpty(varName))
+ {
+ strVar.Append("\t\tprivate " + varType + " " + varName + ";\n");
+ switch (varType)
+ {
+ case "Transform":
+ strBind.Append(string.Format("\t\t\t{0} = FindChild(\"{1}\");\n", varName, varPath));
+ break;
+ case "GameObject":
+ strBind.Append(string.Format("\t\t\t{0} = FindChild(\"{1}\").gameObject;\n", varName, varPath));
+ break;
+ case "AnimationCurve":
+ strBind.Append(string.Format("\t\t\t{0} = FindChildComponent(\"{1}\").m_animCurve;\n", varName, varPath));
+ break;
+ case "RichItemIcon":
+ case "CommonFightWidget":
+ case "PlayerHeadWidget":
+ strBind.Append(string.Format("\t\t\t{0} = CreateWidgetByType<{1}>(\"{2}\");\n", varName, varType, varPath));
+ break;
+ case "RedNoteBehaviour":
+ case "TextButtonItem":
+ case "SwitchTabItem":
+ case "UIActorWidget":
+ case "UIEffectWidget":
+ case "UISpineWidget":
+ strBind.Append(string.Format("\t\t\t{0} = CreateWidget<{1}>(\"{2}\");\n", varName, varType, varPath));
+ break;
+ case "ActorNameBinderText":
+ strBind.Append(string.Format("\t\t\t{0} = FindTextBinder(\"{1}\");\n", varName, varPath));
+ break;
+ case "ActorNameBinderEffect":
+ strBind.Append(string.Format("\t\t\t{0} = FindEffectBinder(\"{1}\");\n", varName, varPath));
+ break;
+ default:
+ strBind.Append(string.Format("\t\t\t{0} = FindChildComponent<{1}>(\"{2}\");\n", varName, varType, varPath));
+ break;
+ }
+
+ if (varType == "Button")
+ {
+ string varFuncName = GetBtnFuncName(varName);
+ if (isUniTask)
+ {
+ strOnCreate.Append(string.Format("\t\t\t{0}.onClick.AddListener(UniTask.UnityAction({1}));\n", varName, varFuncName));
+ strCallback.Append(string.Format("\t\tprivate async UniTaskVoid {0}()\n", varFuncName));
+ strCallback.Append("\t\t{\n await UniTask.Yield();\n\t\t}\n");
+ }
+ else
+ {
+ strOnCreate.Append(string.Format("\t\t\t{0}.onClick.AddListener({1});\n", varName, varFuncName));
+ strCallback.Append(string.Format("\t\tprivate void {0}()\n", varFuncName));
+ strCallback.Append("\t\t{\n\t\t}\n");
+ }
+ }
+ else if (varType == "Toggle")
+ {
+ string varFuncName = GetToggleFuncName(varName);
+ strOnCreate.Append(string.Format("\t\t\t{0}.onValueChanged.AddListener({1});\n", varName, varFuncName));
+ strCallback.Append(string.Format("\t\tprivate void {0}(bool isOn)\n", varFuncName));
+ strCallback.Append("\t\t{\n\t\t}\n");
+ }
+ else if (varType == "Slider")
+ {
+ string varFuncName = GetSliderFuncName(varName);
+ strOnCreate.Append(string.Format("\t\t\t{0}.onValueChanged.AddListener({1});\n", varName, varFuncName));
+ strCallback.Append(string.Format("\t\tprivate void {0}(float value)\n", varFuncName));
+ strCallback.Append("\t\t{\n\t\t}\n");
+ }
+ }
+ }
+
+ public class GeneratorHelper : EditorWindow
+ {
+ [MenuItem("GameObject/ScriptGenerator/About", priority = 49)]
+ public static void About()
+ {
+ GeneratorHelper welcomeWindow = (GeneratorHelper)EditorWindow.GetWindow(typeof(GeneratorHelper), false, "About");
+ }
+
+ public void Awake()
+ {
+ minSize = new Vector2(400, 600);
+ }
+
+ protected void OnGUI()
+ {
+ GUILayout.BeginVertical();
+ foreach (var item in ScriptGenerator.dicWidget)
+ {
+ GUILayout.Label(item.Key + ":\t" + item.Value);
+ }
+ }
+ }
+
+ public class SwitchGroupGeneratorHelper : EditorWindow
+ {
+ [MenuItem("GameObject/ScriptGenerator/AboutSwitchGroup", priority = 50)]
+ public static void About()
+ {
+ GetWindow(typeof(SwitchGroupGeneratorHelper), false, "AboutSwitchGroup");
+ }
+
+ public void Awake()
+ {
+ minSize = new Vector2(400, 600);
+ }
+
+ protected void OnGUI()
+ {
+ GUILayout.BeginVertical();
+ GUILayout.Label(SwitchGroupGenerator.CONDITION + ":\t" + "SwitchTabItem[]");
+ }
+ }
+
+ public class SwitchGroupGenerator
+ {
+ /*
+ 遍历子节点,找到所有名为 m_switchGroup 开始的节点,输出该节点
+ */
+
+ public const string CONDITION = "m_switchGroup";
+
+ public static readonly SwitchGroupGenerator Instance = new SwitchGroupGenerator();
+
+ public string Process(Transform root)
+ {
+ var sbd = new StringBuilder();
+ var list = new List();
+ Collect(root, list);
+ foreach (var node in list)
+ {
+ sbd.AppendLine(Process(root, node)).AppendLine();
+ }
+
+ return sbd.ToString();
+ }
+
+ public void Collect(Transform node, List nodeList)
+ {
+ if (node.name.StartsWith(CONDITION))
+ {
+ nodeList.Add(node);
+ return;
+ }
+
+ var childCnt = node.childCount;
+ for (var i = 0; i < childCnt; i++)
+ {
+ var child = node.GetChild(i);
+ Collect(child, nodeList);
+ }
+ }
+
+ public string Process(Transform root, Transform groupTf)
+ {
+ var parentPath = GetPath(root, groupTf);
+ var _name = groupTf.name;
+ var sbd = new StringBuilder(@"
+var _namePath = ""#parentPath"";
+var _nameTf = FindChild(_namePath);
+var childCnt = _nameTf.childCount;
+SwitchTabItem[] _name;
+_name = new SwitchTabItem[childCnt];
+for (var i = 0; i < childCnt; i++)
+{
+ var child = _nameTf.GetChild(i);
+ _name[i] = CreateWidget(_namePath + ""/"" + child.name);
+}");
+ sbd.Replace("_name", _name);
+ sbd.Replace("#parentPath", parentPath);
+ return sbd.ToString();
+ }
+
+ public string GetPath(Transform root, Transform childTf)
+ {
+ if (childTf == null)
+ {
+ return string.Empty;
+ }
+
+ if (childTf == root)
+ {
+ return childTf.name;
+ }
+
+ var parentPath = GetPath(root, childTf.parent);
+ if (parentPath == string.Empty)
+ {
+ return childTf.name;
+ }
+
+ return parentPath + "/" + childTf.name;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/TEngine/Editor/UI/ScriptGenerator.cs.meta b/Assets/TEngine/Editor/UI/ScriptGenerator.cs.meta
new file mode 100644
index 00000000..5934b0f1
--- /dev/null
+++ b/Assets/TEngine/Editor/UI/ScriptGenerator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5cde6b81462affb4bac5ca8cd0862f38
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Editor/UniTask.meta b/Assets/TEngine/Editor/UniTask.meta
new file mode 100644
index 00000000..cc05f302
--- /dev/null
+++ b/Assets/TEngine/Editor/UniTask.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f6673f99eb7822940b3c975bbaf0ddb8
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Editor/UniTask/EditorRunnerChecker.cs b/Assets/TEngine/Editor/UniTask/EditorRunnerChecker.cs
new file mode 100644
index 00000000..d4afbc8a
--- /dev/null
+++ b/Assets/TEngine/Editor/UniTask/EditorRunnerChecker.cs
@@ -0,0 +1,33 @@
+#if UNITY_EDITOR
+
+using Cysharp.Threading.Tasks;
+using System;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Networking;
+
+public static class EditorRunnerChecker
+{
+ [MenuItem("Tools/UniTaskEditorRunnerChecker")]
+ public static void RunUniTaskAsync()
+ {
+ RunCore().Forget();
+ }
+
+ static async UniTaskVoid RunCore()
+ {
+ Debug.Log("Start");
+
+ //var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
+ //Debug.Log(r.downloadHandler.text.Substring(0, 100));
+ //await UniTask.Yield();
+
+ await UniTask.DelayFrame(30);
+
+ Debug.Log("End");
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/Assets/TEngine/Editor/UniTask/EditorRunnerChecker.cs.meta b/Assets/TEngine/Editor/UniTask/EditorRunnerChecker.cs.meta
new file mode 100644
index 00000000..f6a8be7a
--- /dev/null
+++ b/Assets/TEngine/Editor/UniTask/EditorRunnerChecker.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e51b78c06cb410f42b36e0af9de3b065
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Editor/UniTask/SplitterGUILayout.cs b/Assets/TEngine/Editor/UniTask/SplitterGUILayout.cs
new file mode 100644
index 00000000..41891337
--- /dev/null
+++ b/Assets/TEngine/Editor/UniTask/SplitterGUILayout.cs
@@ -0,0 +1,62 @@
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+using System;
+using System.Linq;
+using System.Reflection;
+using UnityEditor;
+using UnityEngine;
+
+namespace Cysharp.Threading.Tasks.Editor
+{
+ // reflection call of UnityEditor.SplitterGUILayout
+ internal static class SplitterGUILayout
+ {
+ static BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;
+
+ static Lazy splitterStateType = new Lazy(() =>
+ {
+ var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterState");
+ return type;
+ });
+
+ static Lazy splitterStateCtor = new Lazy(() =>
+ {
+ var type = splitterStateType.Value;
+ return type.GetConstructor(flags, null, new Type[] { typeof(float[]), typeof(int[]), typeof(int[]) }, null);
+ });
+
+ static Lazy splitterGUILayoutType = new Lazy(() =>
+ {
+ var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterGUILayout");
+ return type;
+ });
+
+ static Lazy beginVerticalSplit = new Lazy(() =>
+ {
+ var type = splitterGUILayoutType.Value;
+ return type.GetMethod("BeginVerticalSplit", flags, null, new Type[] { splitterStateType.Value, typeof(GUILayoutOption[]) }, null);
+ });
+
+ static Lazy endVerticalSplit = new Lazy(() =>
+ {
+ var type = splitterGUILayoutType.Value;
+ return type.GetMethod("EndVerticalSplit", flags, null, Type.EmptyTypes, null);
+ });
+
+ public static object CreateSplitterState(float[] relativeSizes, int[] minSizes, int[] maxSizes)
+ {
+ return splitterStateCtor.Value.Invoke(new object[] { relativeSizes, minSizes, maxSizes });
+ }
+
+ public static void BeginVerticalSplit(object splitterState, params GUILayoutOption[] options)
+ {
+ beginVerticalSplit.Value.Invoke(null, new object[] { splitterState, options });
+ }
+
+ public static void EndVerticalSplit()
+ {
+ endVerticalSplit.Value.Invoke(null, Type.EmptyTypes);
+ }
+ }
+}
+
diff --git a/Assets/TEngine/Editor/UniTask/SplitterGUILayout.cs.meta b/Assets/TEngine/Editor/UniTask/SplitterGUILayout.cs.meta
new file mode 100644
index 00000000..4d718f4e
--- /dev/null
+++ b/Assets/TEngine/Editor/UniTask/SplitterGUILayout.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 40ef2e46f900131419e869398a8d3c9d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Editor/UniTask/UniTaskTrackerTreeView.cs b/Assets/TEngine/Editor/UniTask/UniTaskTrackerTreeView.cs
new file mode 100644
index 00000000..e7b62692
--- /dev/null
+++ b/Assets/TEngine/Editor/UniTask/UniTaskTrackerTreeView.cs
@@ -0,0 +1,182 @@
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+using UnityEngine;
+using UnityEditor;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System;
+using UnityEditor.IMGUI.Controls;
+using Cysharp.Threading.Tasks.Internal;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace Cysharp.Threading.Tasks.Editor
+{
+ public class UniTaskTrackerViewItem : TreeViewItem
+ {
+ static Regex removeHref = new Regex("(.+)", RegexOptions.Compiled);
+
+ public string TaskType { get; set; }
+ public string Elapsed { get; set; }
+ public string Status { get; set; }
+
+ string position;
+ public string Position
+ {
+ get { return position; }
+ set
+ {
+ position = value;
+ PositionFirstLine = GetFirstLine(position);
+ }
+ }
+
+ public string PositionFirstLine { get; private set; }
+
+ static string GetFirstLine(string str)
+ {
+ var sb = new StringBuilder();
+ for (int i = 0; i < str.Length; i++)
+ {
+ if (str[i] == '\r' || str[i] == '\n')
+ {
+ break;
+ }
+ sb.Append(str[i]);
+ }
+
+ return removeHref.Replace(sb.ToString(), "$1");
+ }
+
+ public UniTaskTrackerViewItem(int id) : base(id)
+ {
+
+ }
+ }
+
+ public class UniTaskTrackerTreeView : TreeView
+ {
+ const string sortedColumnIndexStateKey = "UniTaskTrackerTreeView_sortedColumnIndex";
+
+ public IReadOnlyList CurrentBindingItems;
+
+ public UniTaskTrackerTreeView()
+ : this(new TreeViewState(), new MultiColumnHeader(new MultiColumnHeaderState(new[]
+ {
+ new MultiColumnHeaderState.Column() { headerContent = new GUIContent("TaskType"), width = 20},
+ new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Elapsed"), width = 10},
+ new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Status"), width = 10},
+ new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Position")},
+ })))
+ {
+ }
+
+ UniTaskTrackerTreeView(TreeViewState state, MultiColumnHeader header)
+ : base(state, header)
+ {
+ rowHeight = 20;
+ showAlternatingRowBackgrounds = true;
+ showBorder = true;
+ header.sortingChanged += Header_sortingChanged;
+
+ header.ResizeToFit();
+ Reload();
+
+ header.sortedColumnIndex = SessionState.GetInt(sortedColumnIndexStateKey, 1);
+ }
+
+ public void ReloadAndSort()
+ {
+ var currentSelected = this.state.selectedIDs;
+ Reload();
+ Header_sortingChanged(this.multiColumnHeader);
+ this.state.selectedIDs = currentSelected;
+ }
+
+ private void Header_sortingChanged(MultiColumnHeader multiColumnHeader)
+ {
+ SessionState.SetInt(sortedColumnIndexStateKey, multiColumnHeader.sortedColumnIndex);
+ var index = multiColumnHeader.sortedColumnIndex;
+ var ascending = multiColumnHeader.IsSortedAscending(multiColumnHeader.sortedColumnIndex);
+
+ var items = rootItem.children.Cast();
+
+ IOrderedEnumerable orderedEnumerable;
+ switch (index)
+ {
+ case 0:
+ orderedEnumerable = ascending ? items.OrderBy(item => item.TaskType) : items.OrderByDescending(item => item.TaskType);
+ break;
+ case 1:
+ orderedEnumerable = ascending ? items.OrderBy(item => double.Parse(item.Elapsed)) : items.OrderByDescending(item => double.Parse(item.Elapsed));
+ break;
+ case 2:
+ orderedEnumerable = ascending ? items.OrderBy(item => item.Status) : items.OrderByDescending(item => item.Elapsed);
+ break;
+ case 3:
+ orderedEnumerable = ascending ? items.OrderBy(item => item.Position) : items.OrderByDescending(item => item.PositionFirstLine);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(index), index, null);
+ }
+
+ CurrentBindingItems = rootItem.children = orderedEnumerable.Cast().ToList();
+ BuildRows(rootItem);
+ }
+
+ protected override TreeViewItem BuildRoot()
+ {
+ var root = new TreeViewItem { depth = -1 };
+
+ var children = new List();
+
+ TaskTracker.ForEachActiveTask((trackingId, awaiterType, status, created, stackTrace) =>
+ {
+ children.Add(new UniTaskTrackerViewItem(trackingId) { TaskType = awaiterType, Status = status.ToString(), Elapsed = (DateTime.UtcNow - created).TotalSeconds.ToString("00.00"), Position = stackTrace });
+ });
+
+ CurrentBindingItems = children;
+ root.children = CurrentBindingItems as List;
+ return root;
+ }
+
+ protected override bool CanMultiSelect(TreeViewItem item)
+ {
+ return false;
+ }
+
+ protected override void RowGUI(RowGUIArgs args)
+ {
+ var item = args.item as UniTaskTrackerViewItem;
+
+ for (var visibleColumnIndex = 0; visibleColumnIndex < args.GetNumVisibleColumns(); visibleColumnIndex++)
+ {
+ var rect = args.GetCellRect(visibleColumnIndex);
+ var columnIndex = args.GetColumn(visibleColumnIndex);
+
+ var labelStyle = args.selected ? EditorStyles.whiteLabel : EditorStyles.label;
+ labelStyle.alignment = TextAnchor.MiddleLeft;
+ switch (columnIndex)
+ {
+ case 0:
+ EditorGUI.LabelField(rect, item.TaskType, labelStyle);
+ break;
+ case 1:
+ EditorGUI.LabelField(rect, item.Elapsed, labelStyle);
+ break;
+ case 2:
+ EditorGUI.LabelField(rect, item.Status, labelStyle);
+ break;
+ case 3:
+ EditorGUI.LabelField(rect, item.PositionFirstLine, labelStyle);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(columnIndex), columnIndex, null);
+ }
+ }
+ }
+ }
+
+}
+
diff --git a/Assets/TEngine/Editor/UniTask/UniTaskTrackerTreeView.cs.meta b/Assets/TEngine/Editor/UniTask/UniTaskTrackerTreeView.cs.meta
new file mode 100644
index 00000000..9b34d7b9
--- /dev/null
+++ b/Assets/TEngine/Editor/UniTask/UniTaskTrackerTreeView.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 52e2d973a2156674e8c1c9433ed031f7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Editor/UniTask/UniTaskTrackerWindow.cs b/Assets/TEngine/Editor/UniTask/UniTaskTrackerWindow.cs
new file mode 100644
index 00000000..242ac6db
--- /dev/null
+++ b/Assets/TEngine/Editor/UniTask/UniTaskTrackerWindow.cs
@@ -0,0 +1,209 @@
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+using UnityEngine;
+using UnityEditor;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System;
+using UnityEditor.IMGUI.Controls;
+using Cysharp.Threading.Tasks.Internal;
+
+namespace Cysharp.Threading.Tasks.Editor
+{
+ public class UniTaskTrackerWindow : EditorWindow
+ {
+ static int interval;
+
+ static UniTaskTrackerWindow window;
+
+ [MenuItem("Window/UniTask Tracker")]
+ public static void OpenWindow()
+ {
+ if (window != null)
+ {
+ window.Close();
+ }
+
+ // will called OnEnable(singleton instance will be set).
+ GetWindow("UniTask Tracker").Show();
+ }
+
+ static readonly GUILayoutOption[] EmptyLayoutOption = new GUILayoutOption[0];
+
+ UniTaskTrackerTreeView treeView;
+ object splitterState;
+
+ void OnEnable()
+ {
+ window = this; // set singleton.
+ splitterState = SplitterGUILayout.CreateSplitterState(new float[] { 75f, 25f }, new int[] { 32, 32 }, null);
+ treeView = new UniTaskTrackerTreeView();
+ TaskTracker.EditorEnableState.EnableAutoReload = EditorPrefs.GetBool(TaskTracker.EnableAutoReloadKey, false);
+ TaskTracker.EditorEnableState.EnableTracking = EditorPrefs.GetBool(TaskTracker.EnableTrackingKey, false);
+ TaskTracker.EditorEnableState.EnableStackTrace = EditorPrefs.GetBool(TaskTracker.EnableStackTraceKey, false);
+ }
+
+ void OnGUI()
+ {
+ // Head
+ RenderHeadPanel();
+
+ // Splittable
+ SplitterGUILayout.BeginVerticalSplit(this.splitterState, EmptyLayoutOption);
+ {
+ // Column Tabble
+ RenderTable();
+
+ // StackTrace details
+ RenderDetailsPanel();
+ }
+ SplitterGUILayout.EndVerticalSplit();
+ }
+
+ #region HeadPanel
+
+ public static bool EnableAutoReload => TaskTracker.EditorEnableState.EnableAutoReload;
+ public static bool EnableTracking => TaskTracker.EditorEnableState.EnableTracking;
+ public static bool EnableStackTrace => TaskTracker.EditorEnableState.EnableStackTrace;
+ static readonly GUIContent EnableAutoReloadHeadContent = EditorGUIUtility.TrTextContent("Enable AutoReload", "Reload automatically.", (Texture)null);
+ static readonly GUIContent ReloadHeadContent = EditorGUIUtility.TrTextContent("Reload", "Reload View.", (Texture)null);
+ static readonly GUIContent GCHeadContent = EditorGUIUtility.TrTextContent("GC.Collect", "Invoke GC.Collect.", (Texture)null);
+ static readonly GUIContent EnableTrackingHeadContent = EditorGUIUtility.TrTextContent("Enable Tracking", "Start to track async/await UniTask. Performance impact: low", (Texture)null);
+ static readonly GUIContent EnableStackTraceHeadContent = EditorGUIUtility.TrTextContent("Enable StackTrace", "Capture StackTrace when task is started. Performance impact: high", (Texture)null);
+
+ // [Enable Tracking] | [Enable StackTrace]
+ void RenderHeadPanel()
+ {
+ EditorGUILayout.BeginVertical(EmptyLayoutOption);
+ EditorGUILayout.BeginHorizontal(EditorStyles.toolbar, EmptyLayoutOption);
+
+ if (GUILayout.Toggle(EnableAutoReload, EnableAutoReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableAutoReload)
+ {
+ TaskTracker.EditorEnableState.EnableAutoReload = !EnableAutoReload;
+ }
+
+ if (GUILayout.Toggle(EnableTracking, EnableTrackingHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableTracking)
+ {
+ TaskTracker.EditorEnableState.EnableTracking = !EnableTracking;
+ }
+
+ if (GUILayout.Toggle(EnableStackTrace, EnableStackTraceHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableStackTrace)
+ {
+ TaskTracker.EditorEnableState.EnableStackTrace = !EnableStackTrace;
+ }
+
+ GUILayout.FlexibleSpace();
+
+ if (GUILayout.Button(ReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption))
+ {
+ TaskTracker.CheckAndResetDirty();
+ treeView.ReloadAndSort();
+ Repaint();
+ }
+
+ if (GUILayout.Button(GCHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption))
+ {
+ GC.Collect(0);
+ }
+
+ EditorGUILayout.EndHorizontal();
+ EditorGUILayout.EndVertical();
+ }
+
+ #endregion
+
+ #region TableColumn
+
+ Vector2 tableScroll;
+ GUIStyle tableListStyle;
+
+ void RenderTable()
+ {
+ if (tableListStyle == null)
+ {
+ tableListStyle = new GUIStyle("CN Box");
+ tableListStyle.margin.top = 0;
+ tableListStyle.padding.left = 3;
+ }
+
+ EditorGUILayout.BeginVertical(tableListStyle, EmptyLayoutOption);
+
+ this.tableScroll = EditorGUILayout.BeginScrollView(this.tableScroll, new GUILayoutOption[]
+ {
+ GUILayout.ExpandWidth(true),
+ GUILayout.MaxWidth(2000f)
+ });
+ var controlRect = EditorGUILayout.GetControlRect(new GUILayoutOption[]
+ {
+ GUILayout.ExpandHeight(true),
+ GUILayout.ExpandWidth(true)
+ });
+
+
+ treeView?.OnGUI(controlRect);
+
+ EditorGUILayout.EndScrollView();
+ EditorGUILayout.EndVertical();
+ }
+
+ private void Update()
+ {
+ if (EnableAutoReload)
+ {
+ if (interval++ % 120 == 0)
+ {
+ if (TaskTracker.CheckAndResetDirty())
+ {
+ treeView.ReloadAndSort();
+ Repaint();
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Details
+
+ static GUIStyle detailsStyle;
+ Vector2 detailsScroll;
+
+ void RenderDetailsPanel()
+ {
+ if (detailsStyle == null)
+ {
+ detailsStyle = new GUIStyle("CN Message");
+ detailsStyle.wordWrap = false;
+ detailsStyle.stretchHeight = true;
+ detailsStyle.margin.right = 15;
+ }
+
+ string message = "";
+ var selected = treeView.state.selectedIDs;
+ if (selected.Count > 0)
+ {
+ var first = selected[0];
+ var item = treeView.CurrentBindingItems.FirstOrDefault(x => x.id == first) as UniTaskTrackerViewItem;
+ if (item != null)
+ {
+ message = item.Position;
+ }
+ }
+
+ detailsScroll = EditorGUILayout.BeginScrollView(this.detailsScroll, EmptyLayoutOption);
+ var vector = detailsStyle.CalcSize(new GUIContent(message));
+ EditorGUILayout.SelectableLabel(message, detailsStyle, new GUILayoutOption[]
+ {
+ GUILayout.ExpandHeight(true),
+ GUILayout.ExpandWidth(true),
+ GUILayout.MinWidth(vector.x),
+ GUILayout.MinHeight(vector.y)
+ });
+ EditorGUILayout.EndScrollView();
+ }
+
+ #endregion
+ }
+}
+
diff --git a/Assets/TEngine/Editor/UniTask/UniTaskTrackerWindow.cs.meta b/Assets/TEngine/Editor/UniTask/UniTaskTrackerWindow.cs.meta
new file mode 100644
index 00000000..ba1b7045
--- /dev/null
+++ b/Assets/TEngine/Editor/UniTask/UniTaskTrackerWindow.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5bee3e3860e37484aa3b861bf76d129f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/GameMain.cs b/Assets/TEngine/Runtime/GameMain.cs
new file mode 100644
index 00000000..9b2600f5
--- /dev/null
+++ b/Assets/TEngine/Runtime/GameMain.cs
@@ -0,0 +1,18 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class GameMain : MonoBehaviour
+{
+ // Start is called before the first frame update
+ void Start()
+ {
+
+ }
+
+ // Update is called once per frame
+ void Update()
+ {
+
+ }
+}
diff --git a/Assets/TEngine/Runtime/GameMain.cs.meta b/Assets/TEngine/Runtime/GameMain.cs.meta
new file mode 100644
index 00000000..10976261
--- /dev/null
+++ b/Assets/TEngine/Runtime/GameMain.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9b7cdcfd6cb40fe489faae0b85038cb9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/TEngine/Runtime/TEngine.Runtime.asmdef b/Assets/TEngine/Runtime/TEngine.Runtime.asmdef
index 27c65a55..f742a16b 100644
--- a/Assets/TEngine/Runtime/TEngine.Runtime.asmdef
+++ b/Assets/TEngine/Runtime/TEngine.Runtime.asmdef
@@ -1,3 +1,45 @@
{
- "name": "TEngine.Runtime"
-}
+ "name": "TEngine.Runtime",
+ "rootNamespace": "TEngine.Runtime",
+ "references": [],
+ "includePlatforms": [],
+ "excludePlatforms": [],
+ "allowUnsafeCode": true,
+ "overrideReferences": false,
+ "precompiledReferences": [],
+ "autoReferenced": true,
+ "defineConstraints": [],
+ "versionDefines": [
+ {
+ "name": "com.unity.modules.assetbundle",
+ "expression": "",
+ "define": "UNITASK_ASSETBUNDLE_SUPPORT"
+ },
+ {
+ "name": "com.unity.modules.physics",
+ "expression": "",
+ "define": "UNITASK_PHYSICS_SUPPORT"
+ },
+ {
+ "name": "com.unity.modules.physics2d",
+ "expression": "",
+ "define": "UNITASK_PHYSICS2D_SUPPORT"
+ },
+ {
+ "name": "com.unity.ugui",
+ "expression": "",
+ "define": "UNITASK_UGUI_SUPPORT"
+ },
+ {
+ "name": "com.unity.modules.unitywebrequest",
+ "expression": "",
+ "define": "UNITASK_WEBREQUEST_SUPPORT"
+ },
+ {
+ "name": "com.unity.modules.particlesystem",
+ "expression": "",
+ "define": "UNITASK_PARTICLESYSTEM_SUPPORT"
+ }
+ ],
+ "noEngineReferences": false
+}
\ No newline at end of file
diff --git a/Assets/TEngine/Runtime/UniTask.meta b/Assets/TEngine/Runtime/UniTask.meta
new file mode 100644
index 00000000..1c41b054
--- /dev/null
+++ b/Assets/TEngine/Runtime/UniTask.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 2d25b22b472145d69e97172204ce86ea
+timeCreated: 1680255942
\ No newline at end of file
diff --git a/Assets/TEngine/Runtime/UniTask/AsyncLazy.cs b/Assets/TEngine/Runtime/UniTask/AsyncLazy.cs
new file mode 100644
index 00000000..51bfadc7
--- /dev/null
+++ b/Assets/TEngine/Runtime/UniTask/AsyncLazy.cs
@@ -0,0 +1,245 @@
+#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+
+using System;
+using System.Threading;
+
+namespace Cysharp.Threading.Tasks
+{
+ public class AsyncLazy
+ {
+ static Action