diff --git a/Assets/GameMain/DataTables/tbglobalconfig.bytes b/Assets/GameMain/DataTables/tbglobalconfig.bytes
index a43fb97..80bd62d 100644
Binary files a/Assets/GameMain/DataTables/tbglobalconfig.bytes and b/Assets/GameMain/DataTables/tbglobalconfig.bytes differ
diff --git a/Assets/GameMain/DataTables/tbglobalconfig.json b/Assets/GameMain/DataTables/tbglobalconfig.json
index 730d253..a81057f 100644
--- a/Assets/GameMain/DataTables/tbglobalconfig.json
+++ b/Assets/GameMain/DataTables/tbglobalconfig.json
@@ -1,5 +1,12 @@
[
{
- "weaponCapacity": 6
+ "weaponCapacity": 6,
+ "weaponRecycleRate": 300,
+ "shopGoodsCount": 4,
+ "shopPriceRandomPercent": 50,
+ "shopMinPrice": 0,
+ "propMaxCount": -1,
+ "levelUpRefreshPriceBase": 2,
+ "levelUpRewardCount": 4
}
]
\ No newline at end of file
diff --git a/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs b/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs
index 945c1e2..e17127c 100644
--- a/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs
+++ b/Assets/GameMain/Scripts/Base/Gen/GlobalConfig.cs
@@ -17,6 +17,13 @@ public sealed partial class GlobalConfig : Luban.BeanBase
public GlobalConfig(ByteBuf _buf)
{
WeaponCapacity = _buf.ReadInt();
+ WeaponRecycleRate = _buf.ReadInt();
+ ShopGoodsCount = _buf.ReadInt();
+ ShopPriceRandomPercent = _buf.ReadInt();
+ ShopMinPrice = _buf.ReadInt();
+ PropMaxCount = _buf.ReadInt();
+ LevelUpRefreshPriceBase = _buf.ReadInt();
+ LevelUpRewardCount = _buf.ReadInt();
}
public static GlobalConfig DeserializeGlobalConfig(ByteBuf _buf)
@@ -28,6 +35,34 @@ public sealed partial class GlobalConfig : Luban.BeanBase
/// 最大持有武器数量
///
public readonly int WeaponCapacity;
+ ///
+ /// 武器回收价比例
+ ///
+ public readonly int WeaponRecycleRate;
+ ///
+ /// 商店商品数量
+ ///
+ public readonly int ShopGoodsCount;
+ ///
+ /// 商品价格浮动
+ ///
+ public readonly int ShopPriceRandomPercent;
+ ///
+ /// 商品最低价
+ ///
+ public readonly int ShopMinPrice;
+ ///
+ /// 最大持有道具数量
+ ///
+ public readonly int PropMaxCount;
+ ///
+ /// 升级奖励刷新基本价格
+ ///
+ public readonly int LevelUpRefreshPriceBase;
+ ///
+ /// 升级奖励候选数
+ ///
+ public readonly int LevelUpRewardCount;
public const int __ID__ = -958250779;
public override int GetTypeId() => __ID__;
@@ -35,12 +70,26 @@ public sealed partial class GlobalConfig : Luban.BeanBase
public void ResolveRef(Tables tables)
{
+
+
+
+
+
+
+
}
public override string ToString()
{
return "{ "
+ "weaponCapacity:" + WeaponCapacity + ","
+ + "weaponRecycleRate:" + WeaponRecycleRate + ","
+ + "shopGoodsCount:" + ShopGoodsCount + ","
+ + "shopPriceRandomPercent:" + ShopPriceRandomPercent + ","
+ + "shopMinPrice:" + ShopMinPrice + ","
+ + "propMaxCount:" + PropMaxCount + ","
+ + "levelUpRefreshPriceBase:" + LevelUpRefreshPriceBase + ","
+ + "levelUpRewardCount:" + LevelUpRewardCount + ","
+ "}";
}
}
diff --git a/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs b/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs
index 89c2894..d2833c7 100644
--- a/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs
+++ b/Assets/GameMain/Scripts/Base/Gen/TbGlobalConfig.cs
@@ -34,6 +34,34 @@ public partial class TbGlobalConfig
/// 最大持有武器数量
///
public int WeaponCapacity => _data.WeaponCapacity;
+ ///
+ /// 武器回收价比例
+ ///
+ public int WeaponRecycleRate => _data.WeaponRecycleRate;
+ ///
+ /// 商店商品数量
+ ///
+ public int ShopGoodsCount => _data.ShopGoodsCount;
+ ///
+ /// 商品价格浮动
+ ///
+ public int ShopPriceRandomPercent => _data.ShopPriceRandomPercent;
+ ///
+ /// 商品最低价
+ ///
+ public int ShopMinPrice => _data.ShopMinPrice;
+ ///
+ /// 最大持有道具数量
+ ///
+ public int PropMaxCount => _data.PropMaxCount;
+ ///
+ /// 升级奖励刷新基本价格
+ ///
+ public int LevelUpRefreshPriceBase => _data.LevelUpRefreshPriceBase;
+ ///
+ /// 升级奖励候选数
+ ///
+ public int LevelUpRewardCount => _data.LevelUpRewardCount;
public void ResolveRef(Tables tables)
{
diff --git a/Assets/GameMain/Scripts/Presentation/Menu/Menu.meta b/Assets/GameMain/Scripts/Editor/UI.meta
similarity index 77%
rename from Assets/GameMain/Scripts/Presentation/Menu/Menu.meta
rename to Assets/GameMain/Scripts/Editor/UI.meta
index 1b1207b..154c9fe 100644
--- a/Assets/GameMain/Scripts/Presentation/Menu/Menu.meta
+++ b/Assets/GameMain/Scripts/Editor/UI.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 88a351268ca28c14fb18659fe3d042d3
+guid: 3c650e4873fd47548b7df088c59adfad
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Assets/GameMain/Scripts/Runtime/UIBase/Menu/Setting.meta b/Assets/GameMain/Scripts/Editor/UI/Joystick.meta
similarity index 77%
rename from Assets/GameMain/Scripts/Runtime/UIBase/Menu/Setting.meta
rename to Assets/GameMain/Scripts/Editor/UI/Joystick.meta
index c833997..0e6d700 100644
--- a/Assets/GameMain/Scripts/Runtime/UIBase/Menu/Setting.meta
+++ b/Assets/GameMain/Scripts/Editor/UI/Joystick.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: 35c8961f073eae8479506bdec8b745f8
+guid: 10f3a6fad1dfdb444a2e57be170f9967
folderAsset: yes
DefaultImporter:
externalObjects: {}
diff --git a/Assets/GameMain/Scripts/Editor/UI/Joystick/DynamicJoystickEditor.cs b/Assets/GameMain/Scripts/Editor/UI/Joystick/DynamicJoystickEditor.cs
new file mode 100644
index 0000000..1da7f72
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/Joystick/DynamicJoystickEditor.cs
@@ -0,0 +1,35 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+
+[CustomEditor(typeof(DynamicJoystick))]
+public class DynamicJoystickEditor : JoystickEditor
+{
+ private SerializedProperty moveThreshold;
+
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+ moveThreshold = serializedObject.FindProperty("moveThreshold");
+ }
+
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+
+ if (background != null)
+ {
+ RectTransform backgroundRect = (RectTransform)background.objectReferenceValue;
+ backgroundRect.anchorMax = Vector2.zero;
+ backgroundRect.anchorMin = Vector2.zero;
+ backgroundRect.pivot = center;
+ }
+ }
+
+ protected override void DrawValues()
+ {
+ base.DrawValues();
+ EditorGUILayout.PropertyField(moveThreshold, new GUIContent("Move Threshold", "The distance away from the center input has to be before the joystick begins to move."));
+ }
+}
\ No newline at end of file
diff --git a/Assets/GameMain/Scripts/Presentation/Menu/SelectRole/RolePropertyArea.cs.meta b/Assets/GameMain/Scripts/Editor/UI/Joystick/DynamicJoystickEditor.cs.meta
similarity index 83%
rename from Assets/GameMain/Scripts/Presentation/Menu/SelectRole/RolePropertyArea.cs.meta
rename to Assets/GameMain/Scripts/Editor/UI/Joystick/DynamicJoystickEditor.cs.meta
index 080b853..bb13db0 100644
--- a/Assets/GameMain/Scripts/Presentation/Menu/SelectRole/RolePropertyArea.cs.meta
+++ b/Assets/GameMain/Scripts/Editor/UI/Joystick/DynamicJoystickEditor.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
-guid: e82dc413a4cd277429ec1e3f2de69434
+guid: 52071c88a467b46438b3cbf159bf988b
MonoImporter:
externalObjects: {}
serializedVersion: 2
diff --git a/Assets/GameMain/Scripts/Editor/UI/Joystick/FloatingJoystickEditor.cs b/Assets/GameMain/Scripts/Editor/UI/Joystick/FloatingJoystickEditor.cs
new file mode 100644
index 0000000..a9b079c
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/Joystick/FloatingJoystickEditor.cs
@@ -0,0 +1,21 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+
+[CustomEditor(typeof(FloatingJoystick))]
+public class FloatingJoystickEditor : JoystickEditor
+{
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+
+ if (background != null)
+ {
+ RectTransform backgroundRect = (RectTransform)background.objectReferenceValue;
+ backgroundRect.anchorMax = Vector2.zero;
+ backgroundRect.anchorMin = Vector2.zero;
+ backgroundRect.pivot = center;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/GameMain/Scripts/Editor/UI/Joystick/FloatingJoystickEditor.cs.meta b/Assets/GameMain/Scripts/Editor/UI/Joystick/FloatingJoystickEditor.cs.meta
new file mode 100644
index 0000000..1026f94
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/Joystick/FloatingJoystickEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 614d06243900d764f9c50b4da2c950a3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/GameMain/Scripts/Editor/UI/Joystick/JoystickEditor.cs b/Assets/GameMain/Scripts/Editor/UI/Joystick/JoystickEditor.cs
new file mode 100644
index 0000000..de94efd
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/Joystick/JoystickEditor.cs
@@ -0,0 +1,64 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEditor;
+
+[CustomEditor(typeof(Joystick), true)]
+public class JoystickEditor : Editor
+{
+ private SerializedProperty handleRange;
+ private SerializedProperty deadZone;
+ private SerializedProperty axisOptions;
+ private SerializedProperty snapX;
+ private SerializedProperty snapY;
+ protected SerializedProperty background;
+ private SerializedProperty handle;
+
+ protected Vector2 center = new Vector2(0.5f, 0.5f);
+
+ protected virtual void OnEnable()
+ {
+ handleRange = serializedObject.FindProperty("handleRange");
+ deadZone = serializedObject.FindProperty("deadZone");
+ axisOptions = serializedObject.FindProperty("axisOptions");
+ snapX = serializedObject.FindProperty("snapX");
+ snapY = serializedObject.FindProperty("snapY");
+ background = serializedObject.FindProperty("background");
+ handle = serializedObject.FindProperty("handle");
+ }
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+
+ DrawValues();
+ EditorGUILayout.Space();
+ DrawComponents();
+
+ serializedObject.ApplyModifiedProperties();
+
+ if(handle != null)
+ {
+ RectTransform handleRect = (RectTransform)handle.objectReferenceValue;
+ handleRect.anchorMax = center;
+ handleRect.anchorMin = center;
+ handleRect.pivot = center;
+ handleRect.anchoredPosition = Vector2.zero;
+ }
+ }
+
+ protected virtual void DrawValues()
+ {
+ EditorGUILayout.PropertyField(handleRange, new GUIContent("Handle Range", "The distance the visual handle can move from the center of the joystick."));
+ EditorGUILayout.PropertyField(deadZone, new GUIContent("Dead Zone", "The distance away from the center input has to be before registering."));
+ EditorGUILayout.PropertyField(axisOptions, new GUIContent("Axis Options", "Which axes the joystick uses."));
+ EditorGUILayout.PropertyField(snapX, new GUIContent("Snap X", "Snap the horizontal input to a whole value."));
+ EditorGUILayout.PropertyField(snapY, new GUIContent("Snap Y", "Snap the vertical input to a whole value."));
+ }
+
+ protected virtual void DrawComponents()
+ {
+ EditorGUILayout.ObjectField(background, new GUIContent("Background", "The background's RectTransform component."));
+ EditorGUILayout.ObjectField(handle, new GUIContent("Handle", "The handle's RectTransform component."));
+ }
+}
\ No newline at end of file
diff --git a/Assets/GameMain/Scripts/Editor/UI/Joystick/JoystickEditor.cs.meta b/Assets/GameMain/Scripts/Editor/UI/Joystick/JoystickEditor.cs.meta
new file mode 100644
index 0000000..59d5e45
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/Joystick/JoystickEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d4b2b0d911a01a64ebfca9918e2b90b4
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/GameMain/Scripts/Editor/UI/Joystick/VariableJoystickEditor.cs b/Assets/GameMain/Scripts/Editor/UI/Joystick/VariableJoystickEditor.cs
new file mode 100644
index 0000000..888e020
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/Joystick/VariableJoystickEditor.cs
@@ -0,0 +1,37 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+using UnityEditor;
+
+[CustomEditor(typeof(VariableJoystick))]
+public class VariableJoystickEditor : JoystickEditor
+{
+ private SerializedProperty moveThreshold;
+ private SerializedProperty joystickType;
+
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+ moveThreshold = serializedObject.FindProperty("moveThreshold");
+ joystickType = serializedObject.FindProperty("joystickType");
+ }
+
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+
+ if (background != null)
+ {
+ RectTransform backgroundRect = (RectTransform)background.objectReferenceValue;
+ backgroundRect.pivot = center;
+ }
+ }
+
+ protected override void DrawValues()
+ {
+ base.DrawValues();
+ EditorGUILayout.PropertyField(moveThreshold, new GUIContent("Move Threshold", "The distance away from the center input has to be before the joystick begins to move."));
+ EditorGUILayout.PropertyField(joystickType, new GUIContent("Joystick Type", "The type of joystick the variable joystick is current using."));
+ }
+}
\ No newline at end of file
diff --git a/Assets/GameMain/Scripts/Editor/UI/Joystick/VariableJoystickEditor.cs.meta b/Assets/GameMain/Scripts/Editor/UI/Joystick/VariableJoystickEditor.cs.meta
new file mode 100644
index 0000000..881d524
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/Joystick/VariableJoystickEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 58b5a2db95af76c4c9d4116a09ec3697
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/GameMain/Scripts/Editor/UI/UIAssetsTools.cs b/Assets/GameMain/Scripts/Editor/UI/UIAssetsTools.cs
new file mode 100644
index 0000000..6d6b8a4
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/UIAssetsTools.cs
@@ -0,0 +1,975 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using SepCore.UI;
+using UnityEditor;
+using UnityEditor.Callbacks;
+using UnityEditor.Compilation;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+
+namespace SepCore.Editor
+{
+ public static class UIAssetsTools
+ {
+ public const string GeneratedNamespace = "SepCore.UI";
+ public const string GeneratedDirectory = "Assets/GameMain/Scripts/Presentation";
+
+ private const string PendingGenerationKey = "UI.Serialization.PendingGeneration";
+ private static bool finalizeQueued;
+
+ private static readonly HashSet CSharpKeywords = new HashSet(StringComparer.Ordinal)
+ {
+ "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class",
+ "const", "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event",
+ "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", "if",
+ "implicit", "in", "int", "interface", "internal", "is", "lock", "long", "namespace", "new",
+ "null", "object", "operator", "out", "override", "params", "private", "protected", "public",
+ "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static",
+ "string", "struct", "switch", "this", "throw", "true", "try", "typeof", "uint", "ulong",
+ "unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while"
+ };
+
+ [Serializable]
+ private sealed class PendingGenerationBatch
+ {
+ public List entries = new List();
+ public string primaryScriptPath;
+ }
+
+ [Serializable]
+ private sealed class PendingGeneration
+ {
+ public string rootGlobalId;
+ public string prefabAssetPath;
+ public string prefabRootRelativePath;
+ public string scenePath;
+ public string hierarchyPath;
+ public string viewClassFullName;
+ public string formClassFullName;
+ public string viewScriptPath;
+ public string formScriptPath;
+ }
+
+ private sealed class GenerationUnit
+ {
+ public UISerializationRoot root;
+ public string viewClassName;
+ public string formClassName;
+ public string viewScriptPath;
+ public string formScriptPath;
+ public string viewSource;
+ public string formSource;
+ public bool viewChanged;
+ public bool formChanged;
+
+ public bool SourceChanged => viewChanged || formChanged;
+ }
+
+ private sealed class Binding
+ {
+ public Component component;
+ public UISerializationRoot nestedRoot;
+ public string variableName;
+ public string csharpTypeName;
+ }
+
+ [MenuItem("Assets/Create View Script", false, 2000)]
+ private static void GenerateFromAssetsMenu()
+ {
+ GenerateWithDialog(GetSelectedRoot());
+ }
+
+ [MenuItem("Assets/Create View Script", true)]
+ private static bool ValidateAssetsMenu()
+ {
+ return GetSelectedRoot() != null;
+ }
+
+ [MenuItem("GameObject/Create View Script", false, 30)]
+ private static void GenerateFromGameObjectMenu()
+ {
+ GenerateWithDialog(GetSelectedRoot());
+ }
+
+ [MenuItem("GameObject/Create View Script", true)]
+ private static bool ValidateGameObjectMenu()
+ {
+ return GetSelectedRoot() != null;
+ }
+
+ [MenuItem("CONTEXT/UISerializationRoot/Create View Script")]
+ private static void GenerateFromComponentMenu(MenuCommand command)
+ {
+ GenerateWithDialog(command.context as UISerializationRoot);
+ }
+
+ public static void GenerateWithDialog(UISerializationRoot root)
+ {
+ try
+ {
+ Generate(root, true);
+ }
+ catch (Exception exception)
+ {
+ Debug.LogException(exception);
+ EditorUtility.DisplayDialog("Create UI View Script", exception.Message, "OK");
+ }
+ }
+
+ public static string Generate(UISerializationRoot root, bool confirmOverwrite)
+ {
+ if (root == null)
+ {
+ throw new InvalidOperationException("Select a GameObject or Prefab with UISerializationRoot.");
+ }
+
+ List generationRoots = CollectGenerationRoots(root);
+ List units = BuildGenerationUnits(generationRoots);
+ GenerationUnit primaryUnit = units[units.Count - 1];
+
+ List changedExistingFiles = units
+ .Where(unit => unit.SourceChanged)
+ .SelectMany(unit => new[] { unit.viewScriptPath, unit.formScriptPath })
+ .Where(File.Exists)
+ .ToList();
+ if (confirmOverwrite && changedExistingFiles.Count > 0)
+ {
+ string message = "Overwrite the following generated View/Form scripts?\n\n" +
+ string.Join("\n", changedExistingFiles.ToArray());
+ if (!EditorUtility.DisplayDialog("Regenerate UI View/Form", message, "Overwrite", "Cancel"))
+ {
+ return null;
+ }
+ }
+
+ EnsureFolder(GeneratedDirectory);
+ SaveDirtyRootAssets(units);
+
+ var pendingBatch = new PendingGenerationBatch
+ {
+ primaryScriptPath = primaryUnit.viewScriptPath
+ };
+ for (int i = 0; i < units.Count; i++)
+ {
+ pendingBatch.entries.Add(CreatePendingGeneration(units[i]));
+ }
+ SessionState.SetString(PendingGenerationKey, JsonUtility.ToJson(pendingBatch));
+
+ bool anySourceChanged = false;
+ for (int i = 0; i < units.Count; i++)
+ {
+ GenerationUnit unit = units[i];
+ if (!unit.SourceChanged)
+ {
+ continue;
+ }
+
+ EnsureFolder(Path.GetDirectoryName(unit.viewScriptPath));
+ if (unit.viewChanged)
+ {
+ File.WriteAllText(unit.viewScriptPath, unit.viewSource, new UTF8Encoding(false));
+ anySourceChanged = true;
+ }
+
+ if (unit.formChanged)
+ {
+ File.WriteAllText(unit.formScriptPath, unit.formSource, new UTF8Encoding(false));
+ anySourceChanged = true;
+ }
+ }
+
+ if (anySourceChanged)
+ {
+ AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
+ }
+
+ UnityEngine.Object scriptAsset = AssetDatabase.LoadAssetAtPath(primaryUnit.viewScriptPath);
+ if (scriptAsset != null)
+ {
+ EditorGUIUtility.PingObject(scriptAsset);
+ }
+
+ if (anySourceChanged)
+ {
+ CompilationPipeline.RequestScriptCompilation();
+ }
+ else
+ {
+ QueuePendingGeneration();
+ }
+
+ Debug.Log("Generated " + units.Count + " UI View/Form script pair(s); primary script: " +
+ primaryUnit.viewScriptPath);
+ return primaryUnit.viewScriptPath;
+ }
+
+ public static string GetViewClassName(UISerializationRoot root)
+ {
+ if (root == null)
+ {
+ return string.Empty;
+ }
+
+ return GetBaseName(root) + "View";
+ }
+
+ public static string GetFormClassName(UISerializationRoot root)
+ {
+ if (root == null)
+ {
+ return string.Empty;
+ }
+
+ return GetBaseName(root) + "Form";
+ }
+
+ public static string GetBaseName(UISerializationRoot root)
+ {
+ if (root == null)
+ {
+ return string.Empty;
+ }
+
+ string sourceName = root.gameObject.name.Replace("(Clone)", string.Empty).Trim();
+ if (sourceName.EndsWith("Form", StringComparison.Ordinal) && sourceName.Length > 4)
+ {
+ sourceName = sourceName.Substring(0, sourceName.Length - 4);
+ }
+ else if (sourceName.EndsWith("UI", StringComparison.Ordinal) && sourceName.Length > 2)
+ {
+ sourceName = sourceName.Substring(0, sourceName.Length - 2);
+ }
+
+ string identifier = ToPascalIdentifier(sourceName);
+ if (string.IsNullOrEmpty(identifier))
+ {
+ throw new InvalidOperationException("The UI root name cannot be converted to a C# class name.");
+ }
+
+ return identifier;
+ }
+
+ public static bool IsValidIdentifier(string value)
+ {
+ if (string.IsNullOrEmpty(value) || CSharpKeywords.Contains(value))
+ {
+ return false;
+ }
+
+ if (!char.IsLetter(value[0]) && value[0] != '_')
+ {
+ return false;
+ }
+
+ for (int i = 1; i < value.Length; i++)
+ {
+ if (!char.IsLetterOrDigit(value[i]) && value[i] != '_')
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ [InitializeOnLoadMethod]
+ private static void InitializePendingGeneration()
+ {
+ QueuePendingGeneration();
+ }
+
+ [DidReloadScripts]
+ private static void OnScriptsReloaded()
+ {
+ QueuePendingGeneration();
+ }
+
+ private static void QueuePendingGeneration()
+ {
+ if (finalizeQueued || string.IsNullOrEmpty(SessionState.GetString(PendingGenerationKey, string.Empty)))
+ {
+ return;
+ }
+
+ finalizeQueued = true;
+ EditorApplication.delayCall += RunQueuedPendingGeneration;
+ }
+
+ private static void RunQueuedPendingGeneration()
+ {
+ finalizeQueued = false;
+ FinalizePendingGeneration();
+ }
+
+ private static void FinalizePendingGeneration()
+ {
+ string json = SessionState.GetString(PendingGenerationKey, string.Empty);
+ if (string.IsNullOrEmpty(json))
+ {
+ return;
+ }
+
+ PendingGenerationBatch batch = JsonUtility.FromJson(json);
+ if (batch == null || batch.entries == null || batch.entries.Count == 0)
+ {
+ SessionState.EraseString(PendingGenerationKey);
+ Debug.LogError("The pending UI View generation data is invalid.");
+ return;
+ }
+
+ var resolvedViewTypes = new List(batch.entries.Count);
+ var resolvedFormTypes = new List(batch.entries.Count);
+ for (int i = 0; i < batch.entries.Count; i++)
+ {
+ PendingGeneration pending = batch.entries[i];
+ Type viewType = FindGeneratedType(pending.viewClassFullName);
+ if (viewType == null)
+ {
+ Debug.LogError("Generated UI view type was not found after compilation: " + pending.viewClassFullName);
+ return;
+ }
+
+ Type formType = FindGeneratedType(pending.formClassFullName);
+ if (formType == null)
+ {
+ Debug.LogError("Generated UI form type was not found after compilation: " + pending.formClassFullName);
+ return;
+ }
+
+ resolvedViewTypes.Add(viewType);
+ resolvedFormTypes.Add(formType);
+ }
+
+ SessionState.EraseString(PendingGenerationKey);
+ UnityEngine.Object primaryTarget = null;
+ for (int i = 0; i < batch.entries.Count; i++)
+ {
+ PendingGeneration pending = batch.entries[i];
+ Type viewType = resolvedViewTypes[i];
+ Type formType = resolvedFormTypes[i];
+ if (!string.IsNullOrEmpty(pending.prefabAssetPath))
+ {
+ primaryTarget = ApplyToPrefab(pending, viewType, formType);
+ continue;
+ }
+
+ UISerializationRoot root = ResolveSceneRoot(pending);
+ if (root == null)
+ {
+ throw new InvalidOperationException(
+ "Could not resolve the UI root after compilation. The scripts were generated at " +
+ pending.viewScriptPath + " and " + pending.formScriptPath + ".");
+ }
+
+ ApplyGeneratedComponents(root, viewType, formType, true);
+ EditorSceneManager.MarkSceneDirty(root.gameObject.scene);
+ primaryTarget = root.gameObject;
+ }
+
+ if (primaryTarget != null)
+ {
+ Selection.activeObject = primaryTarget;
+ EditorGUIUtility.PingObject(primaryTarget);
+ }
+
+ Debug.Log("Attached and populated " + batch.entries.Count + " generated UI View/Form pair(s).");
+ }
+
+ private static List CollectGenerationRoots(UISerializationRoot selectedRoot)
+ {
+ bool persistentContext = EditorUtility.IsPersistent(selectedRoot);
+ var result = new List();
+ var visited = new HashSet();
+ CollectGenerationRootsRecursive(selectedRoot, persistentContext, visited, result);
+ return result;
+ }
+
+ private static void CollectGenerationRootsRecursive(
+ UISerializationRoot current,
+ bool persistentContext,
+ HashSet visited,
+ List result)
+ {
+ current = GetCanonicalRoot(current, persistentContext);
+ if (current == null || !visited.Add(current))
+ {
+ return;
+ }
+
+ current.RefreshItems();
+ for (int i = 0; i < current.NestedFormReferences.Count; i++)
+ {
+ UISerializationRoot nestedRoot = current.NestedFormReferences[i].Root;
+ if (nestedRoot != null)
+ {
+ CollectGenerationRootsRecursive(nestedRoot, persistentContext, visited, result);
+ }
+ }
+
+ result.Add(current);
+ }
+
+ private static UISerializationRoot GetCanonicalRoot(UISerializationRoot root, bool persistentContext)
+ {
+ if (!persistentContext || root == null)
+ {
+ return root;
+ }
+
+ UISerializationRoot source = PrefabUtility.GetCorrespondingObjectFromSource(root);
+ return source != null ? source : root;
+ }
+
+ private static List BuildGenerationUnits(List roots)
+ {
+ var units = new List(roots.Count);
+ var classOwners = new Dictionary(StringComparer.Ordinal);
+ for (int i = 0; i < roots.Count; i++)
+ {
+ UISerializationRoot root = roots[i];
+ root.RefreshItems();
+ string viewClassName = GetViewClassName(root);
+ string formClassName = GetFormClassName(root);
+ string baseName = GetBaseName(root);
+ UISerializationRoot existingOwner;
+ if (classOwners.TryGetValue(viewClassName, out existingOwner) && existingOwner != root)
+ {
+ throw new InvalidOperationException(
+ "Multiple serialization roots generate the same View class '" + viewClassName +
+ "'. Rename one of the UI roots.");
+ }
+
+ classOwners[viewClassName] = root;
+ if (classOwners.TryGetValue(formClassName, out existingOwner) && existingOwner != root)
+ {
+ throw new InvalidOperationException(
+ "Multiple serialization roots generate the same Form class '" + formClassName +
+ "'. Rename one of the UI roots.");
+ }
+
+ classOwners[formClassName] = root;
+
+ List bindings = CollectBindings(root);
+ string folderPath = GeneratedDirectory + "/" + baseName;
+ string viewScriptPath = folderPath + "/" + viewClassName + ".cs";
+ string formScriptPath = folderPath + "/" + formClassName + ".cs";
+ string viewSource = BuildViewSource(viewClassName, bindings);
+ string formSource = BuildFormSource(formClassName, viewClassName);
+ units.Add(new GenerationUnit
+ {
+ root = root,
+ viewClassName = viewClassName,
+ formClassName = formClassName,
+ viewScriptPath = viewScriptPath,
+ formScriptPath = formScriptPath,
+ viewSource = viewSource,
+ formSource = formSource,
+ viewChanged = !File.Exists(viewScriptPath) || File.ReadAllText(viewScriptPath) != viewSource,
+ formChanged = !File.Exists(formScriptPath) || File.ReadAllText(formScriptPath) != formSource
+ });
+ }
+
+ return units;
+ }
+
+ private static List CollectBindings(UISerializationRoot root)
+ {
+ var bindings = new List();
+ var names = new HashSet(StringComparer.Ordinal);
+
+ for (int i = 0; i < root.SerializationItems.Count; i++)
+ {
+ UISerializationItem item = root.SerializationItems[i];
+ if (item == null)
+ {
+ continue;
+ }
+
+ item.RefreshComponents();
+ for (int j = 0; j < item.ComponentReferences.Count; j++)
+ {
+ UISerializationItem.ComponentReference reference = item.ComponentReferences[j];
+ if (reference == null || !reference.GenerateReference)
+ {
+ continue;
+ }
+
+ Component component = reference.Component;
+ string variableName = NormalizeAndValidateVariableName(reference.VariableName, item.transform, names);
+ if (component == null)
+ {
+ throw new InvalidOperationException("A selected UI reference is missing on " + GetHierarchyPath(item.transform) + ".");
+ }
+ if (component.GetType().Namespace != null && component.GetType().Namespace.StartsWith("UnityEditor", StringComparison.Ordinal))
+ {
+ throw new InvalidOperationException(
+ "Editor-only components cannot be referenced by a runtime View: " + component.GetType().FullName + ".");
+ }
+
+ bindings.Add(new Binding
+ {
+ component = component,
+ variableName = variableName,
+ csharpTypeName = GetCSharpTypeName(component.GetType())
+ });
+ }
+ }
+
+ for (int i = 0; i < root.NestedFormReferences.Count; i++)
+ {
+ UISerializationRoot.NestedFormReference reference = root.NestedFormReferences[i];
+ if (reference == null || !reference.GenerateReference)
+ {
+ continue;
+ }
+
+ UISerializationRoot nestedRoot = reference.Root;
+ if (nestedRoot == null)
+ {
+ throw new InvalidOperationException("A nested UI Form reference is missing below " + GetHierarchyPath(root.transform) + ".");
+ }
+
+ string variableName = NormalizeAndValidateVariableName(reference.VariableName, nestedRoot.transform, names);
+ bindings.Add(new Binding
+ {
+ nestedRoot = nestedRoot,
+ variableName = variableName,
+ csharpTypeName = "global::" + GeneratedNamespace + "." + GetFormClassName(nestedRoot)
+ });
+ }
+
+ return bindings;
+ }
+
+ private static string NormalizeAndValidateVariableName(string value, Transform owner, HashSet names)
+ {
+ string variableName = value == null ? string.Empty : value.Trim();
+ if (!IsValidIdentifier(variableName))
+ {
+ throw new InvalidOperationException(
+ "Invalid C# field name '" + variableName + "' on " + GetHierarchyPath(owner) + ".");
+ }
+ if (!names.Add(variableName))
+ {
+ throw new InvalidOperationException("Duplicate UI field name '" + variableName + "'.");
+ }
+ if (typeof(MonoBehaviour).GetMember(variableName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Length > 0)
+ {
+ throw new InvalidOperationException("UI field name '" + variableName + "' conflicts with a MonoBehaviour member.");
+ }
+
+ return variableName;
+ }
+
+ private static string BuildViewSource(string className, List bindings)
+ {
+ var builder = new StringBuilder(512 + bindings.Count * 96);
+ builder.Append("// \n");
+ builder.Append("using UnityEngine;\n\n");
+ builder.Append("namespace ").Append(GeneratedNamespace).Append("\n{\n");
+ builder.Append(" [DisallowMultipleComponent]\n");
+ builder.Append(" public partial class ").Append(className).Append(" : MonoBehaviour\n {\n");
+
+ for (int i = 0; i < bindings.Count; i++)
+ {
+ Binding binding = bindings[i];
+ builder.Append(" [SerializeField] public ")
+ .Append(binding.csharpTypeName)
+ .Append(' ')
+ .Append(binding.variableName)
+ .Append(";\n");
+ }
+
+ builder.Append(" }\n}\n");
+ return NormalizeToLf(builder.ToString());
+ }
+
+ private static string BuildFormSource(string className, string viewClassName)
+ {
+ var builder = new StringBuilder(256);
+ builder.Append("// \n");
+ builder.Append("using UnityEngine;\n\n");
+ builder.Append("namespace ").Append(GeneratedNamespace).Append("\n{\n");
+ builder.Append(" [DisallowMultipleComponent]\n");
+ builder.Append(" public partial class ").Append(className).Append(" : UGuiForm\n {\n");
+ builder.Append(" [SerializeField] private ").Append(viewClassName).Append(" _view = null;\n\n");
+ builder.Append(" public ").Append(viewClassName).Append(" View => _view;\n");
+ builder.Append(" }\n}\n");
+ return NormalizeToLf(builder.ToString());
+ }
+
+ private static string NormalizeToLf(string text)
+ {
+ if (string.IsNullOrEmpty(text))
+ {
+ return string.Empty;
+ }
+
+ return text.Replace("\r\n", "\n").Replace('\r', '\n');
+ }
+
+ private static PendingGeneration CreatePendingGeneration(GenerationUnit unit)
+ {
+ UISerializationRoot root = unit.root;
+ bool isPrefabAsset = EditorUtility.IsPersistent(root) && PrefabUtility.IsPartOfPrefabAsset(root);
+ string prefabAssetPath = isPrefabAsset ? AssetDatabase.GetAssetPath(root.gameObject) : string.Empty;
+ string relativePath = string.Empty;
+ if (isPrefabAsset)
+ {
+ GameObject prefabRoot = AssetDatabase.LoadAssetAtPath(prefabAssetPath);
+ if (prefabRoot == null)
+ {
+ throw new InvalidOperationException("Could not load Prefab asset: " + prefabAssetPath);
+ }
+
+ relativePath = GetRelativePath(prefabRoot.transform, root.transform);
+ }
+
+ GlobalObjectId globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(root);
+ return new PendingGeneration
+ {
+ rootGlobalId = globalObjectId.ToString(),
+ prefabAssetPath = prefabAssetPath,
+ prefabRootRelativePath = relativePath,
+ scenePath = root.gameObject.scene.path,
+ hierarchyPath = GetHierarchyPath(root.transform),
+ viewClassFullName = GeneratedNamespace + "." + unit.viewClassName,
+ formClassFullName = GeneratedNamespace + "." + unit.formClassName,
+ viewScriptPath = unit.viewScriptPath,
+ formScriptPath = unit.formScriptPath
+ };
+ }
+
+ private static UnityEngine.Object ApplyToPrefab(PendingGeneration pending, Type viewType, Type formType)
+ {
+ GameObject contents = PrefabUtility.LoadPrefabContents(pending.prefabAssetPath);
+ try
+ {
+ Transform rootTransform = string.IsNullOrEmpty(pending.prefabRootRelativePath)
+ ? contents.transform
+ : contents.transform.Find(pending.prefabRootRelativePath);
+ UISerializationRoot root = rootTransform == null ? null : rootTransform.GetComponent();
+ if (root == null)
+ {
+ throw new InvalidOperationException(
+ "The Prefab no longer contains the expected UISerializationRoot at '" +
+ pending.prefabRootRelativePath + "': " + pending.prefabAssetPath);
+ }
+
+ ApplyGeneratedComponents(root, viewType, formType, false);
+ PrefabUtility.SaveAsPrefabAsset(contents, pending.prefabAssetPath);
+ }
+ finally
+ {
+ PrefabUtility.UnloadPrefabContents(contents);
+ }
+
+ AssetDatabase.ImportAsset(pending.prefabAssetPath, ImportAssetOptions.ForceUpdate);
+ return AssetDatabase.LoadAssetAtPath(pending.prefabAssetPath);
+ }
+
+ public static void ApplyGeneratedComponentsToRoot(UISerializationRoot root)
+ {
+ if (root == null)
+ {
+ return;
+ }
+
+ root.RefreshItems();
+ for (int i = 0; i < root.NestedFormReferences.Count; i++)
+ {
+ UISerializationRoot nestedRoot = root.NestedFormReferences[i].Root;
+ if (nestedRoot != null)
+ {
+ ApplyGeneratedComponentsToRoot(nestedRoot);
+ }
+ }
+
+ Type viewType = FindGeneratedType(GeneratedNamespace + "." + GetViewClassName(root));
+ Type formType = FindGeneratedType(GeneratedNamespace + "." + GetFormClassName(root));
+ if (viewType == null || formType == null)
+ {
+ return;
+ }
+
+ ApplyGeneratedComponents(root, viewType, formType, false);
+ }
+
+ private static void ApplyGeneratedComponents(UISerializationRoot root, Type viewType, Type formType,
+ bool useUndo)
+ {
+ Component view = ApplyViewReferences(root, viewType, useUndo);
+ Component form = root.GetComponent(formType);
+ if (form == null)
+ {
+ form = useUndo ? Undo.AddComponent(root.gameObject, formType) : root.gameObject.AddComponent(formType);
+ }
+ else if (useUndo)
+ {
+ Undo.RecordObject(form, "Populate UI form view reference");
+ }
+
+ var serializedForm = new SerializedObject(form);
+ serializedForm.UpdateIfRequiredOrScript();
+ SerializedProperty viewProperty = serializedForm.FindProperty("_view");
+ if (viewProperty == null)
+ {
+ throw new InvalidOperationException("Generated field was not found: _view on " + formType.FullName + ".");
+ }
+
+ viewProperty.objectReferenceValue = view;
+ serializedForm.ApplyModifiedProperties();
+ EditorUtility.SetDirty(form);
+ if (useUndo && PrefabUtility.IsPartOfPrefabInstance(form))
+ {
+ PrefabUtility.RecordPrefabInstancePropertyModifications(form);
+ }
+ }
+
+ private static Component ApplyViewReferences(UISerializationRoot root, Type viewType, bool useUndo)
+ {
+ root.RefreshItems();
+ List bindings = CollectBindings(root);
+ Component view = root.GetComponent(viewType);
+ if (view == null)
+ {
+ view = useUndo ? Undo.AddComponent(root.gameObject, viewType) : root.gameObject.AddComponent(viewType);
+ }
+ else if (useUndo)
+ {
+ Undo.RecordObject(view, "Populate UI view references");
+ }
+
+ var serializedView = new SerializedObject(view);
+ serializedView.UpdateIfRequiredOrScript();
+ for (int i = 0; i < bindings.Count; i++)
+ {
+ Binding binding = bindings[i];
+ SerializedProperty property = serializedView.FindProperty(binding.variableName);
+ if (property == null)
+ {
+ throw new InvalidOperationException("Generated field was not found: " + binding.variableName + ".");
+ }
+
+ UnityEngine.Object target = binding.component;
+ if (binding.nestedRoot != null)
+ {
+ string nestedFormFullName = GeneratedNamespace + "." + GetFormClassName(binding.nestedRoot);
+ Type nestedFormType = FindGeneratedType(nestedFormFullName);
+ if (nestedFormType == null)
+ {
+ throw new InvalidOperationException("Nested UI form type was not found: " + nestedFormFullName + ".");
+ }
+
+ target = binding.nestedRoot.GetComponent(nestedFormType);
+ if (target == null)
+ {
+ throw new InvalidOperationException(
+ "Nested UI root '" + GetHierarchyPath(binding.nestedRoot.transform) +
+ "' does not have its generated Form component '" + nestedFormFullName + "'.");
+ }
+ }
+
+ property.objectReferenceValue = target;
+ }
+
+ serializedView.ApplyModifiedProperties();
+ EditorUtility.SetDirty(view);
+ if (useUndo && PrefabUtility.IsPartOfPrefabInstance(view))
+ {
+ PrefabUtility.RecordPrefabInstancePropertyModifications(view);
+ }
+
+ return view;
+ }
+
+ private static Type FindGeneratedType(string fullName)
+ {
+ return TypeCache.GetTypesDerivedFrom()
+ .FirstOrDefault(type => type.FullName == fullName);
+ }
+
+ private static UISerializationRoot ResolveSceneRoot(PendingGeneration pending)
+ {
+ GlobalObjectId parsed;
+ if (GlobalObjectId.TryParse(pending.rootGlobalId, out parsed))
+ {
+ UnityEngine.Object resolved = GlobalObjectId.GlobalObjectIdentifierToObjectSlow(parsed);
+ UISerializationRoot root = resolved as UISerializationRoot;
+ if (root != null)
+ {
+ return root;
+ }
+
+ GameObject resolvedObject = resolved as GameObject;
+ if (resolvedObject != null)
+ {
+ root = resolvedObject.GetComponent();
+ if (root != null)
+ {
+ return root;
+ }
+ }
+ }
+
+ UISerializationRoot[] loadedRoots = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < loadedRoots.Length; i++)
+ {
+ UISerializationRoot root = loadedRoots[i];
+ if (root != null && !EditorUtility.IsPersistent(root) && root.gameObject.scene.path == pending.scenePath &&
+ GetHierarchyPath(root.transform) == pending.hierarchyPath)
+ {
+ return root;
+ }
+ }
+
+ return null;
+ }
+
+ private static UISerializationRoot GetSelectedRoot()
+ {
+ GameObject selected = Selection.activeGameObject;
+ if (selected == null)
+ {
+ selected = Selection.activeObject as GameObject;
+ }
+
+ return selected == null ? null : selected.GetComponent();
+ }
+
+ private static string GetCSharpTypeName(Type type)
+ {
+ if (type.IsArray)
+ {
+ return GetCSharpTypeName(type.GetElementType()) + "[]";
+ }
+
+ if (type.IsGenericType)
+ {
+ string definition = type.GetGenericTypeDefinition().FullName ?? type.Name;
+ int arityMarker = definition.IndexOf('`');
+ if (arityMarker >= 0)
+ {
+ definition = definition.Substring(0, arityMarker);
+ }
+
+ string arguments = string.Join(", ", type.GetGenericArguments().Select(GetCSharpTypeName).ToArray());
+ return "global::" + definition.Replace('+', '.') + "<" + arguments + ">";
+ }
+
+ return "global::" + (type.FullName ?? type.Name).Replace('+', '.');
+ }
+
+ private static void SaveDirtyRootAssets(List units)
+ {
+ var savedPaths = new HashSet(StringComparer.Ordinal);
+ for (int i = 0; i < units.Count; i++)
+ {
+ UISerializationRoot root = units[i].root;
+ if (root == null || !EditorUtility.IsPersistent(root))
+ {
+ continue;
+ }
+
+ string assetPath = AssetDatabase.GetAssetPath(root);
+ if (string.IsNullOrEmpty(assetPath) || !savedPaths.Add(assetPath))
+ {
+ continue;
+ }
+
+ AssetDatabase.SaveAssetIfDirty(AssetDatabase.GUIDFromAssetPath(assetPath));
+ }
+ }
+
+ private static string ToPascalIdentifier(string value)
+ {
+ var builder = new StringBuilder(value.Length + 8);
+ bool capitalizeNext = true;
+ for (int i = 0; i < value.Length; i++)
+ {
+ char current = value[i];
+ if (!char.IsLetterOrDigit(current) && current != '_')
+ {
+ capitalizeNext = true;
+ continue;
+ }
+
+ if (builder.Length == 0 && char.IsDigit(current))
+ {
+ builder.Append('_');
+ }
+
+ builder.Append(capitalizeNext ? char.ToUpperInvariant(current) : current);
+ capitalizeNext = false;
+ }
+
+ return builder.ToString();
+ }
+
+ private static string GetRelativePath(Transform root, Transform target)
+ {
+ if (root == target)
+ {
+ return string.Empty;
+ }
+
+ var parts = new Stack();
+ Transform current = target;
+ while (current != null && current != root)
+ {
+ parts.Push(current.name);
+ current = current.parent;
+ }
+
+ if (current != root)
+ {
+ throw new InvalidOperationException("The serialization root is not inside the expected Prefab asset.");
+ }
+
+ return string.Join("/", parts.ToArray());
+ }
+
+ private static string GetHierarchyPath(Transform target)
+ {
+ string path = target.name;
+ Transform current = target.parent;
+ while (current != null)
+ {
+ path = current.name + "/" + path;
+ current = current.parent;
+ }
+
+ return path;
+ }
+
+ private static void EnsureFolder(string folder)
+ {
+ if (string.IsNullOrEmpty(folder))
+ {
+ return;
+ }
+
+ folder = folder.Replace('\\', '/');
+ string[] parts = folder.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
+ if (parts.Length == 0)
+ {
+ return;
+ }
+
+ string current = parts[0];
+ for (int i = 1; i < parts.Length; i++)
+ {
+ string next = current + "/" + parts[i];
+ if (!AssetDatabase.IsValidFolder(next))
+ {
+ AssetDatabase.CreateFolder(current, parts[i]);
+ }
+ current = next;
+ }
+ }
+ }
+}
diff --git a/Assets/GameMain/Scripts/Editor/UI/UIAssetsTools.cs.meta b/Assets/GameMain/Scripts/Editor/UI/UIAssetsTools.cs.meta
new file mode 100644
index 0000000..99cf4eb
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/UIAssetsTools.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 48c1b6017a08547aa94ad17c9611d7b2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/GameMain/Scripts/Editor/UI/UISerializationItemEditor.cs b/Assets/GameMain/Scripts/Editor/UI/UISerializationItemEditor.cs
new file mode 100644
index 0000000..aded62d
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/UISerializationItemEditor.cs
@@ -0,0 +1,65 @@
+using System.Collections.Generic;
+using SepCore.UI;
+using UnityEditor;
+using UnityEngine;
+
+namespace SepCore.Editor
+{
+ [CustomEditor(typeof(UISerializationItem))]
+ internal sealed class UISerializationItemEditor : UnityEditor.Editor
+ {
+ public override void OnInspectorGUI()
+ {
+ var item = (UISerializationItem)target;
+ item.RefreshComponents();
+ serializedObject.UpdateIfRequiredOrScript();
+
+ SerializedProperty references = serializedObject.FindProperty("componentReferences");
+ EditorGUILayout.LabelField("Components", EditorStyles.boldLabel);
+
+ var selectedNames = new HashSet();
+ for (int i = 0; i < references.arraySize; i++)
+ {
+ SerializedProperty entry = references.GetArrayElementAtIndex(i);
+ SerializedProperty component = entry.FindPropertyRelative("component");
+ SerializedProperty generate = entry.FindPropertyRelative("generateReference");
+ SerializedProperty variableName = entry.FindPropertyRelative("variableName");
+
+ EditorGUILayout.BeginVertical(EditorStyles.helpBox);
+ EditorGUILayout.BeginHorizontal();
+ generate.boolValue = EditorGUILayout.Toggle(generate.boolValue, GUILayout.Width(18f));
+
+ using (new EditorGUI.DisabledScope(true))
+ {
+ EditorGUILayout.ObjectField(component.objectReferenceValue, typeof(Component), true);
+ }
+ EditorGUILayout.EndHorizontal();
+
+ if (generate.boolValue)
+ {
+ variableName.stringValue = EditorGUILayout.TextField("Variable Name", variableName.stringValue);
+ string normalized = variableName.stringValue == null ? string.Empty : variableName.stringValue.Trim();
+ if (!UIAssetsTools.IsValidIdentifier(normalized))
+ {
+ EditorGUILayout.HelpBox("Enter a valid C# field name.", MessageType.Error);
+ }
+ else if (!selectedNames.Add(normalized))
+ {
+ EditorGUILayout.HelpBox("This field name is already selected on this item.", MessageType.Error);
+ }
+ }
+
+ EditorGUILayout.EndVertical();
+ }
+
+ if (GUILayout.Button("Refresh Components"))
+ {
+ Undo.RecordObject(item, "Refresh UI serialization components");
+ item.RefreshComponents();
+ serializedObject.UpdateIfRequiredOrScript();
+ }
+
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
diff --git a/Assets/GameMain/Scripts/Editor/UI/UISerializationItemEditor.cs.meta b/Assets/GameMain/Scripts/Editor/UI/UISerializationItemEditor.cs.meta
new file mode 100644
index 0000000..1577faf
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/UISerializationItemEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d5f17238526324f3d98afa8e8c999814
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/GameMain/Scripts/Editor/UI/UISerializationRootEditor.cs b/Assets/GameMain/Scripts/Editor/UI/UISerializationRootEditor.cs
new file mode 100644
index 0000000..d2a1009
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/UISerializationRootEditor.cs
@@ -0,0 +1,211 @@
+using System.Collections.Generic;
+using SepCore.UI;
+using UnityEditor;
+using UnityEngine;
+
+namespace SepCore.Editor
+{
+ [CustomEditor(typeof(UISerializationRoot))]
+ internal sealed class UISerializationRootEditor : UnityEditor.Editor
+ {
+ public override void OnInspectorGUI()
+ {
+ var root = (UISerializationRoot)target;
+ root.RefreshItems();
+ serializedObject.UpdateIfRequiredOrScript();
+
+ int referenceCount = 0;
+ bool hasValidationError = false;
+ var selectedNames = new HashSet();
+ for (int i = 0; i < root.SerializationItems.Count; i++)
+ {
+ UISerializationItem item = root.SerializationItems[i];
+ if (item == null)
+ {
+ continue;
+ }
+
+ for (int j = 0; j < item.ComponentReferences.Count; j++)
+ {
+ UISerializationItem.ComponentReference reference = item.ComponentReferences[j];
+ if (reference.GenerateReference)
+ {
+ referenceCount++;
+ string variableName = reference.VariableName == null ? string.Empty : reference.VariableName.Trim();
+ if (!UIAssetsTools.IsValidIdentifier(variableName) || !selectedNames.Add(variableName))
+ {
+ hasValidationError = true;
+ }
+ }
+ }
+ }
+
+ SerializedProperty nestedForms = serializedObject.FindProperty("nestedFormReferences");
+ for (int i = 0; i < nestedForms.arraySize; i++)
+ {
+ SerializedProperty entry = nestedForms.GetArrayElementAtIndex(i);
+ if (entry.FindPropertyRelative("generateReference").boolValue)
+ {
+ referenceCount++;
+ }
+ }
+
+ EditorGUILayout.LabelField("View Class", UIAssetsTools.GetViewClassName(root));
+ EditorGUILayout.LabelField("Form Class", UIAssetsTools.GetFormClassName(root));
+ EditorGUILayout.LabelField("Owned Items", root.SerializationItems.Count.ToString());
+ EditorGUILayout.LabelField("Nested Forms", nestedForms.arraySize.ToString());
+ EditorGUILayout.LabelField("Generated References", referenceCount.ToString());
+ EditorGUILayout.Space(4f);
+
+ EditorGUILayout.LabelField("Owned Items", EditorStyles.boldLabel);
+ for (int i = 0; i < root.SerializationItems.Count; i++)
+ {
+ UISerializationItem item = root.SerializationItems[i];
+ if (item == null)
+ {
+ continue;
+ }
+
+ EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
+ EditorGUILayout.LabelField(GetRelativePath(root.transform, item.transform));
+ if (GUILayout.Button("Select", GUILayout.Width(56f)))
+ {
+ Selection.activeGameObject = item.gameObject;
+ EditorGUIUtility.PingObject(item.gameObject);
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+
+ EditorGUILayout.Space(4f);
+ EditorGUILayout.LabelField("Nested Forms", EditorStyles.boldLabel);
+ if (nestedForms.arraySize == 0)
+ {
+ EditorGUILayout.HelpBox("Nested UISerializationRoot components will appear here as child Form references and form serialization boundaries.", MessageType.Info);
+ }
+
+ for (int i = 0; i < nestedForms.arraySize; i++)
+ {
+ SerializedProperty entry = nestedForms.GetArrayElementAtIndex(i);
+ SerializedProperty nestedRootProperty = entry.FindPropertyRelative("root");
+ SerializedProperty generate = entry.FindPropertyRelative("generateReference");
+ SerializedProperty variableName = entry.FindPropertyRelative("variableName");
+ var nestedRoot = nestedRootProperty.objectReferenceValue as UISerializationRoot;
+
+ EditorGUILayout.BeginVertical(EditorStyles.helpBox);
+ EditorGUILayout.BeginHorizontal();
+ generate.boolValue = EditorGUILayout.Toggle(generate.boolValue, GUILayout.Width(18f));
+ using (new EditorGUI.DisabledScope(true))
+ {
+ EditorGUILayout.ObjectField(nestedRoot, typeof(UISerializationRoot), true);
+ }
+ if (nestedRoot != null && GUILayout.Button("Select", GUILayout.Width(56f)))
+ {
+ Selection.activeGameObject = nestedRoot.gameObject;
+ EditorGUIUtility.PingObject(nestedRoot.gameObject);
+ }
+ EditorGUILayout.EndHorizontal();
+
+ if (nestedRoot != null)
+ {
+ EditorGUILayout.LabelField("Form Class", UIAssetsTools.GetFormClassName(nestedRoot));
+ }
+
+ if (generate.boolValue)
+ {
+ variableName.stringValue = EditorGUILayout.TextField("Variable Name", variableName.stringValue);
+ string normalized = variableName.stringValue == null ? string.Empty : variableName.stringValue.Trim();
+ if (!UIAssetsTools.IsValidIdentifier(normalized))
+ {
+ hasValidationError = true;
+ EditorGUILayout.HelpBox("Enter a valid C# field name.", MessageType.Error);
+ }
+ else if (!selectedNames.Add(normalized))
+ {
+ hasValidationError = true;
+ EditorGUILayout.HelpBox("This field name conflicts with another generated reference.", MessageType.Error);
+ }
+ }
+
+ EditorGUILayout.EndVertical();
+ }
+
+ if (hasValidationError)
+ {
+ EditorGUILayout.HelpBox("Fix invalid or duplicate generated field names before creating View scripts.", MessageType.Error);
+ }
+
+ serializedObject.ApplyModifiedProperties();
+ EditorGUILayout.Space(4f);
+ EditorGUILayout.BeginHorizontal();
+ if (GUILayout.Button("Refresh Items"))
+ {
+ Undo.RecordObject(root, "Refresh UI serialization items");
+ root.RefreshItems();
+ }
+ using (new EditorGUI.DisabledScope(hasValidationError))
+ {
+ if (GUILayout.Button("Create View & Form Scripts"))
+ {
+ UIAssetsTools.GenerateWithDialog(root);
+ }
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+
+ private static string GetRelativePath(Transform root, Transform target)
+ {
+ if (target == root)
+ {
+ return root.name;
+ }
+
+ string path = target.name;
+ Transform current = target.parent;
+ while (current != null && current != root)
+ {
+ path = current.name + "/" + path;
+ current = current.parent;
+ }
+
+ return path;
+ }
+ }
+
+ [InitializeOnLoad]
+ internal static class UISerializationRootWatcher
+ {
+ private static bool refreshQueued;
+
+ static UISerializationRootWatcher()
+ {
+ EditorApplication.hierarchyChanged += QueueRefresh;
+ }
+
+ private static void QueueRefresh()
+ {
+ if (refreshQueued || EditorApplication.isPlayingOrWillChangePlaymode)
+ {
+ return;
+ }
+
+ refreshQueued = true;
+ EditorApplication.delayCall += RefreshLoadedRoots;
+ }
+
+ private static void RefreshLoadedRoots()
+ {
+ refreshQueued = false;
+ UISerializationRoot[] roots = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < roots.Length; i++)
+ {
+ UISerializationRoot root = roots[i];
+ if (root == null || EditorUtility.IsPersistent(root) || !root.gameObject.scene.IsValid() || !root.gameObject.scene.isLoaded)
+ {
+ continue;
+ }
+
+ root.RefreshItems();
+ }
+ }
+ }
+}
diff --git a/Assets/GameMain/Scripts/Editor/UI/UISerializationRootEditor.cs.meta b/Assets/GameMain/Scripts/Editor/UI/UISerializationRootEditor.cs.meta
new file mode 100644
index 0000000..660d778
--- /dev/null
+++ b/Assets/GameMain/Scripts/Editor/UI/UISerializationRootEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8c6a8249053ff4cf0a368b8a25b9946b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogController.cs b/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogController.cs
index 05add67..cd47dd9 100644
--- a/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogController.cs
+++ b/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogController.cs
@@ -26,6 +26,7 @@ namespace SepCore.UI
{
if (rawData == null)
{
+ Log.Error("DialogController.BuildContext() rawData is null.");
return null;
}
@@ -60,7 +61,6 @@ namespace SepCore.UI
DialogContext context = BuildContext(rawData);
if (context == null)
{
- Log.Warning("DialogController.OpenUIAsync() rawData is invalid.");
return;
}
@@ -82,7 +82,7 @@ namespace SepCore.UI
{
if (useCase != null)
{
- Log.Warning("DialogController does not use a use case.");
+ Log.Error("DialogController does not use a use case.");
}
}
diff --git a/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogForm.cs b/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogForm.cs
index 0b39e0f..31804de 100644
--- a/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogForm.cs
+++ b/Assets/GameMain/Scripts/Presentation/Common/Dialog/DialogForm.cs
@@ -38,7 +38,7 @@ namespace SepCore.UI
if (userData is not DialogContext context)
{
- Log.Warning("DialogContext is invalid.");
+ Log.Error("DialogForm requires DialogContext as userData.");
return;
}
@@ -47,12 +47,6 @@ namespace SepCore.UI
public void RefreshUI(DialogContext context)
{
- if (context == null)
- {
- Log.Warning("DialogContext is invalid.");
- return;
- }
-
_dialogMode = context.Mode;
RefreshDialogMode();
diff --git a/Assets/GameMain/Scripts/Presentation/Main/Hud/HudController.cs b/Assets/GameMain/Scripts/Presentation/Main/Hud/HudController.cs
index 3f1e932..2414bf3 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/Hud/HudController.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/Hud/HudController.cs
@@ -34,7 +34,7 @@ namespace SepCore.UI
private static string BuildPromptContext(InputActionId actionId)
{
- if (GameEntry.InputModule == null || !GameEntry.InputModule.TryGetPrompt(actionId, out InputPrompt prompt))
+ if (!GameEntry.InputModule.TryGetPrompt(actionId, out InputPrompt prompt))
{
return null;
}
@@ -46,7 +46,7 @@ namespace SepCore.UI
{
if (userData != null)
{
- Log.Warning("HudController.OpenUIAsync() userData type is invalid.");
+ Log.Error("HudController.OpenUIAsync() userData type is invalid.");
return;
}
@@ -60,11 +60,6 @@ namespace SepCore.UI
private void OnDeviceKindChanged(InputDeviceKind deviceKind)
{
- if (Context == null || Form == null)
- {
- return;
- }
-
Context.MovePrompt = BuildPromptContext(InputActionId.Move);
Form.RefreshMovePrompt(Context.MovePrompt);
}
diff --git a/Assets/GameMain/Scripts/Presentation/Main/Hud/HudForm.cs b/Assets/GameMain/Scripts/Presentation/Main/Hud/HudForm.cs
index df2fa65..fba03c0 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/Hud/HudForm.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/Hud/HudForm.cs
@@ -51,21 +51,14 @@ namespace SepCore.UI
public void RefreshUI(HudContext hudContext)
{
- RefreshMovePrompt(hudContext?.MovePrompt);
+ RefreshMovePrompt(hudContext.MovePrompt);
}
public void RefreshMovePrompt(string prompt)
{
bool visible = !string.IsNullOrEmpty(prompt);
- if (_movePromptRoot != null)
- {
- _movePromptRoot.SetActive(visible);
- }
-
- if (_movePromptText != null)
- {
- _movePromptText.text = prompt;
- }
+ _movePromptRoot.SetActive(visible);
+ _movePromptText.text = prompt;
}
#region FSM
@@ -182,8 +175,6 @@ namespace SepCore.UI
private void RefreshPerformanceStats()
{
- if (_cpuTimeCostText == null) return;
-
bool hasCpuFrame = TryGetLastValue(_cpuTimeRecorder, out long cpuFrameNanoseconds);
if (!hasCpuFrame)
diff --git a/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipContext.cs b/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipContext.cs
index 51161b8..217b9a6 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipContext.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipContext.cs
@@ -29,7 +29,7 @@ namespace SepCore.UI
ShowRecycleButton = false;
}
- public ItemTooltipContext(ItemTooltipRawData rawData)
+ public ItemTooltipContext(ItemTooltipRawData rawData) : this()
{
Index = rawData.Index;
ItemType = rawData.ItemType;
@@ -37,19 +37,15 @@ namespace SepCore.UI
Price = rawData.Price;
TargetPos = rawData.TargetPos;
ShowRecycleButton = rawData.OnRecycle != null;
- if (ItemType == ItemType.None)
- {
- IconAssetName = string.Empty;
- Title = string.Empty;
- }
- else if (ItemType == ItemType.Weapon)
+
+ if (ItemType == ItemType.Weapon)
{
var data = rawData.WeaponData;
IconAssetName = data.IconAssetName;
Title = data.Title;
Description = ItemDescUtility.CreateWeaponDescription(data);
}
- else
+ else if (ItemType == ItemType.Prop)
{
var data = rawData.PropData;
IconAssetName = data.IconAssetName;
diff --git a/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipController.cs b/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipController.cs
index cce682c..1153246 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipController.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipController.cs
@@ -71,7 +71,6 @@ namespace SepCore.UI
ItemTooltipContext context = BuildContext(rawData);
if (context == null)
{
- Log.Warning("ItemTooltipController.OpenUIAsync() rawData is invalid.");
return;
}
@@ -89,7 +88,7 @@ namespace SepCore.UI
{
if (useCase != null)
{
- Log.Warning("ItemTooltipController does not use a use case.");
+ Log.Error("ItemTooltipController does not use a use case.");
}
}
diff --git a/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipForm.cs b/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipForm.cs
index 9e19323..2e7884a 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipForm.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/ItemTooltip/ItemTooltipForm.cs
@@ -40,33 +40,21 @@ namespace SepCore.UI
public async UniTaskVoid RefreshUI(ItemTooltipContext context)
{
- if (context == null)
- {
- Log.Warning("ItemTooltipForm context is invalid.");
- return;
- }
-
_context = context;
_targetPos = ConvertWorldToAnchored(_context.TargetPos);
Log.Info($"{_context.TargetPos}->{_targetPos}");
- if (_content != null)
- {
- _content.anchoredPosition3D = _targetPos;
- }
+ _content.anchoredPosition3D = _targetPos;
- _itemTitle.text = _context.Title ?? string.Empty;
- _itemTypeText.text = BuildTypeText(_context.ItemType) ?? string.Empty;
- _itemDescription.text = _context.Description ?? string.Empty;
+ _itemTitle.text = _context.Title;
+ _itemTypeText.text = BuildTypeText(_context.ItemType);
+ _itemDescription.text = _context.Description;
_itemPrice.text = $"{_context.Price} ";
- if (_recycleButton != null)
- {
- _recycleButton.gameObject.SetActive(_context.ShowRecycleButton);
- }
+ _recycleButton.gameObject.SetActive(_context.ShowRecycleButton);
- if (_iconArea != null && !string.IsNullOrEmpty(_context.IconAssetName))
+ if (!string.IsNullOrEmpty(_context.IconAssetName))
{
string iconAssetName = _context.IconAssetName;
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(iconAssetName);
@@ -103,12 +91,7 @@ namespace SepCore.UI
private void ResizeToFitContent()
{
- if (!_autoResizeHeight || _content == null)
- {
- return;
- }
-
- if (_itemDescription == null)
+ if (!_autoResizeHeight)
{
return;
}
@@ -126,11 +109,6 @@ namespace SepCore.UI
private void ApplyClampedTargetPos()
{
- if (_content == null)
- {
- return;
- }
-
RectTransform parent = _content.parent as RectTransform;
if (parent == null)
{
@@ -160,11 +138,6 @@ namespace SepCore.UI
private Vector3 ConvertWorldToAnchored(Vector3 worldPos)
{
- if (_content == null)
- {
- return worldPos;
- }
-
RectTransform parent = _content.parent as RectTransform;
if (parent == null)
{
@@ -192,7 +165,6 @@ namespace SepCore.UI
public void OnRecycleButtonClick()
{
- if (_context == null) return;
GameEntry.Event.Fire(this, ItemTooltipRecycleEventArgs.Create(_context.Index, _context.Price));
}
diff --git a/Assets/GameMain/Scripts/Presentation/Main/Joystick/JoystickController.cs b/Assets/GameMain/Scripts/Presentation/Main/Joystick/JoystickController.cs
index 2383078..38cf28e 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/Joystick/JoystickController.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/Joystick/JoystickController.cs
@@ -38,7 +38,6 @@ namespace SepCore.UI
private void OnCommandTriggered(InputCommand cmd)
{
if (cmd.ActionId != InputActionId.Move) return;
- if (Context == null || Form == null) return;
Context.Direction = cmd.Vector2Value;
Context.Magnitude = cmd.Vector2Value.magnitude;
diff --git a/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpController.cs b/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpController.cs
index 3089603..3079359 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpController.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpController.cs
@@ -39,20 +39,9 @@ namespace SepCore.UI
return null;
}
- if (rawData.Rewards == null)
- {
- Log.Error("LevelUpFormController.BuildContext() rewards are null.");
- return null;
- }
-
List props = new List(rawData.Rewards.Count);
foreach (var reward in rawData.Rewards)
{
- if (reward == null)
- {
- continue;
- }
-
props.Add(new LevelUpRewardItemContext
{
Title = reward.Title,
@@ -80,7 +69,7 @@ namespace SepCore.UI
if (userData != null)
{
- Log.Warning("LevelUpController.OpenUIAsync() userData type is invalid.");
+ Log.Error("LevelUpController.OpenUIAsync() userData type is invalid.");
return;
}
diff --git a/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpForm.cs b/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpForm.cs
index 7148384..6aa45e7 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpForm.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpForm.cs
@@ -22,7 +22,6 @@ namespace SepCore.UI
propItem.gameObject.SetActive(false);
}
- if (_context.Props == null) return;
int count = Mathf.Min(_propItems.Length, _context.Props.Count);
for (int i = 0; i < count; i++)
{
@@ -40,13 +39,13 @@ namespace SepCore.UI
{
base.OnOpen(userData);
- if (userData is LevelUpContext context)
+ if (userData is not LevelUpContext context)
{
- RefreshUI(context);
+ Log.Error("LevelUpForm requires LevelUpContext as userData.");
return;
}
- Log.Warning("LevelUpForm requires LevelUpFormContext as userData.");
+ RefreshUI(context);
}
protected override void OnClose(bool isShutdown, object userData)
diff --git a/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpRewardItem.cs b/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpRewardItem.cs
index 5dcb359..09cba82 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpRewardItem.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/LevelUp/LevelUpRewardItem.cs
@@ -30,17 +30,15 @@ namespace SepCore.UI
_context = context;
- if (_titleText != null) _titleText.text = context.Title;
- if (_descriptionText != null) _descriptionText.text = context.Description;
- if (_iconArea != null) _iconArea.OnInit(context.Icon, context.Rarity);
+ _titleText.text = context.Title;
+ _descriptionText.text = context.Description;
+ _iconArea.OnInit(context.Icon, context.Rarity);
LoadIcon(_context.IconAssetName).Forget();
}
private async UniTaskVoid LoadIcon(string iconAssetName)
{
- if (_iconArea == null) return;
-
if (string.IsNullOrEmpty(iconAssetName)) return;
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(iconAssetName);
diff --git a/Assets/GameMain/Scripts/Presentation/Main/Shop/DisplayItem.cs b/Assets/GameMain/Scripts/Presentation/Main/Shop/DisplayItem.cs
index e50368e..0f2b4fe 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/Shop/DisplayItem.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/Shop/DisplayItem.cs
@@ -23,16 +23,11 @@ namespace SepCore.UI
public async UniTaskVoid OnInit(DisplayItemContext context, int index)
{
- if (context == null) return;
-
_context = context;
_index = index;
- if (_iconArea != null)
- {
- var sprite = await SpriteLoader.LoadSpriteByKeyAsync(_context.IconAssetName);
- _iconArea.OnInit(sprite, context.Rarity);
- }
+ var sprite = await SpriteLoader.LoadSpriteByKeyAsync(_context.IconAssetName);
+ _iconArea.OnInit(sprite, _context.Rarity);
}
public void OnReset()
@@ -40,16 +35,12 @@ namespace SepCore.UI
_context = null;
_index = -1;
- if (_iconArea != null)
- {
- _iconArea.OnReset();
- }
+ _iconArea.OnReset();
}
public void OnItemInfoShow()
{
if (_index < 0) return;
- if (_itemRect == null) return;
Rect rect = _itemRect.rect;
Vector3 targetPos = _itemRect.TransformPoint(new Vector3(
diff --git a/Assets/GameMain/Scripts/Presentation/Main/Shop/DisplayListArea.cs b/Assets/GameMain/Scripts/Presentation/Main/Shop/DisplayListArea.cs
index dc167be..8c04525 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/Shop/DisplayListArea.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/Shop/DisplayListArea.cs
@@ -44,20 +44,13 @@ namespace SepCore.UI
public void OnInit(DisplayListAreaContext context)
{
- if (context == null)
- {
- Log.Warning("DisplayListArea context is invalid.");
- return;
- }
-
EnsurePool();
_context = context;
- if (_titleText != null) _titleText.text = context.Title;
+ _titleText.text = context.Title;
UpdateCountText(context.CurrentCount, context.MaxCount);
- int itemCount = context.ItemContexts?.Length ?? 0;
- int targetCount = Mathf.Min(context.CurrentCount, itemCount);
+ int targetCount = Mathf.Min(context.CurrentCount, context.ItemContexts.Length);
EnsureItemCount(targetCount);
for (int i = 0; i < targetCount; i++)
{
@@ -85,16 +78,8 @@ namespace SepCore.UI
public DisplayItem AddItem(DisplayItemContext itemContext)
{
EnsurePool();
- if (_displayItemObjectPool == null)
- {
- return null;
- }
DisplayItem item = CreateItem();
- if (item == null)
- {
- return null;
- }
int index = _activeItems.Count;
_activeItems.Add(item);
@@ -105,13 +90,9 @@ namespace SepCore.UI
LayoutItem(item, index, columnCount);
int currentCount = _activeItems.Count;
- int maxCount = _context != null ? _context.MaxCount : -1;
- if (_context != null)
- {
- _context.CurrentCount = currentCount;
- }
+ _context.CurrentCount = currentCount;
- UpdateCountText(currentCount, maxCount);
+ UpdateCountText(currentCount, _context.MaxCount);
return item;
}
@@ -127,13 +108,9 @@ namespace SepCore.UI
LayoutItems();
int currentCount = _activeItems.Count;
- int maxCount = _context != null ? _context.MaxCount : -1;
- if (_context != null)
- {
- _context.CurrentCount = currentCount;
- }
+ _context.CurrentCount = currentCount;
- UpdateCountText(currentCount, maxCount);
+ UpdateCountText(currentCount, _context.MaxCount);
return true;
}
@@ -142,11 +119,6 @@ namespace SepCore.UI
{
if (_displayItemObjectPool != null) return;
- if (_displayItemRoot == null)
- {
- _displayItemRoot = transform;
- }
-
GridLayoutGroup gridLayout = _displayItemRoot.GetComponent();
if (gridLayout != null)
{
@@ -159,12 +131,6 @@ namespace SepCore.UI
return;
}
- if (_displayItemTemplate == null)
- {
- Debug.LogError("DisplayListArea requires a DisplayItem template.");
- return;
- }
-
_displayItemObjectPool =
GameEntry.ObjectPool.CreateSingleSpawnObjectPool(DisplayItemPoolName,
_instancePoolCapacity);
@@ -172,17 +138,9 @@ namespace SepCore.UI
private void EnsureItemCount(int count)
{
- if (_displayItemObjectPool == null) return;
-
for (int i = _activeItems.Count; i < count; i++)
{
- DisplayItem item = CreateItem();
- if (item == null)
- {
- break;
- }
-
- _activeItems.Add(item);
+ _activeItems.Add(CreateItem());
}
for (int i = _activeItems.Count - 1; i >= count; i--)
@@ -193,7 +151,7 @@ namespace SepCore.UI
private DisplayItem CreateItem()
{
- DisplayItem item = null;
+ DisplayItem item;
DisplayItemObject itemObject = _displayItemObjectPool.Spawn();
if (itemObject != null)
{
@@ -201,39 +159,25 @@ namespace SepCore.UI
}
else
{
- if (_displayItemTemplate == null)
- {
- Debug.LogError("DisplayListArea requires a DisplayItem template.");
- return null;
- }
-
item = Instantiate(_displayItemTemplate);
_displayItemObjectPool.Register(DisplayItemObject.Create(item), true);
}
- if (_displayItemRoot != null)
- {
- Transform itemTransform = item.transform;
- itemTransform.SetParent(_displayItemRoot, false);
- }
-
+ item.transform.SetParent(_displayItemRoot, false);
item.gameObject.SetActive(true);
return item;
}
private void HideItem(DisplayItem item)
{
- if (item == null) return;
item.OnReset();
_activeItems.Remove(item);
item.gameObject.SetActive(false);
- _displayItemObjectPool?.Unspawn(item);
+ _displayItemObjectPool.Unspawn(item);
}
private void LayoutItems()
{
- if (_displayItemRoot == null) return;
-
int columnCount = _fixedColumnCount > 0 ? _fixedColumnCount : CalculateColumnCount(GetRootWidth());
for (int i = 0; i < _activeItems.Count; i++)
@@ -246,9 +190,7 @@ namespace SepCore.UI
private void LayoutItem(DisplayItem item, int index, int columnCount)
{
- if (item == null) return;
RectTransform itemRect = item.GetComponent();
- if (itemRect == null) return;
float step = _itemSize + _itemSpacing;
int row = index / columnCount;
@@ -263,8 +205,8 @@ namespace SepCore.UI
private float GetRootWidth()
{
- RectTransform rootRect = _displayItemRoot as RectTransform;
- return rootRect != null ? rootRect.rect.width : 0f;
+ RectTransform rootRect = (RectTransform)_displayItemRoot;
+ return rootRect.rect.width;
}
private int CalculateColumnCount(float rootWidth)
@@ -278,7 +220,6 @@ namespace SepCore.UI
private void UpdateCountText(int currentCount, int maxCount)
{
- if (_countText == null) return;
_countText.text = maxCount == -1 ? $"({currentCount})" : $"({currentCount}/{maxCount})";
}
}
diff --git a/Assets/GameMain/Scripts/Presentation/Main/Shop/GoodsItemContext.cs b/Assets/GameMain/Scripts/Presentation/Main/Shop/GoodsItemContext.cs
index c00e27c..db25ad4 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/Shop/GoodsItemContext.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/Shop/GoodsItemContext.cs
@@ -25,29 +25,13 @@ namespace SepCore.UI
ItemType = ItemType.Weapon;
}
- public GoodsItemContext(GoodsItemRawData rawData)
+ public GoodsItemContext(GoodsItemRawData rawData) : this()
{
- if (rawData == null)
- {
- Name = string.Empty;
- Description = string.Empty;
- Rarity = Rarity.White;
- Price = 0;
- Icon = null;
- ItemType = ItemType.Weapon;
- return;
- }
-
Price = rawData.Price;
Rarity = rawData.Rarity;
ItemType = rawData.ItemType;
- if (rawData.DataRow == null)
- {
- Icon = null;
- Name = string.Empty;
- }
- else if (ItemType == ItemType.Weapon)
+ if (ItemType == ItemType.Weapon)
{
if (rawData.DataRow is WeaponConfig weaponConfig)
{
diff --git a/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.BuildContext.cs b/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.BuildContext.cs
index 1f9bc61..90c4302 100644
--- a/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.BuildContext.cs
+++ b/Assets/GameMain/Scripts/Presentation/Main/Shop/ShopController.BuildContext.cs
@@ -44,53 +44,32 @@ namespace SepCore.UI
private static DisplayListAreaContext BuildDisplayListAreaContext(DisplayListAreaType listType,
IReadOnlyList