引入 UISerializationRoot 和 Item
- 完成 MenuForm 和 SelectRoleForm 的迁移
This commit is contained in:
Binary file not shown.
@@ -1,5 +1,12 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"weaponCapacity": 6
|
"weaponCapacity": 6,
|
||||||
|
"weaponRecycleRate": 300,
|
||||||
|
"shopGoodsCount": 4,
|
||||||
|
"shopPriceRandomPercent": 50,
|
||||||
|
"shopMinPrice": 0,
|
||||||
|
"propMaxCount": -1,
|
||||||
|
"levelUpRefreshPriceBase": 2,
|
||||||
|
"levelUpRewardCount": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -17,6 +17,13 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
|||||||
public GlobalConfig(ByteBuf _buf)
|
public GlobalConfig(ByteBuf _buf)
|
||||||
{
|
{
|
||||||
WeaponCapacity = _buf.ReadInt();
|
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)
|
public static GlobalConfig DeserializeGlobalConfig(ByteBuf _buf)
|
||||||
@@ -28,6 +35,34 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
|||||||
/// 最大持有武器数量
|
/// 最大持有武器数量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int WeaponCapacity;
|
public readonly int WeaponCapacity;
|
||||||
|
/// <summary>
|
||||||
|
/// 武器回收价比例
|
||||||
|
/// </summary>
|
||||||
|
public readonly int WeaponRecycleRate;
|
||||||
|
/// <summary>
|
||||||
|
/// 商店商品数量
|
||||||
|
/// </summary>
|
||||||
|
public readonly int ShopGoodsCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 商品价格浮动
|
||||||
|
/// </summary>
|
||||||
|
public readonly int ShopPriceRandomPercent;
|
||||||
|
/// <summary>
|
||||||
|
/// 商品最低价
|
||||||
|
/// </summary>
|
||||||
|
public readonly int ShopMinPrice;
|
||||||
|
/// <summary>
|
||||||
|
/// 最大持有道具数量
|
||||||
|
/// </summary>
|
||||||
|
public readonly int PropMaxCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 升级奖励刷新基本价格
|
||||||
|
/// </summary>
|
||||||
|
public readonly int LevelUpRefreshPriceBase;
|
||||||
|
/// <summary>
|
||||||
|
/// 升级奖励候选数
|
||||||
|
/// </summary>
|
||||||
|
public readonly int LevelUpRewardCount;
|
||||||
|
|
||||||
public const int __ID__ = -958250779;
|
public const int __ID__ = -958250779;
|
||||||
public override int GetTypeId() => __ID__;
|
public override int GetTypeId() => __ID__;
|
||||||
@@ -35,12 +70,26 @@ public sealed partial class GlobalConfig : Luban.BeanBase
|
|||||||
public void ResolveRef(Tables tables)
|
public void ResolveRef(Tables tables)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return "{ "
|
return "{ "
|
||||||
+ "weaponCapacity:" + WeaponCapacity + ","
|
+ "weaponCapacity:" + WeaponCapacity + ","
|
||||||
|
+ "weaponRecycleRate:" + WeaponRecycleRate + ","
|
||||||
|
+ "shopGoodsCount:" + ShopGoodsCount + ","
|
||||||
|
+ "shopPriceRandomPercent:" + ShopPriceRandomPercent + ","
|
||||||
|
+ "shopMinPrice:" + ShopMinPrice + ","
|
||||||
|
+ "propMaxCount:" + PropMaxCount + ","
|
||||||
|
+ "levelUpRefreshPriceBase:" + LevelUpRefreshPriceBase + ","
|
||||||
|
+ "levelUpRewardCount:" + LevelUpRewardCount + ","
|
||||||
+ "}";
|
+ "}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,34 @@ public partial class TbGlobalConfig
|
|||||||
/// 最大持有武器数量
|
/// 最大持有武器数量
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int WeaponCapacity => _data.WeaponCapacity;
|
public int WeaponCapacity => _data.WeaponCapacity;
|
||||||
|
/// <summary>
|
||||||
|
/// 武器回收价比例
|
||||||
|
/// </summary>
|
||||||
|
public int WeaponRecycleRate => _data.WeaponRecycleRate;
|
||||||
|
/// <summary>
|
||||||
|
/// 商店商品数量
|
||||||
|
/// </summary>
|
||||||
|
public int ShopGoodsCount => _data.ShopGoodsCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 商品价格浮动
|
||||||
|
/// </summary>
|
||||||
|
public int ShopPriceRandomPercent => _data.ShopPriceRandomPercent;
|
||||||
|
/// <summary>
|
||||||
|
/// 商品最低价
|
||||||
|
/// </summary>
|
||||||
|
public int ShopMinPrice => _data.ShopMinPrice;
|
||||||
|
/// <summary>
|
||||||
|
/// 最大持有道具数量
|
||||||
|
/// </summary>
|
||||||
|
public int PropMaxCount => _data.PropMaxCount;
|
||||||
|
/// <summary>
|
||||||
|
/// 升级奖励刷新基本价格
|
||||||
|
/// </summary>
|
||||||
|
public int LevelUpRefreshPriceBase => _data.LevelUpRefreshPriceBase;
|
||||||
|
/// <summary>
|
||||||
|
/// 升级奖励候选数
|
||||||
|
/// </summary>
|
||||||
|
public int LevelUpRewardCount => _data.LevelUpRewardCount;
|
||||||
|
|
||||||
public void ResolveRef(Tables tables)
|
public void ResolveRef(Tables tables)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 88a351268ca28c14fb18659fe3d042d3
|
guid: 3c650e4873fd47548b7df088c59adfad
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 35c8961f073eae8479506bdec8b745f8
|
guid: 10f3a6fad1dfdb444a2e57be170f9967
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -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."));
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: e82dc413a4cd277429ec1e3f2de69434
|
guid: 52071c88a467b46438b3cbf159bf988b
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 614d06243900d764f9c50b4da2c950a3
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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."));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d4b2b0d911a01a64ebfca9918e2b90b4
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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."));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 58b5a2db95af76c4c9d4116a09ec3697
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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<string> CSharpKeywords = new HashSet<string>(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<PendingGeneration> entries = new List<PendingGeneration>();
|
||||||
|
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<UISerializationRoot> generationRoots = CollectGenerationRoots(root);
|
||||||
|
List<GenerationUnit> units = BuildGenerationUnits(generationRoots);
|
||||||
|
GenerationUnit primaryUnit = units[units.Count - 1];
|
||||||
|
|
||||||
|
List<string> 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<MonoScript>(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<PendingGenerationBatch>(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<Type>(batch.entries.Count);
|
||||||
|
var resolvedFormTypes = new List<Type>(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<UISerializationRoot> CollectGenerationRoots(UISerializationRoot selectedRoot)
|
||||||
|
{
|
||||||
|
bool persistentContext = EditorUtility.IsPersistent(selectedRoot);
|
||||||
|
var result = new List<UISerializationRoot>();
|
||||||
|
var visited = new HashSet<UISerializationRoot>();
|
||||||
|
CollectGenerationRootsRecursive(selectedRoot, persistentContext, visited, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CollectGenerationRootsRecursive(
|
||||||
|
UISerializationRoot current,
|
||||||
|
bool persistentContext,
|
||||||
|
HashSet<UISerializationRoot> visited,
|
||||||
|
List<UISerializationRoot> 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<GenerationUnit> BuildGenerationUnits(List<UISerializationRoot> roots)
|
||||||
|
{
|
||||||
|
var units = new List<GenerationUnit>(roots.Count);
|
||||||
|
var classOwners = new Dictionary<string, UISerializationRoot>(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<Binding> 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<Binding> CollectBindings(UISerializationRoot root)
|
||||||
|
{
|
||||||
|
var bindings = new List<Binding>();
|
||||||
|
var names = new HashSet<string>(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<string> 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<Binding> bindings)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder(512 + bindings.Count * 96);
|
||||||
|
builder.Append("// <auto-generated />\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("// <auto-generated />\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<GameObject>(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<UISerializationRoot>();
|
||||||
|
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<GameObject>(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<Binding> 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<MonoBehaviour>()
|
||||||
|
.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<UISerializationRoot>();
|
||||||
|
if (root != null)
|
||||||
|
{
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UISerializationRoot[] loadedRoots = Resources.FindObjectsOfTypeAll<UISerializationRoot>();
|
||||||
|
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<UISerializationRoot>();
|
||||||
|
}
|
||||||
|
|
||||||
|
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<GenerationUnit> units)
|
||||||
|
{
|
||||||
|
var savedPaths = new HashSet<string>(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<string>();
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 48c1b6017a08547aa94ad17c9611d7b2
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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<string>();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: d5f17238526324f3d98afa8e8c999814
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -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<string>();
|
||||||
|
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<UISerializationRoot>();
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8c6a8249053ff4cf0a368b8a25b9946b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -26,6 +26,7 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
if (rawData == null)
|
if (rawData == null)
|
||||||
{
|
{
|
||||||
|
Log.Error("DialogController.BuildContext() rawData is null.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +61,6 @@ namespace SepCore.UI
|
|||||||
DialogContext context = BuildContext(rawData);
|
DialogContext context = BuildContext(rawData);
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
Log.Warning("DialogController.OpenUIAsync() rawData is invalid.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
if (useCase != null)
|
if (useCase != null)
|
||||||
{
|
{
|
||||||
Log.Warning("DialogController does not use a use case.");
|
Log.Error("DialogController does not use a use case.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
if (userData is not DialogContext context)
|
if (userData is not DialogContext context)
|
||||||
{
|
{
|
||||||
Log.Warning("DialogContext is invalid.");
|
Log.Error("DialogForm requires DialogContext as userData.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,12 +47,6 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
public void RefreshUI(DialogContext context)
|
public void RefreshUI(DialogContext context)
|
||||||
{
|
{
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
Log.Warning("DialogContext is invalid.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_dialogMode = context.Mode;
|
_dialogMode = context.Mode;
|
||||||
RefreshDialogMode();
|
RefreshDialogMode();
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private static string BuildPromptContext(InputActionId actionId)
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
if (userData != null)
|
if (userData != null)
|
||||||
{
|
{
|
||||||
Log.Warning("HudController.OpenUIAsync() userData type is invalid.");
|
Log.Error("HudController.OpenUIAsync() userData type is invalid.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,11 +60,6 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private void OnDeviceKindChanged(InputDeviceKind deviceKind)
|
private void OnDeviceKindChanged(InputDeviceKind deviceKind)
|
||||||
{
|
{
|
||||||
if (Context == null || Form == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Context.MovePrompt = BuildPromptContext(InputActionId.Move);
|
Context.MovePrompt = BuildPromptContext(InputActionId.Move);
|
||||||
Form.RefreshMovePrompt(Context.MovePrompt);
|
Form.RefreshMovePrompt(Context.MovePrompt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,22 +51,15 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
public void RefreshUI(HudContext hudContext)
|
public void RefreshUI(HudContext hudContext)
|
||||||
{
|
{
|
||||||
RefreshMovePrompt(hudContext?.MovePrompt);
|
RefreshMovePrompt(hudContext.MovePrompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshMovePrompt(string prompt)
|
public void RefreshMovePrompt(string prompt)
|
||||||
{
|
{
|
||||||
bool visible = !string.IsNullOrEmpty(prompt);
|
bool visible = !string.IsNullOrEmpty(prompt);
|
||||||
if (_movePromptRoot != null)
|
|
||||||
{
|
|
||||||
_movePromptRoot.SetActive(visible);
|
_movePromptRoot.SetActive(visible);
|
||||||
}
|
|
||||||
|
|
||||||
if (_movePromptText != null)
|
|
||||||
{
|
|
||||||
_movePromptText.text = prompt;
|
_movePromptText.text = prompt;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#region FSM
|
#region FSM
|
||||||
|
|
||||||
@@ -182,8 +175,6 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private void RefreshPerformanceStats()
|
private void RefreshPerformanceStats()
|
||||||
{
|
{
|
||||||
if (_cpuTimeCostText == null) return;
|
|
||||||
|
|
||||||
bool hasCpuFrame = TryGetLastValue(_cpuTimeRecorder, out long cpuFrameNanoseconds);
|
bool hasCpuFrame = TryGetLastValue(_cpuTimeRecorder, out long cpuFrameNanoseconds);
|
||||||
|
|
||||||
if (!hasCpuFrame)
|
if (!hasCpuFrame)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace SepCore.UI
|
|||||||
ShowRecycleButton = false;
|
ShowRecycleButton = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemTooltipContext(ItemTooltipRawData rawData)
|
public ItemTooltipContext(ItemTooltipRawData rawData) : this()
|
||||||
{
|
{
|
||||||
Index = rawData.Index;
|
Index = rawData.Index;
|
||||||
ItemType = rawData.ItemType;
|
ItemType = rawData.ItemType;
|
||||||
@@ -37,19 +37,15 @@ namespace SepCore.UI
|
|||||||
Price = rawData.Price;
|
Price = rawData.Price;
|
||||||
TargetPos = rawData.TargetPos;
|
TargetPos = rawData.TargetPos;
|
||||||
ShowRecycleButton = rawData.OnRecycle != null;
|
ShowRecycleButton = rawData.OnRecycle != null;
|
||||||
if (ItemType == ItemType.None)
|
|
||||||
{
|
if (ItemType == ItemType.Weapon)
|
||||||
IconAssetName = string.Empty;
|
|
||||||
Title = string.Empty;
|
|
||||||
}
|
|
||||||
else if (ItemType == ItemType.Weapon)
|
|
||||||
{
|
{
|
||||||
var data = rawData.WeaponData;
|
var data = rawData.WeaponData;
|
||||||
IconAssetName = data.IconAssetName;
|
IconAssetName = data.IconAssetName;
|
||||||
Title = data.Title;
|
Title = data.Title;
|
||||||
Description = ItemDescUtility.CreateWeaponDescription(data);
|
Description = ItemDescUtility.CreateWeaponDescription(data);
|
||||||
}
|
}
|
||||||
else
|
else if (ItemType == ItemType.Prop)
|
||||||
{
|
{
|
||||||
var data = rawData.PropData;
|
var data = rawData.PropData;
|
||||||
IconAssetName = data.IconAssetName;
|
IconAssetName = data.IconAssetName;
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ namespace SepCore.UI
|
|||||||
ItemTooltipContext context = BuildContext(rawData);
|
ItemTooltipContext context = BuildContext(rawData);
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
Log.Warning("ItemTooltipController.OpenUIAsync() rawData is invalid.");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +88,7 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
if (useCase != null)
|
if (useCase != null)
|
||||||
{
|
{
|
||||||
Log.Warning("ItemTooltipController does not use a use case.");
|
Log.Error("ItemTooltipController does not use a use case.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,33 +40,21 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
public async UniTaskVoid RefreshUI(ItemTooltipContext context)
|
public async UniTaskVoid RefreshUI(ItemTooltipContext context)
|
||||||
{
|
{
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
Log.Warning("ItemTooltipForm context is invalid.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|
||||||
_targetPos = ConvertWorldToAnchored(_context.TargetPos);
|
_targetPos = ConvertWorldToAnchored(_context.TargetPos);
|
||||||
Log.Info($"{_context.TargetPos}->{_targetPos}");
|
Log.Info($"{_context.TargetPos}->{_targetPos}");
|
||||||
|
|
||||||
if (_content != null)
|
|
||||||
{
|
|
||||||
_content.anchoredPosition3D = _targetPos;
|
_content.anchoredPosition3D = _targetPos;
|
||||||
}
|
|
||||||
|
|
||||||
_itemTitle.text = _context.Title ?? string.Empty;
|
_itemTitle.text = _context.Title;
|
||||||
_itemTypeText.text = BuildTypeText(_context.ItemType) ?? string.Empty;
|
_itemTypeText.text = BuildTypeText(_context.ItemType);
|
||||||
_itemDescription.text = _context.Description ?? string.Empty;
|
_itemDescription.text = _context.Description;
|
||||||
_itemPrice.text = $"{_context.Price} <sprite name=\"coin\" index=0>";
|
_itemPrice.text = $"{_context.Price} <sprite name=\"coin\" index=0>";
|
||||||
|
|
||||||
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;
|
string iconAssetName = _context.IconAssetName;
|
||||||
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(iconAssetName);
|
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(iconAssetName);
|
||||||
@@ -103,12 +91,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private void ResizeToFitContent()
|
private void ResizeToFitContent()
|
||||||
{
|
{
|
||||||
if (!_autoResizeHeight || _content == null)
|
if (!_autoResizeHeight)
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_itemDescription == null)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -126,11 +109,6 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private void ApplyClampedTargetPos()
|
private void ApplyClampedTargetPos()
|
||||||
{
|
{
|
||||||
if (_content == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RectTransform parent = _content.parent as RectTransform;
|
RectTransform parent = _content.parent as RectTransform;
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
{
|
{
|
||||||
@@ -160,11 +138,6 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private Vector3 ConvertWorldToAnchored(Vector3 worldPos)
|
private Vector3 ConvertWorldToAnchored(Vector3 worldPos)
|
||||||
{
|
{
|
||||||
if (_content == null)
|
|
||||||
{
|
|
||||||
return worldPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
RectTransform parent = _content.parent as RectTransform;
|
RectTransform parent = _content.parent as RectTransform;
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
{
|
{
|
||||||
@@ -192,7 +165,6 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
public void OnRecycleButtonClick()
|
public void OnRecycleButtonClick()
|
||||||
{
|
{
|
||||||
if (_context == null) return;
|
|
||||||
GameEntry.Event.Fire(this, ItemTooltipRecycleEventArgs.Create(_context.Index, _context.Price));
|
GameEntry.Event.Fire(this, ItemTooltipRecycleEventArgs.Create(_context.Index, _context.Price));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ namespace SepCore.UI
|
|||||||
private void OnCommandTriggered(InputCommand cmd)
|
private void OnCommandTriggered(InputCommand cmd)
|
||||||
{
|
{
|
||||||
if (cmd.ActionId != InputActionId.Move) return;
|
if (cmd.ActionId != InputActionId.Move) return;
|
||||||
if (Context == null || Form == null) return;
|
|
||||||
|
|
||||||
Context.Direction = cmd.Vector2Value;
|
Context.Direction = cmd.Vector2Value;
|
||||||
Context.Magnitude = cmd.Vector2Value.magnitude;
|
Context.Magnitude = cmd.Vector2Value.magnitude;
|
||||||
|
|||||||
@@ -39,20 +39,9 @@ namespace SepCore.UI
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rawData.Rewards == null)
|
|
||||||
{
|
|
||||||
Log.Error("LevelUpFormController.BuildContext() rewards are null.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<LevelUpRewardItemContext> props = new List<LevelUpRewardItemContext>(rawData.Rewards.Count);
|
List<LevelUpRewardItemContext> props = new List<LevelUpRewardItemContext>(rawData.Rewards.Count);
|
||||||
foreach (var reward in rawData.Rewards)
|
foreach (var reward in rawData.Rewards)
|
||||||
{
|
{
|
||||||
if (reward == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
props.Add(new LevelUpRewardItemContext
|
props.Add(new LevelUpRewardItemContext
|
||||||
{
|
{
|
||||||
Title = reward.Title,
|
Title = reward.Title,
|
||||||
@@ -80,7 +69,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
if (userData != null)
|
if (userData != null)
|
||||||
{
|
{
|
||||||
Log.Warning("LevelUpController.OpenUIAsync() userData type is invalid.");
|
Log.Error("LevelUpController.OpenUIAsync() userData type is invalid.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ namespace SepCore.UI
|
|||||||
propItem.gameObject.SetActive(false);
|
propItem.gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_context.Props == null) return;
|
|
||||||
int count = Mathf.Min(_propItems.Length, _context.Props.Count);
|
int count = Mathf.Min(_propItems.Length, _context.Props.Count);
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -40,13 +39,13 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
base.OnOpen(userData);
|
base.OnOpen(userData);
|
||||||
|
|
||||||
if (userData is LevelUpContext context)
|
if (userData is not LevelUpContext context)
|
||||||
{
|
{
|
||||||
RefreshUI(context);
|
Log.Error("LevelUpForm requires LevelUpContext as userData.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Warning("LevelUpForm requires LevelUpFormContext as userData.");
|
RefreshUI(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnClose(bool isShutdown, object userData)
|
protected override void OnClose(bool isShutdown, object userData)
|
||||||
|
|||||||
@@ -30,17 +30,15 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|
||||||
if (_titleText != null) _titleText.text = context.Title;
|
_titleText.text = context.Title;
|
||||||
if (_descriptionText != null) _descriptionText.text = context.Description;
|
_descriptionText.text = context.Description;
|
||||||
if (_iconArea != null) _iconArea.OnInit(context.Icon, context.Rarity);
|
_iconArea.OnInit(context.Icon, context.Rarity);
|
||||||
|
|
||||||
LoadIcon(_context.IconAssetName).Forget();
|
LoadIcon(_context.IconAssetName).Forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async UniTaskVoid LoadIcon(string iconAssetName)
|
private async UniTaskVoid LoadIcon(string iconAssetName)
|
||||||
{
|
{
|
||||||
if (_iconArea == null) return;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(iconAssetName)) return;
|
if (string.IsNullOrEmpty(iconAssetName)) return;
|
||||||
|
|
||||||
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(iconAssetName);
|
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(iconAssetName);
|
||||||
|
|||||||
@@ -23,16 +23,11 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
public async UniTaskVoid OnInit(DisplayItemContext context, int index)
|
public async UniTaskVoid OnInit(DisplayItemContext context, int index)
|
||||||
{
|
{
|
||||||
if (context == null) return;
|
|
||||||
|
|
||||||
_context = context;
|
_context = context;
|
||||||
_index = index;
|
_index = index;
|
||||||
|
|
||||||
if (_iconArea != null)
|
|
||||||
{
|
|
||||||
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(_context.IconAssetName);
|
var sprite = await SpriteLoader.LoadSpriteByKeyAsync(_context.IconAssetName);
|
||||||
_iconArea.OnInit(sprite, context.Rarity);
|
_iconArea.OnInit(sprite, _context.Rarity);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnReset()
|
public void OnReset()
|
||||||
@@ -40,16 +35,12 @@ namespace SepCore.UI
|
|||||||
_context = null;
|
_context = null;
|
||||||
_index = -1;
|
_index = -1;
|
||||||
|
|
||||||
if (_iconArea != null)
|
|
||||||
{
|
|
||||||
_iconArea.OnReset();
|
_iconArea.OnReset();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void OnItemInfoShow()
|
public void OnItemInfoShow()
|
||||||
{
|
{
|
||||||
if (_index < 0) return;
|
if (_index < 0) return;
|
||||||
if (_itemRect == null) return;
|
|
||||||
|
|
||||||
Rect rect = _itemRect.rect;
|
Rect rect = _itemRect.rect;
|
||||||
Vector3 targetPos = _itemRect.TransformPoint(new Vector3(
|
Vector3 targetPos = _itemRect.TransformPoint(new Vector3(
|
||||||
|
|||||||
@@ -44,20 +44,13 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
public void OnInit(DisplayListAreaContext context)
|
public void OnInit(DisplayListAreaContext context)
|
||||||
{
|
{
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
Log.Warning("DisplayListArea context is invalid.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsurePool();
|
EnsurePool();
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|
||||||
if (_titleText != null) _titleText.text = context.Title;
|
_titleText.text = context.Title;
|
||||||
UpdateCountText(context.CurrentCount, context.MaxCount);
|
UpdateCountText(context.CurrentCount, context.MaxCount);
|
||||||
|
|
||||||
int itemCount = context.ItemContexts?.Length ?? 0;
|
int targetCount = Mathf.Min(context.CurrentCount, context.ItemContexts.Length);
|
||||||
int targetCount = Mathf.Min(context.CurrentCount, itemCount);
|
|
||||||
EnsureItemCount(targetCount);
|
EnsureItemCount(targetCount);
|
||||||
for (int i = 0; i < targetCount; i++)
|
for (int i = 0; i < targetCount; i++)
|
||||||
{
|
{
|
||||||
@@ -85,16 +78,8 @@ namespace SepCore.UI
|
|||||||
public DisplayItem AddItem(DisplayItemContext itemContext)
|
public DisplayItem AddItem(DisplayItemContext itemContext)
|
||||||
{
|
{
|
||||||
EnsurePool();
|
EnsurePool();
|
||||||
if (_displayItemObjectPool == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
DisplayItem item = CreateItem();
|
DisplayItem item = CreateItem();
|
||||||
if (item == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = _activeItems.Count;
|
int index = _activeItems.Count;
|
||||||
_activeItems.Add(item);
|
_activeItems.Add(item);
|
||||||
@@ -105,13 +90,9 @@ namespace SepCore.UI
|
|||||||
LayoutItem(item, index, columnCount);
|
LayoutItem(item, index, columnCount);
|
||||||
|
|
||||||
int currentCount = _activeItems.Count;
|
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;
|
return item;
|
||||||
}
|
}
|
||||||
@@ -127,13 +108,9 @@ namespace SepCore.UI
|
|||||||
LayoutItems();
|
LayoutItems();
|
||||||
|
|
||||||
int currentCount = _activeItems.Count;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -142,11 +119,6 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
if (_displayItemObjectPool != null) return;
|
if (_displayItemObjectPool != null) return;
|
||||||
|
|
||||||
if (_displayItemRoot == null)
|
|
||||||
{
|
|
||||||
_displayItemRoot = transform;
|
|
||||||
}
|
|
||||||
|
|
||||||
GridLayoutGroup gridLayout = _displayItemRoot.GetComponent<GridLayoutGroup>();
|
GridLayoutGroup gridLayout = _displayItemRoot.GetComponent<GridLayoutGroup>();
|
||||||
if (gridLayout != null)
|
if (gridLayout != null)
|
||||||
{
|
{
|
||||||
@@ -159,12 +131,6 @@ namespace SepCore.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_displayItemTemplate == null)
|
|
||||||
{
|
|
||||||
Debug.LogError("DisplayListArea requires a DisplayItem template.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_displayItemObjectPool =
|
_displayItemObjectPool =
|
||||||
GameEntry.ObjectPool.CreateSingleSpawnObjectPool<DisplayItemObject>(DisplayItemPoolName,
|
GameEntry.ObjectPool.CreateSingleSpawnObjectPool<DisplayItemObject>(DisplayItemPoolName,
|
||||||
_instancePoolCapacity);
|
_instancePoolCapacity);
|
||||||
@@ -172,17 +138,9 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private void EnsureItemCount(int count)
|
private void EnsureItemCount(int count)
|
||||||
{
|
{
|
||||||
if (_displayItemObjectPool == null) return;
|
|
||||||
|
|
||||||
for (int i = _activeItems.Count; i < count; i++)
|
for (int i = _activeItems.Count; i < count; i++)
|
||||||
{
|
{
|
||||||
DisplayItem item = CreateItem();
|
_activeItems.Add(CreateItem());
|
||||||
if (item == null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_activeItems.Add(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = _activeItems.Count - 1; i >= count; i--)
|
for (int i = _activeItems.Count - 1; i >= count; i--)
|
||||||
@@ -193,7 +151,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private DisplayItem CreateItem()
|
private DisplayItem CreateItem()
|
||||||
{
|
{
|
||||||
DisplayItem item = null;
|
DisplayItem item;
|
||||||
DisplayItemObject itemObject = _displayItemObjectPool.Spawn();
|
DisplayItemObject itemObject = _displayItemObjectPool.Spawn();
|
||||||
if (itemObject != null)
|
if (itemObject != null)
|
||||||
{
|
{
|
||||||
@@ -201,39 +159,25 @@ namespace SepCore.UI
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_displayItemTemplate == null)
|
|
||||||
{
|
|
||||||
Debug.LogError("DisplayListArea requires a DisplayItem template.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
item = Instantiate(_displayItemTemplate);
|
item = Instantiate(_displayItemTemplate);
|
||||||
_displayItemObjectPool.Register(DisplayItemObject.Create(item), true);
|
_displayItemObjectPool.Register(DisplayItemObject.Create(item), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_displayItemRoot != null)
|
item.transform.SetParent(_displayItemRoot, false);
|
||||||
{
|
|
||||||
Transform itemTransform = item.transform;
|
|
||||||
itemTransform.SetParent(_displayItemRoot, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.gameObject.SetActive(true);
|
item.gameObject.SetActive(true);
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HideItem(DisplayItem item)
|
private void HideItem(DisplayItem item)
|
||||||
{
|
{
|
||||||
if (item == null) return;
|
|
||||||
item.OnReset();
|
item.OnReset();
|
||||||
_activeItems.Remove(item);
|
_activeItems.Remove(item);
|
||||||
item.gameObject.SetActive(false);
|
item.gameObject.SetActive(false);
|
||||||
_displayItemObjectPool?.Unspawn(item);
|
_displayItemObjectPool.Unspawn(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LayoutItems()
|
private void LayoutItems()
|
||||||
{
|
{
|
||||||
if (_displayItemRoot == null) return;
|
|
||||||
|
|
||||||
int columnCount = _fixedColumnCount > 0 ? _fixedColumnCount : CalculateColumnCount(GetRootWidth());
|
int columnCount = _fixedColumnCount > 0 ? _fixedColumnCount : CalculateColumnCount(GetRootWidth());
|
||||||
|
|
||||||
for (int i = 0; i < _activeItems.Count; i++)
|
for (int i = 0; i < _activeItems.Count; i++)
|
||||||
@@ -246,9 +190,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private void LayoutItem(DisplayItem item, int index, int columnCount)
|
private void LayoutItem(DisplayItem item, int index, int columnCount)
|
||||||
{
|
{
|
||||||
if (item == null) return;
|
|
||||||
RectTransform itemRect = item.GetComponent<RectTransform>();
|
RectTransform itemRect = item.GetComponent<RectTransform>();
|
||||||
if (itemRect == null) return;
|
|
||||||
|
|
||||||
float step = _itemSize + _itemSpacing;
|
float step = _itemSize + _itemSpacing;
|
||||||
int row = index / columnCount;
|
int row = index / columnCount;
|
||||||
@@ -263,8 +205,8 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private float GetRootWidth()
|
private float GetRootWidth()
|
||||||
{
|
{
|
||||||
RectTransform rootRect = _displayItemRoot as RectTransform;
|
RectTransform rootRect = (RectTransform)_displayItemRoot;
|
||||||
return rootRect != null ? rootRect.rect.width : 0f;
|
return rootRect.rect.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int CalculateColumnCount(float rootWidth)
|
private int CalculateColumnCount(float rootWidth)
|
||||||
@@ -278,7 +220,6 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private void UpdateCountText(int currentCount, int maxCount)
|
private void UpdateCountText(int currentCount, int maxCount)
|
||||||
{
|
{
|
||||||
if (_countText == null) return;
|
|
||||||
_countText.text = maxCount == -1 ? $"({currentCount})" : $"({currentCount}/{maxCount})";
|
_countText.text = maxCount == -1 ? $"({currentCount})" : $"({currentCount}/{maxCount})";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,29 +25,13 @@ namespace SepCore.UI
|
|||||||
ItemType = ItemType.Weapon;
|
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;
|
Price = rawData.Price;
|
||||||
Rarity = rawData.Rarity;
|
Rarity = rawData.Rarity;
|
||||||
ItemType = rawData.ItemType;
|
ItemType = rawData.ItemType;
|
||||||
|
|
||||||
if (rawData.DataRow == null)
|
if (ItemType == ItemType.Weapon)
|
||||||
{
|
|
||||||
Icon = null;
|
|
||||||
Name = string.Empty;
|
|
||||||
}
|
|
||||||
else if (ItemType == ItemType.Weapon)
|
|
||||||
{
|
{
|
||||||
if (rawData.DataRow is WeaponConfig weaponConfig)
|
if (rawData.DataRow is WeaponConfig weaponConfig)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,53 +44,32 @@ namespace SepCore.UI
|
|||||||
private static DisplayListAreaContext BuildDisplayListAreaContext(DisplayListAreaType listType,
|
private static DisplayListAreaContext BuildDisplayListAreaContext(DisplayListAreaType listType,
|
||||||
IReadOnlyList<object> items, int maxCount)
|
IReadOnlyList<object> items, int maxCount)
|
||||||
{
|
{
|
||||||
string title = GetDisplayListTitle(listType);
|
|
||||||
if (items == null)
|
|
||||||
{
|
|
||||||
return new DisplayListAreaContext
|
|
||||||
{
|
|
||||||
Title = title,
|
|
||||||
CurrentCount = 0,
|
|
||||||
MaxCount = maxCount,
|
|
||||||
ItemContexts = System.Array.Empty<DisplayItemContext>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
DisplayItemContext[] itemContexts = new DisplayItemContext[items.Count];
|
DisplayItemContext[] itemContexts = new DisplayItemContext[items.Count];
|
||||||
switch (listType)
|
switch (listType)
|
||||||
{
|
{
|
||||||
case DisplayListAreaType.Weapon:
|
case DisplayListAreaType.Weapon:
|
||||||
if (items is IReadOnlyList<WeaponBase> weapons)
|
IReadOnlyList<WeaponBase> weapons = (IReadOnlyList<WeaponBase>)items;
|
||||||
{
|
|
||||||
for (int i = 0; i < weapons.Count; i++)
|
for (int i = 0; i < weapons.Count; i++)
|
||||||
{
|
{
|
||||||
WeaponBase weapon = weapons[i];
|
itemContexts[i] = BuildWeaponItem(weapons[i]);
|
||||||
if (weapon == null) break;
|
|
||||||
itemContexts[i] = BuildWeaponItem(weapon);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DisplayListAreaType.Prop:
|
case DisplayListAreaType.Prop:
|
||||||
if (items is IReadOnlyList<PropItem> propItems)
|
IReadOnlyList<PropItem> propItems = (IReadOnlyList<PropItem>)items;
|
||||||
{
|
|
||||||
for (int i = 0; i < propItems.Count; i++)
|
for (int i = 0; i < propItems.Count; i++)
|
||||||
{
|
{
|
||||||
PropItem propItem = propItems[i];
|
itemContexts[i] = BuildPropItem(propItems[i]);
|
||||||
if (propItem == null) break;
|
|
||||||
itemContexts[i] = BuildPropItem(propItem);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int currentCount = itemContexts.Length;
|
|
||||||
return new DisplayListAreaContext
|
return new DisplayListAreaContext
|
||||||
{
|
{
|
||||||
Title = title,
|
Title = GetDisplayListTitle(listType),
|
||||||
CurrentCount = currentCount,
|
CurrentCount = itemContexts.Length,
|
||||||
MaxCount = maxCount,
|
MaxCount = maxCount,
|
||||||
ItemContexts = itemContexts
|
ItemContexts = itemContexts
|
||||||
};
|
};
|
||||||
@@ -108,45 +87,27 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private static DisplayItemContext BuildPropItem(PropItem propItem)
|
private static DisplayItemContext BuildPropItem(PropItem propItem)
|
||||||
{
|
{
|
||||||
string iconAssetName = null;
|
|
||||||
Rarity rarity = Rarity.None;
|
|
||||||
|
|
||||||
if (propItem != null)
|
|
||||||
{
|
|
||||||
iconAssetName = propItem.IconAssetName;
|
|
||||||
rarity = propItem.Rarity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DisplayItemContext
|
return new DisplayItemContext
|
||||||
{
|
{
|
||||||
IconAssetName = iconAssetName,
|
IconAssetName = propItem.IconAssetName,
|
||||||
Rarity = rarity,
|
Rarity = propItem.Rarity,
|
||||||
IsWeapon = false
|
IsWeapon = false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DisplayItemContext BuildWeaponItem(WeaponBase weaponBase)
|
private static DisplayItemContext BuildWeaponItem(WeaponBase weaponBase)
|
||||||
{
|
{
|
||||||
string iconAssetName = null;
|
|
||||||
Rarity rarity = Rarity.None;
|
|
||||||
|
|
||||||
if (weaponBase != null && weaponBase.WeaponData != null)
|
|
||||||
{
|
|
||||||
iconAssetName = weaponBase.WeaponData.IconAssetName;
|
|
||||||
rarity = weaponBase.WeaponData.Rarity;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DisplayItemContext
|
return new DisplayItemContext
|
||||||
{
|
{
|
||||||
IconAssetName = iconAssetName,
|
IconAssetName = weaponBase.WeaponData.IconAssetName,
|
||||||
Rarity = rarity,
|
Rarity = weaponBase.WeaponData.Rarity,
|
||||||
IsWeapon = true
|
IsWeapon = true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AppendDisplayItemContext(DisplayListAreaContext listContext, DisplayItemContext newItem)
|
private static void AppendDisplayItemContext(DisplayListAreaContext listContext, DisplayItemContext newItem)
|
||||||
{
|
{
|
||||||
int oldCount = listContext.ItemContexts != null ? listContext.ItemContexts.Length : 0;
|
int oldCount = listContext.ItemContexts.Length;
|
||||||
DisplayItemContext[] newContexts = new DisplayItemContext[oldCount + 1];
|
DisplayItemContext[] newContexts = new DisplayItemContext[oldCount + 1];
|
||||||
if (oldCount > 0)
|
if (oldCount > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -126,15 +126,13 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private void ApplyGoodsPurchased(ShopPurchaseResult result)
|
private void ApplyGoodsPurchased(ShopPurchaseResult result)
|
||||||
{
|
{
|
||||||
if (Context.GoodsItems != null && result.GoodsIndex >= 0 && result.GoodsIndex < Context.GoodsItems.Count)
|
if (result.GoodsIndex >= 0 && result.GoodsIndex < Context.GoodsItems.Count)
|
||||||
{
|
{
|
||||||
Context.GoodsItems[result.GoodsIndex] = null;
|
Context.GoodsItems[result.GoodsIndex] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
DisplayItemContext context = new DisplayItemContext(result.DisplayItem);
|
DisplayItemContext context = new DisplayItemContext(result.DisplayItem);
|
||||||
|
|
||||||
if (result.DisplayItem != null)
|
|
||||||
{
|
|
||||||
if (result.DisplayItem.IsWeapon)
|
if (result.DisplayItem.IsWeapon)
|
||||||
{
|
{
|
||||||
AppendDisplayItemContext(Context.WeaponListContext, context);
|
AppendDisplayItemContext(Context.WeaponListContext, context);
|
||||||
@@ -145,7 +143,6 @@ namespace SepCore.UI
|
|||||||
AppendDisplayItemContext(Context.PropListContext, context);
|
AppendDisplayItemContext(Context.PropListContext, context);
|
||||||
Context.NeedRefreshPropList = true;
|
Context.NeedRefreshPropList = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Context.NeedRefreshGoodsItems = true;
|
Context.NeedRefreshGoodsItems = true;
|
||||||
RefreshUI(Form, Context);
|
RefreshUI(Form, Context);
|
||||||
@@ -242,11 +239,8 @@ namespace SepCore.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Context.WeaponListContext != null)
|
|
||||||
{
|
|
||||||
int currentCount = Mathf.Max(0, Context.WeaponListContext.CurrentCount - 1);
|
int currentCount = Mathf.Max(0, Context.WeaponListContext.CurrentCount - 1);
|
||||||
Context.WeaponListContext.CurrentCount = currentCount;
|
Context.WeaponListContext.CurrentCount = currentCount;
|
||||||
}
|
|
||||||
|
|
||||||
Context.NeedRefreshWeaponList = true;
|
Context.NeedRefreshWeaponList = true;
|
||||||
RefreshUI(Form, Context);
|
RefreshUI(Form, Context);
|
||||||
@@ -301,7 +295,7 @@ namespace SepCore.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_useCase?.Continue();
|
_useCase.Continue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisplayItemHover(object sender, GameEventArgs e)
|
private void DisplayItemHover(object sender, GameEventArgs e)
|
||||||
@@ -359,7 +353,6 @@ namespace SepCore.UI
|
|||||||
private void PlayerCoinChange(object sender, GameEventArgs e)
|
private void PlayerCoinChange(object sender, GameEventArgs e)
|
||||||
{
|
{
|
||||||
if (e is not PlayerCoinChangeEventArgs args) return;
|
if (e is not PlayerCoinChangeEventArgs args) return;
|
||||||
if (Context == null) return;
|
|
||||||
|
|
||||||
Context.PlayerCoin = args.CoinCount;
|
Context.PlayerCoin = args.CoinCount;
|
||||||
Context.NeedRefreshPlayerCoin = true;
|
Context.NeedRefreshPlayerCoin = true;
|
||||||
|
|||||||
@@ -54,13 +54,13 @@ namespace SepCore.UI
|
|||||||
context.NeedRefreshPlayerCoin = false;
|
context.NeedRefreshPlayerCoin = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.NeedRefreshPropList && _propListArea != null && context.PropListContext != null)
|
if (context.NeedRefreshPropList)
|
||||||
{
|
{
|
||||||
_propListArea.OnInit(context.PropListContext);
|
_propListArea.OnInit(context.PropListContext);
|
||||||
context.NeedRefreshPropList = false;
|
context.NeedRefreshPropList = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.NeedRefreshWeaponList && _weaponListArea != null && context.WeaponListContext != null)
|
if (context.NeedRefreshWeaponList)
|
||||||
{
|
{
|
||||||
_weaponListArea.OnInit(context.WeaponListContext);
|
_weaponListArea.OnInit(context.WeaponListContext);
|
||||||
context.NeedRefreshWeaponList = false;
|
context.NeedRefreshWeaponList = false;
|
||||||
@@ -74,11 +74,6 @@ namespace SepCore.UI
|
|||||||
item.gameObject.SetActive(false);
|
item.gameObject.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (goodsItems == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = Mathf.Min(_goodsItems.Length, goodsItems.Count);
|
int count = Mathf.Min(_goodsItems.Length, goodsItems.Count);
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -94,54 +89,6 @@ namespace SepCore.UI
|
|||||||
_refreshPriceText.text = $"刷新 -{refreshPrice} <sprite name=\"coin\">";
|
_refreshPriceText.text = $"刷新 -{refreshPrice} <sprite name=\"coin\">";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyGoodsPurchased(int goodsIndex, DisplayItemContext displayItem)
|
|
||||||
{
|
|
||||||
SetGoodsItemVisible(goodsIndex, false);
|
|
||||||
|
|
||||||
if (displayItem == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (displayItem.IsWeapon)
|
|
||||||
{
|
|
||||||
AddWeaponDisplayItem(displayItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AddPropDisplayItem(displayItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetGoodsItemVisible(int index, bool visible)
|
|
||||||
{
|
|
||||||
if (_goodsItems == null || index < 0 || index >= _goodsItems.Length)
|
|
||||||
{
|
|
||||||
Log.Warning("ShopForm.SetGoodsItemVisible: Invalid index.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_goodsItems[index].gameObject.SetActive(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddPropDisplayItem(DisplayItemContext context)
|
|
||||||
{
|
|
||||||
if (_propListArea == null || context == null) return;
|
|
||||||
_propListArea.AddItem(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddWeaponDisplayItem(DisplayItemContext context)
|
|
||||||
{
|
|
||||||
if (_weaponListArea == null || context == null) return;
|
|
||||||
_weaponListArea.AddItem(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RemoveWeaponDisplayItem(int index)
|
|
||||||
{
|
|
||||||
if (_weaponListArea == null) return;
|
|
||||||
_weaponListArea.RemoveItemAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region ButtonClick
|
#region ButtonClick
|
||||||
|
|
||||||
public void OnContinueButtonClick()
|
public void OnContinueButtonClick()
|
||||||
@@ -167,21 +114,21 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
base.OnOpen(userData);
|
base.OnOpen(userData);
|
||||||
|
|
||||||
if (userData is ShopContext context)
|
if (userData is not ShopContext context)
|
||||||
{
|
{
|
||||||
RefreshUI(context);
|
Log.Error("ShopForm requires ShopContext as userData.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Warning("ShopForm requires ShopFormContext as userData.");
|
RefreshUI(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnClose(bool isShutdown, object userData)
|
protected override void OnClose(bool isShutdown, object userData)
|
||||||
{
|
{
|
||||||
_context = null;
|
_context = null;
|
||||||
|
|
||||||
_propListArea?.OnReset();
|
_propListArea.OnReset();
|
||||||
_weaponListArea?.OnReset();
|
_weaponListArea.OnReset();
|
||||||
|
|
||||||
base.OnClose(isShutdown, userData);
|
base.OnClose(isShutdown, userData);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
using SepCore.Definition;
|
|
||||||
using SepCore.Event;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityGameFramework.Runtime;
|
|
||||||
|
|
||||||
namespace SepCore.UI
|
|
||||||
{
|
|
||||||
public class MenuForm : UGuiForm
|
|
||||||
{
|
|
||||||
[SerializeField] private GameObject _defaultButton;
|
|
||||||
|
|
||||||
public void RefreshUI(MenuContext context)
|
|
||||||
{
|
|
||||||
if (EventSystem.current.currentSelectedGameObject != _defaultButton)
|
|
||||||
{
|
|
||||||
EventSystem.current.SetSelectedGameObject(this._defaultButton);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Lifecycle
|
|
||||||
|
|
||||||
protected override void OnOpen(object userData)
|
|
||||||
{
|
|
||||||
base.OnOpen(userData);
|
|
||||||
|
|
||||||
if (userData is MenuContext context)
|
|
||||||
{
|
|
||||||
RefreshUI(context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Warning("MenuForm requires MenuFormContext as userData.");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnClose(bool isShutdown, object userData)
|
|
||||||
{
|
|
||||||
base.OnClose(isShutdown, userData);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
// 以下方法被 MenuForm.prefab 中的 Button.onClick 通过名称反射绑定。
|
|
||||||
// 重命名前需同步修改 prefab 中的 m_MethodName 引用。
|
|
||||||
public void OnStartButtonClick()
|
|
||||||
{
|
|
||||||
FireButtonClick(MenuButtonId.StartGame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnFileButtonClick()
|
|
||||||
{
|
|
||||||
FireButtonClick(MenuButtonId.File);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnGuideButtonClick()
|
|
||||||
{
|
|
||||||
FireButtonClick(MenuButtonId.Guide);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnSettingButtonClick()
|
|
||||||
{
|
|
||||||
FireButtonClick(MenuButtonId.Setting);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnQuitButtonClick()
|
|
||||||
{
|
|
||||||
FireButtonClick(MenuButtonId.Quit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnAboutButtonClick()
|
|
||||||
{
|
|
||||||
FireButtonClick(MenuButtonId.About);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FireButtonClick(MenuButtonId button)
|
|
||||||
{
|
|
||||||
GameEntry.Event.Fire(this, MenuButtonClickEventArgs.Create(button));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -34,7 +34,7 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
if (userData != null)
|
if (userData != null)
|
||||||
{
|
{
|
||||||
Log.Warning("MenuController.OpenUIAsync() userData type is invalid.");
|
Log.Error("MenuController.OpenUIAsync() userData type is invalid.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using SepCore.Definition;
|
||||||
|
using SepCore.Event;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
public partial class MenuForm
|
||||||
|
{
|
||||||
|
private bool _listenersBound;
|
||||||
|
|
||||||
|
public void RefreshUI(MenuContext context)
|
||||||
|
{
|
||||||
|
EnsureButtonListeners();
|
||||||
|
|
||||||
|
if (EventSystem.current.currentSelectedGameObject != View.startButton.gameObject)
|
||||||
|
{
|
||||||
|
EventSystem.current.SetSelectedGameObject(View.startButton.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
|
|
||||||
|
protected override void OnOpen(object userData)
|
||||||
|
{
|
||||||
|
base.OnOpen(userData);
|
||||||
|
|
||||||
|
if (userData is not MenuContext context)
|
||||||
|
{
|
||||||
|
Log.Error("MenuForm requires MenuContext as userData.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshUI(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private void EnsureButtonListeners()
|
||||||
|
{
|
||||||
|
if (_listenersBound)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_listenersBound = true;
|
||||||
|
|
||||||
|
View.startButton.onClick.AddListener(OnStartButtonClick);
|
||||||
|
View.fileButton.onClick.AddListener(OnFileButtonClick);
|
||||||
|
View.guideButton.onClick.AddListener(OnGuideButtonClick);
|
||||||
|
View.settingButton.onClick.AddListener(OnSettingButtonClick);
|
||||||
|
View.quitButton.onClick.AddListener(OnQuitButtonClick);
|
||||||
|
View.aboutButton.onClick.AddListener(OnAboutButtonClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStartButtonClick()
|
||||||
|
{
|
||||||
|
FireButtonClick(MenuButtonId.StartGame);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFileButtonClick()
|
||||||
|
{
|
||||||
|
FireButtonClick(MenuButtonId.File);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGuideButtonClick()
|
||||||
|
{
|
||||||
|
FireButtonClick(MenuButtonId.Guide);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSettingButtonClick()
|
||||||
|
{
|
||||||
|
FireButtonClick(MenuButtonId.Setting);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnQuitButtonClick()
|
||||||
|
{
|
||||||
|
FireButtonClick(MenuButtonId.Quit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAboutButtonClick()
|
||||||
|
{
|
||||||
|
FireButtonClick(MenuButtonId.About);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FireButtonClick(MenuButtonId button)
|
||||||
|
{
|
||||||
|
GameEntry.Event.Fire(this, MenuButtonClickEventArgs.Create(button));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public partial class MenuForm : UGuiForm
|
||||||
|
{
|
||||||
|
[SerializeField] private MenuView _view = null;
|
||||||
|
|
||||||
|
public MenuView View => _view;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8b02c5e7bddd2ab418026de1527b5e25
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public partial class MenuView : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] public global::SepCore.UI.CommonButton startButton;
|
||||||
|
[SerializeField] public global::SepCore.UI.CommonButton fileButton;
|
||||||
|
[SerializeField] public global::SepCore.UI.CommonButton guideButton;
|
||||||
|
[SerializeField] public global::SepCore.UI.CommonButton settingButton;
|
||||||
|
[SerializeField] public global::SepCore.UI.CommonButton quitButton;
|
||||||
|
[SerializeField] public global::SepCore.UI.CommonButton aboutButton;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 47c8b7ced811ef448ad8b07d5cafdfc7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
using TMPro;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
namespace SepCore.UI
|
|
||||||
{
|
|
||||||
public class RolePropertyArea : MonoBehaviour
|
|
||||||
{
|
|
||||||
[SerializeField] private TMP_Text _roleNameText;
|
|
||||||
|
|
||||||
[SerializeField] private RawImage _roleAvatar;
|
|
||||||
|
|
||||||
[SerializeField] private TMP_Text _roleInitialPropertyText;
|
|
||||||
|
|
||||||
[SerializeField] private Transform _roleInitialItemParent;
|
|
||||||
|
|
||||||
private RolePropertyAreaContext _context;
|
|
||||||
|
|
||||||
public void OnInit(RolePropertyAreaContext context)
|
|
||||||
{
|
|
||||||
_context = context;
|
|
||||||
if (_context == null)
|
|
||||||
{
|
|
||||||
OnReset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_roleNameText.text = _context.RoleName;
|
|
||||||
_roleInitialPropertyText.text = _context.InitialPropertyText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnReset(object userData = null)
|
|
||||||
{
|
|
||||||
_context = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+17
-4
@@ -12,10 +12,14 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private RoleItemContext _context = null;
|
private RoleItemContext _context = null;
|
||||||
|
|
||||||
|
private bool _listenersBound;
|
||||||
|
|
||||||
public async UniTaskVoid OnInit(RoleItemContext context)
|
public async UniTaskVoid OnInit(RoleItemContext context)
|
||||||
{
|
{
|
||||||
|
EnsureListeners();
|
||||||
|
|
||||||
_context = context;
|
_context = context;
|
||||||
if (_context == null || string.IsNullOrEmpty(_context.IconName))
|
if (string.IsNullOrEmpty(_context.IconName))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -28,17 +32,26 @@ namespace SepCore.UI
|
|||||||
_context = null;
|
_context = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnHoverEnter()
|
private void EnsureListeners()
|
||||||
{
|
{
|
||||||
if (_context == null)
|
if (_listenersBound)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_listenersBound = true;
|
||||||
|
|
||||||
|
CommonButton button = GetComponent<CommonButton>();
|
||||||
|
button.onClick.AddListener(OnConfirmRoleClick);
|
||||||
|
button.RegisterPointerEnterAction(OnHoverEnter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHoverEnter()
|
||||||
|
{
|
||||||
GameEntry.Event.Fire(this, SelectRoleHoverEventArgs.Create(_context.RoleName));
|
GameEntry.Event.Fire(this, SelectRoleHoverEventArgs.Create(_context.RoleName));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnConfirmRoleClick()
|
private void OnConfirmRoleClick()
|
||||||
{
|
{
|
||||||
GameEntry.Event.Fire(this, SelectRoleConfirmEventArgs.Create());
|
GameEntry.Event.Fire(this, SelectRoleConfirmEventArgs.Create());
|
||||||
}
|
}
|
||||||
+1
-1
@@ -3,7 +3,7 @@ namespace SepCore.UI
|
|||||||
public class SelectRoleContext : UIContext
|
public class SelectRoleContext : UIContext
|
||||||
{
|
{
|
||||||
public RoleItemContext[] RoleItemContexts;
|
public RoleItemContext[] RoleItemContexts;
|
||||||
public RolePropertyAreaContext RolePropertyAreaContext;
|
public ShowAreaContext ShowAreaContext;
|
||||||
public bool ShowRoleProperty;
|
public bool ShowRoleProperty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+6
-7
@@ -43,7 +43,7 @@ namespace SepCore.UI
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = rawData.RoleItems?.Length ?? 0;
|
int count = rawData.RoleItems.Length;
|
||||||
RoleItemContext[] roleItems = new RoleItemContext[count];
|
RoleItemContext[] roleItems = new RoleItemContext[count];
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
@@ -55,22 +55,21 @@ namespace SepCore.UI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
RolePropertyAreaContext propertyContext = null;
|
ShowAreaContext showAreaContext = null;
|
||||||
if (rawData.SelectedRole != null)
|
if (rawData.SelectedRole != null)
|
||||||
{
|
{
|
||||||
propertyContext = new RolePropertyAreaContext
|
showAreaContext = new ShowAreaContext
|
||||||
{
|
{
|
||||||
RoleName = rawData.SelectedRole.Name,
|
RoleName = rawData.SelectedRole.Name,
|
||||||
InitialPropertyText = ItemDescUtility.CreatePropDescription(rawData.SelectedRole.InitialProperties)
|
InitialPropertyText = ItemDescUtility.CreatePropDescription(rawData.SelectedRole.InitialProperties)
|
||||||
?? string.Empty
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SelectRoleContext
|
return new SelectRoleContext
|
||||||
{
|
{
|
||||||
RoleItemContexts = roleItems,
|
RoleItemContexts = roleItems,
|
||||||
RolePropertyAreaContext = propertyContext,
|
ShowAreaContext = showAreaContext,
|
||||||
ShowRoleProperty = propertyContext != null
|
ShowRoleProperty = showAreaContext != null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +83,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
if (userData != null)
|
if (userData != null)
|
||||||
{
|
{
|
||||||
Log.Warning("SelectRoleController.OpenUIAsync() userData type is invalid.");
|
Log.Error("SelectRoleController.OpenUIAsync() userData type is invalid.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
+40
-32
@@ -1,27 +1,27 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
using SepCore.Event;
|
using SepCore.Event;
|
||||||
using UnityEngine;
|
|
||||||
using UnityGameFramework.Runtime;
|
using UnityGameFramework.Runtime;
|
||||||
|
|
||||||
namespace SepCore.UI
|
namespace SepCore.UI
|
||||||
{
|
{
|
||||||
public class SelectRoleForm : UGuiForm
|
public partial class SelectRoleForm
|
||||||
{
|
{
|
||||||
[SerializeField] private GameObject _randomRoleCard = null;
|
private readonly List<RoleItem> _roleItems = new List<RoleItem>();
|
||||||
|
|
||||||
[SerializeField] private GameObject _roleCard = null;
|
|
||||||
|
|
||||||
[SerializeField] private RolePropertyArea _propertyArea = null;
|
|
||||||
|
|
||||||
private SelectRoleContext _context = null;
|
private SelectRoleContext _context = null;
|
||||||
|
|
||||||
private RoleItem[] _roleItems = null;
|
private bool _listenersBound;
|
||||||
|
|
||||||
public void RefreshUI(SelectRoleContext context)
|
public void RefreshUI(SelectRoleContext context)
|
||||||
{
|
{
|
||||||
|
EnsureButtonListeners();
|
||||||
|
|
||||||
_context = context;
|
_context = context;
|
||||||
|
|
||||||
|
EnsureRoleItemCount(context.RoleItemContexts.Length);
|
||||||
|
|
||||||
RoleItemContext[] roleContexts = _context.RoleItemContexts;
|
RoleItemContext[] roleContexts = _context.RoleItemContexts;
|
||||||
for (int i = 0; i < _roleItems.Length; i++)
|
for (int i = 0; i < _roleItems.Count; i++)
|
||||||
{
|
{
|
||||||
RoleItem item = _roleItems[i];
|
RoleItem item = _roleItems[i];
|
||||||
if (i < roleContexts.Length)
|
if (i < roleContexts.Length)
|
||||||
@@ -36,16 +36,17 @@ namespace SepCore.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateShowRole(_context.ShowRoleProperty, _context.RolePropertyAreaContext);
|
if (_context.ShowRoleProperty)
|
||||||
}
|
|
||||||
|
|
||||||
#region FSM
|
|
||||||
|
|
||||||
protected override void OnInit(object userData)
|
|
||||||
{
|
{
|
||||||
base.OnInit(userData);
|
View.showAreaForm.ShowRoleProperty(_context.ShowAreaContext);
|
||||||
_roleItems = this.transform.GetComponentsInChildren<RoleItem>();
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
View.showAreaForm.ShowRandomRole();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Lifecycle
|
||||||
|
|
||||||
protected override void OnOpen(object userData)
|
protected override void OnOpen(object userData)
|
||||||
{
|
{
|
||||||
@@ -57,7 +58,7 @@ namespace SepCore.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Warning("SelectRoleForm requires SelectRoleFormContext as userData.");
|
Log.Error("SelectRoleForm requires SelectRoleContext as userData.");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnClose(bool isShutdown, object userData)
|
protected override void OnClose(bool isShutdown, object userData)
|
||||||
@@ -65,41 +66,48 @@ namespace SepCore.UI
|
|||||||
base.OnClose(isShutdown, userData);
|
base.OnClose(isShutdown, userData);
|
||||||
|
|
||||||
_context = null;
|
_context = null;
|
||||||
foreach (var item in _roleItems)
|
for (int i = 0; i < _roleItems.Count; i++)
|
||||||
{
|
{
|
||||||
item.OnReset();
|
_roleItems[i].OnReset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private void UpdateShowRole(bool showRoleProperty, RolePropertyAreaContext propertyAreaContext)
|
private void EnsureButtonListeners()
|
||||||
{
|
{
|
||||||
if (!showRoleProperty)
|
if (_listenersBound)
|
||||||
{
|
{
|
||||||
_randomRoleCard.SetActive(true);
|
return;
|
||||||
_roleCard.SetActive(false);
|
|
||||||
_propertyArea.OnReset();
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
_listenersBound = true;
|
||||||
|
|
||||||
|
View.returnButton.onClick.AddListener(OnReturnButtonClick);
|
||||||
|
View.randomRoleItem.onClick.AddListener(OnRandomConfirmClick);
|
||||||
|
View.randomRoleItem.RegisterPointerEnterAction(OnRandomHoverEnter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureRoleItemCount(int count)
|
||||||
{
|
{
|
||||||
_randomRoleCard.SetActive(false);
|
while (_roleItems.Count < count)
|
||||||
_propertyArea.OnInit(propertyAreaContext);
|
{
|
||||||
_roleCard.SetActive(true);
|
RoleItem item = Instantiate(View.roleItemTemplate, View.roleListRoot);
|
||||||
|
_roleItems.Add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnReturnButtonClick()
|
private void OnReturnButtonClick()
|
||||||
{
|
{
|
||||||
GameEntry.Event.Fire(this, SelectRoleReturnEventArgs.Create());
|
GameEntry.Event.Fire(this, SelectRoleReturnEventArgs.Create());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnRandomConfirmClick()
|
private void OnRandomConfirmClick()
|
||||||
{
|
{
|
||||||
GameEntry.Event.Fire(this, SelectRoleConfirmEventArgs.Create());
|
GameEntry.Event.Fire(this, SelectRoleConfirmEventArgs.Create());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnRandomHoverEnter()
|
private void OnRandomHoverEnter()
|
||||||
{
|
{
|
||||||
GameEntry.Event.Fire(this, SelectRoleClearEventArgs.Create());
|
GameEntry.Event.Fire(this, SelectRoleClearEventArgs.Create());
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ad9c806bb95be684cb75ae84394f8cd0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public partial class SelectRoleForm : UGuiForm
|
||||||
|
{
|
||||||
|
[SerializeField] private SelectRoleView _view = null;
|
||||||
|
|
||||||
|
public SelectRoleView View => _view;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public partial class SelectRoleView : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] public global::SepCore.UI.CommonButton returnButton;
|
||||||
|
[SerializeField] public global::UnityEngine.RectTransform roleListRoot;
|
||||||
|
[SerializeField] public global::SepCore.UI.CommonButton randomRoleItem;
|
||||||
|
[SerializeField] public global::SepCore.UI.RoleItem roleItemTemplate;
|
||||||
|
[SerializeField] public global::SepCore.UI.ShowAreaForm showAreaForm;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 69fadbcf4f76eb4499b21ac72f667fc0
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: f228612923ff51343a4dab06ce4c1f92
|
guid: a0f180c7a5bba1f44af212a1a8d6f328
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
[ExecuteAlways]
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public sealed class UISerializationItem : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public sealed class ComponentReference
|
||||||
|
{
|
||||||
|
[SerializeField] private Component component;
|
||||||
|
[SerializeField] private bool generateReference;
|
||||||
|
[SerializeField] private string variableName;
|
||||||
|
|
||||||
|
public Component Component => component;
|
||||||
|
public bool GenerateReference => generateReference;
|
||||||
|
public string VariableName => variableName;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
internal ComponentReference(Component target, string defaultVariableName)
|
||||||
|
{
|
||||||
|
component = target;
|
||||||
|
generateReference = false;
|
||||||
|
variableName = defaultVariableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Configure(bool shouldGenerate, string generatedVariableName)
|
||||||
|
{
|
||||||
|
generateReference = shouldGenerate;
|
||||||
|
variableName = generatedVariableName;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField, HideInInspector]
|
||||||
|
private List<ComponentReference> componentReferences = new List<ComponentReference>();
|
||||||
|
|
||||||
|
public IReadOnlyList<ComponentReference> ComponentReferences => componentReferences;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
private bool refreshQueued;
|
||||||
|
|
||||||
|
public bool RefreshComponents()
|
||||||
|
{
|
||||||
|
Component[] attachedComponents = GetComponents<Component>();
|
||||||
|
var next = new List<ComponentReference>(attachedComponents.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < attachedComponents.Length; i++)
|
||||||
|
{
|
||||||
|
Component attached = attachedComponents[i];
|
||||||
|
if (attached == null || attached == this || attached is UISerializationRoot)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ComponentReference existing = FindReference(attached);
|
||||||
|
next.Add(existing ?? new ComponentReference(attached, CreateDefaultVariableName(gameObject, attached)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasSameComponents(next))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentReferences = next;
|
||||||
|
UnityEditor.EditorUtility.SetDirty(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetReference(Component component, bool shouldGenerate, string variableName)
|
||||||
|
{
|
||||||
|
if (component == null || component.gameObject != gameObject)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The component must be attached to the same GameObject as the serialization item.", nameof(component));
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshComponents();
|
||||||
|
ComponentReference reference = FindReference(component);
|
||||||
|
if (reference == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The component is not available for UI serialization.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string normalizedName = variableName == null ? string.Empty : variableName.Trim();
|
||||||
|
if (reference.GenerateReference == shouldGenerate && reference.VariableName == normalizedName)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference.Configure(shouldGenerate, normalizedName);
|
||||||
|
UnityEditor.EditorUtility.SetDirty(this);
|
||||||
|
NotifyRoot();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CreateDefaultVariableName(GameObject owner, Component component)
|
||||||
|
{
|
||||||
|
string ownerName = ToCamelIdentifier(owner == null ? "uiItem" : owner.name);
|
||||||
|
string typeName = component == null ? "Component" : component.GetType().Name;
|
||||||
|
|
||||||
|
if (component is RectTransform)
|
||||||
|
{
|
||||||
|
typeName = "RectTransform";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ownerName.EndsWith(typeName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return ownerName;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ownerName + typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void QueueRefresh()
|
||||||
|
{
|
||||||
|
if (refreshQueued || Application.isPlaying)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshQueued = true;
|
||||||
|
UnityEditor.EditorApplication.delayCall += RefreshDelayed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
RefreshComponents();
|
||||||
|
NotifyRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
QueueRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
QueueRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshDelayed()
|
||||||
|
{
|
||||||
|
refreshQueued = false;
|
||||||
|
if (this == null || Application.isPlaying)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshComponents();
|
||||||
|
NotifyRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotifyRoot()
|
||||||
|
{
|
||||||
|
UISerializationRoot root = GetComponentInParent<UISerializationRoot>(true);
|
||||||
|
if (root != null)
|
||||||
|
{
|
||||||
|
root.QueueRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ComponentReference FindReference(Component component)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < componentReferences.Count; i++)
|
||||||
|
{
|
||||||
|
ComponentReference reference = componentReferences[i];
|
||||||
|
if (reference != null && reference.Component == component)
|
||||||
|
{
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasSameComponents(List<ComponentReference> next)
|
||||||
|
{
|
||||||
|
if (componentReferences.Count != next.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < next.Count; i++)
|
||||||
|
{
|
||||||
|
if (componentReferences[i] == null || componentReferences[i].Component != next[i].Component)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string ToCamelIdentifier(string value)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder(value.Length + 8);
|
||||||
|
bool capitalizeNext = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < value.Length; i++)
|
||||||
|
{
|
||||||
|
char current = value[i];
|
||||||
|
if (!char.IsLetterOrDigit(current) && current != '_')
|
||||||
|
{
|
||||||
|
capitalizeNext = builder.Length > 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builder.Length == 0)
|
||||||
|
{
|
||||||
|
if (char.IsDigit(current))
|
||||||
|
{
|
||||||
|
builder.Append('_');
|
||||||
|
}
|
||||||
|
builder.Append(char.ToLowerInvariant(current));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append(capitalizeNext ? char.ToUpperInvariant(current) : current);
|
||||||
|
capitalizeNext = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Length == 0 ? "uiItem" : builder.ToString();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fbaa237e23f7d4d3f85f73e0acd7eaec
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,337 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Serialization;
|
||||||
|
|
||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
[ExecuteAlways]
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public sealed class UISerializationRoot : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public sealed class NestedFormReference
|
||||||
|
{
|
||||||
|
[SerializeField] private UISerializationRoot root;
|
||||||
|
[SerializeField] private bool generateReference;
|
||||||
|
[SerializeField] private string variableName;
|
||||||
|
|
||||||
|
public UISerializationRoot Root => root;
|
||||||
|
public bool GenerateReference => generateReference;
|
||||||
|
public string VariableName => variableName;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
internal NestedFormReference(UISerializationRoot nestedRoot, string defaultVariableName)
|
||||||
|
{
|
||||||
|
root = nestedRoot;
|
||||||
|
generateReference = true;
|
||||||
|
variableName = defaultVariableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Configure(bool shouldGenerate, string generatedVariableName)
|
||||||
|
{
|
||||||
|
generateReference = shouldGenerate;
|
||||||
|
variableName = generatedVariableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal bool MigrateLegacyVariableName()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(variableName) ||
|
||||||
|
!variableName.EndsWith("View", StringComparison.Ordinal) ||
|
||||||
|
variableName.Length <= 4)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
variableName = variableName.Substring(0, variableName.Length - 4) + "Form";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[SerializeField, HideInInspector]
|
||||||
|
private List<UISerializationItem> serializationItems = new List<UISerializationItem>();
|
||||||
|
|
||||||
|
[SerializeField, HideInInspector]
|
||||||
|
[FormerlySerializedAs("nestedViewReferences")]
|
||||||
|
private List<NestedFormReference> nestedFormReferences = new List<NestedFormReference>();
|
||||||
|
|
||||||
|
public IReadOnlyList<UISerializationItem> SerializationItems => serializationItems;
|
||||||
|
public IReadOnlyList<NestedFormReference> NestedFormReferences => nestedFormReferences;
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
private bool refreshQueued;
|
||||||
|
|
||||||
|
public bool RefreshItems()
|
||||||
|
{
|
||||||
|
UISerializationItem[] allItems = GetComponentsInChildren<UISerializationItem>(true);
|
||||||
|
var ownedItems = new List<UISerializationItem>(allItems.Length);
|
||||||
|
for (int i = 0; i < allItems.Length; i++)
|
||||||
|
{
|
||||||
|
UISerializationItem item = allItems[i];
|
||||||
|
if (item == null || FindNearestRoot(item.transform) != this)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.RefreshComponents();
|
||||||
|
ownedItems.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
UISerializationRoot[] allRoots = GetComponentsInChildren<UISerializationRoot>(true);
|
||||||
|
var directNestedRoots = new List<UISerializationRoot>(allRoots.Length);
|
||||||
|
for (int i = 0; i < allRoots.Length; i++)
|
||||||
|
{
|
||||||
|
UISerializationRoot candidate = allRoots[i];
|
||||||
|
if (candidate != null && candidate != this && FindNearestParentRoot(candidate.transform) == this)
|
||||||
|
{
|
||||||
|
directNestedRoots.Add(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool itemsChanged = !HasSameItems(ownedItems);
|
||||||
|
bool nestedFormsChanged = !HasSameNestedForms(directNestedRoots);
|
||||||
|
bool nestedNamesChanged = false;
|
||||||
|
for (int i = 0; i < directNestedRoots.Count; i++)
|
||||||
|
{
|
||||||
|
NestedFormReference existing = FindNestedForm(directNestedRoots[i]);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
nestedNamesChanged |= existing.MigrateLegacyVariableName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!itemsChanged && !nestedFormsChanged && !nestedNamesChanged)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itemsChanged)
|
||||||
|
{
|
||||||
|
serializationItems.Clear();
|
||||||
|
serializationItems.AddRange(ownedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nestedFormsChanged)
|
||||||
|
{
|
||||||
|
var next = new List<NestedFormReference>(directNestedRoots.Count);
|
||||||
|
for (int i = 0; i < directNestedRoots.Count; i++)
|
||||||
|
{
|
||||||
|
UISerializationRoot nestedRoot = directNestedRoots[i];
|
||||||
|
NestedFormReference existing = FindNestedForm(nestedRoot);
|
||||||
|
next.Add(existing ?? new NestedFormReference(nestedRoot, CreateDefaultNestedFormVariableName(nestedRoot)));
|
||||||
|
}
|
||||||
|
|
||||||
|
nestedFormReferences = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnityEditor.EditorUtility.SetDirty(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetNestedFormReference(UISerializationRoot nestedRoot, bool shouldGenerate, string variableName)
|
||||||
|
{
|
||||||
|
if (nestedRoot == null || FindNearestParentRoot(nestedRoot.transform) != this)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("The nested root must be directly owned by this serialization root.", nameof(nestedRoot));
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshItems();
|
||||||
|
NestedFormReference reference = FindNestedForm(nestedRoot);
|
||||||
|
if (reference == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The nested root is not registered.");
|
||||||
|
}
|
||||||
|
|
||||||
|
string normalizedName = variableName == null ? string.Empty : variableName.Trim();
|
||||||
|
if (reference.GenerateReference == shouldGenerate && reference.VariableName == normalizedName)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reference.Configure(shouldGenerate, normalizedName);
|
||||||
|
UnityEditor.EditorUtility.SetDirty(this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string CreateDefaultNestedFormVariableName(UISerializationRoot nestedRoot)
|
||||||
|
{
|
||||||
|
string sourceName = nestedRoot == null ? "nested" : nestedRoot.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))
|
||||||
|
{
|
||||||
|
identifier = "Nested";
|
||||||
|
}
|
||||||
|
|
||||||
|
return char.ToLowerInvariant(identifier[0]) + identifier.Substring(1) + "Form";
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void QueueRefresh()
|
||||||
|
{
|
||||||
|
if (refreshQueued || Application.isPlaying)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshQueued = true;
|
||||||
|
UnityEditor.EditorApplication.delayCall += RefreshDelayed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
RefreshItems();
|
||||||
|
NotifyParentRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
QueueRefresh();
|
||||||
|
NotifyParentRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
QueueRefresh();
|
||||||
|
NotifyParentRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTransformChildrenChanged()
|
||||||
|
{
|
||||||
|
QueueRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTransformParentChanged()
|
||||||
|
{
|
||||||
|
QueueRefresh();
|
||||||
|
NotifyParentRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshDelayed()
|
||||||
|
{
|
||||||
|
refreshQueued = false;
|
||||||
|
if (this == null || Application.isPlaying)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasSameItems(List<UISerializationItem> found)
|
||||||
|
{
|
||||||
|
if (serializationItems.Count != found.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < found.Count; i++)
|
||||||
|
{
|
||||||
|
if (serializationItems[i] != found[i])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasSameNestedForms(List<UISerializationRoot> found)
|
||||||
|
{
|
||||||
|
if (nestedFormReferences.Count != found.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < found.Count; i++)
|
||||||
|
{
|
||||||
|
if (nestedFormReferences[i] == null || nestedFormReferences[i].Root != found[i])
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NestedFormReference FindNestedForm(UISerializationRoot nestedRoot)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < nestedFormReferences.Count; i++)
|
||||||
|
{
|
||||||
|
NestedFormReference reference = nestedFormReferences[i];
|
||||||
|
if (reference != null && reference.Root == nestedRoot)
|
||||||
|
{
|
||||||
|
return reference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotifyParentRoot()
|
||||||
|
{
|
||||||
|
UISerializationRoot parentRoot = FindNearestParentRoot(transform);
|
||||||
|
if (parentRoot != null)
|
||||||
|
{
|
||||||
|
parentRoot.QueueRefresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UISerializationRoot FindNearestRoot(Transform target)
|
||||||
|
{
|
||||||
|
Transform current = target;
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
UISerializationRoot root = current.GetComponent<UISerializationRoot>();
|
||||||
|
if (root != null)
|
||||||
|
{
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static UISerializationRoot FindNearestParentRoot(Transform target)
|
||||||
|
{
|
||||||
|
return target == null ? null : FindNearestRoot(target.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 335062b0d81ae40f49c923ef4bf19dd7
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 8e4010de30b33144fa20400058edfeba
|
guid: debba566f996cf14cae850a4a9d461ed
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
namespace SepCore.UI
|
namespace SepCore.UI
|
||||||
{
|
{
|
||||||
public class RolePropertyAreaContext : UIContext
|
public class ShowAreaContext : UIContext
|
||||||
{
|
{
|
||||||
public string RoleName;
|
public string RoleName;
|
||||||
public string InitialPropertyText;
|
public string InitialPropertyText;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
public partial class ShowAreaForm
|
||||||
|
{
|
||||||
|
public void ShowRandomRole()
|
||||||
|
{
|
||||||
|
View.randomRoleCardObject.gameObject.SetActive(true);
|
||||||
|
View.roleCardObject.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ShowRoleProperty(ShowAreaContext context)
|
||||||
|
{
|
||||||
|
View.roleNameText.text = context.RoleName;
|
||||||
|
View.initialPropertyText.text = context.InitialPropertyText;
|
||||||
|
|
||||||
|
View.randomRoleCardObject.gameObject.SetActive(false);
|
||||||
|
View.roleCardObject.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 342797c2a9cf0364aaf7fe4688e3f460
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public partial class ShowAreaForm : UGuiForm
|
||||||
|
{
|
||||||
|
[SerializeField] private ShowAreaView _view = null;
|
||||||
|
|
||||||
|
public ShowAreaView View => _view;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f77da2a257f3eb44fbb67351780d84df
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace SepCore.UI
|
||||||
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public partial class ShowAreaView : MonoBehaviour
|
||||||
|
{
|
||||||
|
[SerializeField] public global::UnityEngine.RectTransform randomRoleCardObject;
|
||||||
|
[SerializeField] public global::UnityEngine.RectTransform roleCardObject;
|
||||||
|
[SerializeField] public global::TMPro.TextMeshProUGUI roleNameText;
|
||||||
|
[SerializeField] public global::UnityEngine.UI.RawImage roleAvatarRawImage;
|
||||||
|
[SerializeField] public global::TMPro.TextMeshProUGUI initialPropertyText;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 65d79ca5fd70cef4abd5169b1621200b
|
||||||
|
MonoImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
defaultReferences: []
|
||||||
|
executionOrder: 0
|
||||||
|
icon: {instanceID: 0}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -79,5 +79,25 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
_onPointerExitAction.Invoke();
|
_onPointerExitAction.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RegisterPointerEnterAction(UnityAction action)
|
||||||
|
{
|
||||||
|
_onPointerEnterAction.AddListener(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterPointerExitAction(UnityAction action)
|
||||||
|
{
|
||||||
|
_onPointerExitAction.AddListener(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnregisterPointerEnterAction(UnityAction action)
|
||||||
|
{
|
||||||
|
_onPointerEnterAction.RemoveListener(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnregisterPointerExitAction(UnityAction action)
|
||||||
|
{
|
||||||
|
_onPointerExitAction.RemoveListener(action);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
public class LevelUpUseCase : IUIUseCase
|
public class LevelUpUseCase : IUIUseCase
|
||||||
{
|
{
|
||||||
private const int BaseRefreshPrice = 2;
|
|
||||||
private readonly Action _onCompleted;
|
private readonly Action _onCompleted;
|
||||||
private readonly LevelUpRewardConfig[] _allRewards;
|
private readonly LevelUpRewardConfig[] _allRewards;
|
||||||
private readonly Dictionary<Rarity, List<LevelUpRewardConfig>> _rewardsByRarity = new();
|
private readonly Dictionary<Rarity, List<LevelUpRewardConfig>> _rewardsByRarity = new();
|
||||||
@@ -32,28 +31,28 @@ namespace SepCore.UI
|
|||||||
BuildRewardRarityPools();
|
BuildRewardRarityPools();
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelUpRawData CreateInitialModel(int count = 4)
|
public LevelUpRawData CreateInitialModel()
|
||||||
{
|
{
|
||||||
_refreshCount = 0;
|
_refreshCount = 0;
|
||||||
|
|
||||||
if (_player == null || _player.PendingLevelPoints <= 0)
|
if (_player.PendingLevelPoints <= 0)
|
||||||
{
|
{
|
||||||
_currentModel = null;
|
_currentModel = null;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentModel = BuildModel(BaseRefreshPrice, count);
|
_currentModel = BuildModel(GameEntry.Luban.Global.LevelUpRefreshPriceBase);
|
||||||
return _currentModel;
|
return _currentModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelUpRawData TryRefresh(int refreshCost, int count = 4)
|
public LevelUpRawData TryRefresh(int refreshCost)
|
||||||
{
|
{
|
||||||
if (_currentModel == null)
|
if (_currentModel == null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_player == null || _player.Coin < refreshCost)
|
if (_player.Coin < refreshCost)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -61,11 +60,11 @@ namespace SepCore.UI
|
|||||||
_player.Coin -= refreshCost;
|
_player.Coin -= refreshCost;
|
||||||
|
|
||||||
_refreshCount++;
|
_refreshCount++;
|
||||||
_currentModel = BuildModel((_refreshCount + 1) * BaseRefreshPrice, count);
|
_currentModel = BuildModel((_refreshCount + 1) * GameEntry.Luban.Global.LevelUpRefreshPriceBase);
|
||||||
return _currentModel;
|
return _currentModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LevelUpRawData SelectReward(int selectedIndex, int count = 4)
|
public LevelUpRawData SelectReward(int selectedIndex)
|
||||||
{
|
{
|
||||||
if (_currentModel?.Rewards == null)
|
if (_currentModel?.Rewards == null)
|
||||||
{
|
{
|
||||||
@@ -74,7 +73,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
if (selectedIndex < 0 || selectedIndex >= _currentModel.Rewards.Count)
|
if (selectedIndex < 0 || selectedIndex >= _currentModel.Rewards.Count)
|
||||||
{
|
{
|
||||||
Log.Warning("LevelUpFormUseCase::SelectReward: Invalid index");
|
Log.Error("LevelUpFormUseCase::SelectReward: Invalid index");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +82,7 @@ namespace SepCore.UI
|
|||||||
if (--_player.PendingLevelPoints > 0)
|
if (--_player.PendingLevelPoints > 0)
|
||||||
{
|
{
|
||||||
_refreshCount = 0;
|
_refreshCount = 0;
|
||||||
_currentModel = BuildModel(BaseRefreshPrice, count);
|
_currentModel = BuildModel(GameEntry.Luban.Global.LevelUpRefreshPriceBase);
|
||||||
return _currentModel;
|
return _currentModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,25 +91,25 @@ namespace SepCore.UI
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LevelUpRawData BuildModel(int refreshPrice, int count)
|
private LevelUpRawData BuildModel(int refreshPrice)
|
||||||
{
|
{
|
||||||
if (_allRewards == null || _allRewards.Length == 0)
|
if (_allRewards.Length == 0)
|
||||||
{
|
{
|
||||||
Log.Error("LevelUpFormUseCase::BuildModel(): _allRewards is empty");
|
Log.Error("LevelUpFormUseCase::BuildModel(): _allRewards is empty");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
int finalCount = count > 0 ? Mathf.Min(count, _allRewards.Length) : _allRewards.Length;
|
int finalCount = Mathf.Min(GameEntry.Luban.Global.LevelUpRewardCount, _allRewards.Length);
|
||||||
List<LevelUpRewardConfig> selections = new List<LevelUpRewardConfig>(finalCount);
|
List<LevelUpRewardConfig> selections = new List<LevelUpRewardConfig>(finalCount);
|
||||||
|
|
||||||
int currentLevel = _player != null ? _player.CurrentLevel : 1;
|
int currentLevel = _player.CurrentLevel;
|
||||||
for (int i = 0; i < finalCount; i++)
|
for (int i = 0; i < finalCount; i++)
|
||||||
{
|
{
|
||||||
Rarity rarity = RarityUtility.SelectRarityForLevel(currentLevel);
|
Rarity rarity = RarityUtility.SelectRarityForLevel(currentLevel);
|
||||||
LevelUpRewardConfig reward = PickRewardByRarity(rarity);
|
LevelUpRewardConfig reward = PickRewardByRarity(rarity);
|
||||||
if (reward == null)
|
if (reward == null)
|
||||||
{
|
{
|
||||||
Log.Warning("LevelUpFormUseCase::BuildModel(): No available reward for selection.");
|
Log.Error("LevelUpFormUseCase::BuildModel(): No available reward for selection.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,23 +125,6 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private void ApplyReward(LevelUpRewardConfig reward)
|
private void ApplyReward(LevelUpRewardConfig reward)
|
||||||
{
|
{
|
||||||
if (reward == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_player == null)
|
|
||||||
{
|
|
||||||
Log.Warning("LevelUpFormUseCase::ApplyReward: Player is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reward.Modifiers == null || reward.Modifiers.Count == 0)
|
|
||||||
{
|
|
||||||
Log.Warning("LevelUpFormUseCase::ApplyReward: Reward modifiers are empty");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_player.AddProp(new PropItem(reward.Modifiers, reward.Rarity, reward.Title, reward.Sprite));
|
_player.AddProp(new PropItem(reward.Modifiers, reward.Rarity, reward.Title, reward.Sprite));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,15 +133,8 @@ namespace SepCore.UI
|
|||||||
_rewardsByRarity.Clear();
|
_rewardsByRarity.Clear();
|
||||||
_validRewards.Clear();
|
_validRewards.Clear();
|
||||||
|
|
||||||
if (_allRewards == null) return;
|
|
||||||
|
|
||||||
foreach (LevelUpRewardConfig reward in _allRewards)
|
foreach (LevelUpRewardConfig reward in _allRewards)
|
||||||
{
|
{
|
||||||
if (reward == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rarity rarity = reward.Rarity;
|
Rarity rarity = reward.Rarity;
|
||||||
_validRewards.Add(reward);
|
_validRewards.Add(reward);
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
public class ShopUseCase : IUIUseCase
|
public class ShopUseCase : IUIUseCase
|
||||||
{
|
{
|
||||||
private const float WeaponRecycleRate = 0.3f;
|
|
||||||
|
|
||||||
private readonly Player _player;
|
private readonly Player _player;
|
||||||
private readonly int _currentLevel;
|
private readonly int _currentLevel;
|
||||||
private readonly Action _onShopFinish;
|
private readonly Action _onShopFinish;
|
||||||
@@ -39,8 +37,6 @@ namespace SepCore.UI
|
|||||||
private readonly List<ShopGoodsSelection> _selections = new();
|
private readonly List<ShopGoodsSelection> _selections = new();
|
||||||
private int _refreshTime;
|
private int _refreshTime;
|
||||||
|
|
||||||
private Player Player => _player;
|
|
||||||
|
|
||||||
private sealed class ShopGoodsSelection
|
private sealed class ShopGoodsSelection
|
||||||
{
|
{
|
||||||
public GoodsType GoodsType;
|
public GoodsType GoodsType;
|
||||||
@@ -63,42 +59,34 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
_refreshTime = 0;
|
_refreshTime = 0;
|
||||||
|
|
||||||
Player player = Player;
|
|
||||||
if (_player == null || player == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int currentLevel = _currentLevel;
|
|
||||||
return new ShopRawData
|
return new ShopRawData
|
||||||
{
|
{
|
||||||
CurrentLevel = currentLevel,
|
CurrentLevel = _currentLevel,
|
||||||
RefreshPrice = currentLevel,
|
RefreshPrice = _currentLevel,
|
||||||
PlayerCoin = player.Coin,
|
PlayerCoin = _player.Coin,
|
||||||
GoodsItems = BuildRandomGoodsItems(4),
|
GoodsItems = BuildRandomGoodsItems(GameEntry.Luban.Global.ShopGoodsCount),
|
||||||
PropItems = player.Props,
|
PropItems = _player.Props,
|
||||||
PropMaxCount = -1,
|
PropMaxCount = GameEntry.Luban.Global.PropMaxCount,
|
||||||
WeaponItems = player.Weapons,
|
WeaponItems = _player.Weapons,
|
||||||
WeaponMaxCount = player.WeaponCapacity
|
WeaponMaxCount = _player.WeaponCapacity
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShopRefreshResult TryRefresh(int cost)
|
public ShopRefreshResult TryRefresh(int cost)
|
||||||
{
|
{
|
||||||
Player player = Player;
|
if (_player.Coin < cost)
|
||||||
if (player == null || _player == null || player.Coin < cost)
|
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
player.Coin -= cost;
|
_player.Coin -= cost;
|
||||||
|
|
||||||
int refreshPrice = (_refreshTime + 1) * _currentLevel;
|
int refreshPrice = (_refreshTime + 1) * _currentLevel;
|
||||||
_refreshTime++;
|
_refreshTime++;
|
||||||
|
|
||||||
return new ShopRefreshResult
|
return new ShopRefreshResult
|
||||||
{
|
{
|
||||||
GoodsItems = BuildRandomGoodsItems(4),
|
GoodsItems = BuildRandomGoodsItems(GameEntry.Luban.Global.ShopGoodsCount),
|
||||||
RefreshPrice = refreshPrice
|
RefreshPrice = refreshPrice
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -107,7 +95,7 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
if (goodsIndex < 0 || goodsIndex >= _selections.Count)
|
if (goodsIndex < 0 || goodsIndex >= _selections.Count)
|
||||||
{
|
{
|
||||||
Log.Warning("ShopFormUseCase::TryPurchase: Invalid index");
|
Log.Error("ShopFormUseCase::TryPurchase: Invalid index");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +106,7 @@ namespace SepCore.UI
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player player = Player;
|
if (_player.Coin < selection.Price)
|
||||||
if (player == null || player.Coin < selection.Price)
|
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -130,7 +117,7 @@ namespace SepCore.UI
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
player.Coin -= selection.Price;
|
_player.Coin -= selection.Price;
|
||||||
_selections[goodsIndex] = null;
|
_selections[goodsIndex] = null;
|
||||||
|
|
||||||
return new ShopPurchaseResult
|
return new ShopPurchaseResult
|
||||||
@@ -147,7 +134,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private List<GoodsItemRawData> BuildRandomGoodsItems(int count)
|
private List<GoodsItemRawData> BuildRandomGoodsItems(int count)
|
||||||
{
|
{
|
||||||
if (_allGoods == null || _allGoods.Count == 0)
|
if (_allGoods.Count == 0)
|
||||||
{
|
{
|
||||||
Log.Error("ShopFormUseCase::BuildRandomGoodsItems(): _allGoods is empty");
|
Log.Error("ShopFormUseCase::BuildRandomGoodsItems(): _allGoods is empty");
|
||||||
return null;
|
return null;
|
||||||
@@ -157,7 +144,8 @@ namespace SepCore.UI
|
|||||||
List<GoodsItemRawData> goodsItems = new List<GoodsItemRawData>(finalCount);
|
List<GoodsItemRawData> goodsItems = new List<GoodsItemRawData>(finalCount);
|
||||||
_selections.Clear();
|
_selections.Clear();
|
||||||
|
|
||||||
int currentLevel = _currentLevel > 0 ? _currentLevel : 1;
|
int currentLevel = _currentLevel;
|
||||||
|
float priceRandomPercent = GameEntry.Luban.Global.ShopPriceRandomPercent / 1000f;
|
||||||
|
|
||||||
for (int i = 0; i < finalCount; i++)
|
for (int i = 0; i < finalCount; i++)
|
||||||
{
|
{
|
||||||
@@ -165,7 +153,7 @@ namespace SepCore.UI
|
|||||||
GoodsConfig goodsConfig = PickGoodsByRarity(rarity);
|
GoodsConfig goodsConfig = PickGoodsByRarity(rarity);
|
||||||
if (goodsConfig == null)
|
if (goodsConfig == null)
|
||||||
{
|
{
|
||||||
Log.Warning("ShopFormUseCase::BuildRandomGoodsItems: No available goods for selection.");
|
Log.Error("ShopFormUseCase::BuildRandomGoodsItems: No available goods for selection.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +164,8 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
PropConfig propConfig = GameEntry.Luban.Get<PropConfig>(goodsConfig.PropId);
|
PropConfig propConfig = GameEntry.Luban.Get<PropConfig>(goodsConfig.PropId);
|
||||||
int basePrice = propConfig.Price;
|
int basePrice = propConfig.Price;
|
||||||
float randomPercent = 0.05f;
|
|
||||||
|
|
||||||
price = CalculateRandomizedPrice(basePrice, randomPercent);
|
price = CalculateRandomizedPrice(basePrice, priceRandomPercent);
|
||||||
goodsItem.ItemType = ItemType.Prop;
|
goodsItem.ItemType = ItemType.Prop;
|
||||||
goodsItem.Rarity = propConfig.Rarity;
|
goodsItem.Rarity = propConfig.Rarity;
|
||||||
goodsItem.DataRow = propConfig;
|
goodsItem.DataRow = propConfig;
|
||||||
@@ -187,9 +174,8 @@ namespace SepCore.UI
|
|||||||
{
|
{
|
||||||
WeaponConfig weaponConfig = GameEntry.Luban.Get<WeaponConfig>((int)goodsConfig.WeaponType);
|
WeaponConfig weaponConfig = GameEntry.Luban.Get<WeaponConfig>((int)goodsConfig.WeaponType);
|
||||||
int basePrice = weaponConfig.Price;
|
int basePrice = weaponConfig.Price;
|
||||||
float randomPercent = 0.05f;
|
|
||||||
|
|
||||||
price = CalculateRandomizedPrice(basePrice, randomPercent);
|
price = CalculateRandomizedPrice(basePrice, priceRandomPercent);
|
||||||
goodsItem.ItemType = ItemType.Weapon;
|
goodsItem.ItemType = ItemType.Weapon;
|
||||||
goodsItem.Rarity = weaponConfig.Rarity;
|
goodsItem.Rarity = weaponConfig.Rarity;
|
||||||
goodsItem.DataRow = weaponConfig;
|
goodsItem.DataRow = weaponConfig;
|
||||||
@@ -222,12 +208,8 @@ namespace SepCore.UI
|
|||||||
_goodsByRarity.Clear();
|
_goodsByRarity.Clear();
|
||||||
_validGoods.Clear();
|
_validGoods.Clear();
|
||||||
|
|
||||||
if (_allGoods == null) return;
|
|
||||||
|
|
||||||
foreach (GoodsConfig goods in _allGoods)
|
foreach (GoodsConfig goods in _allGoods)
|
||||||
{
|
{
|
||||||
if (goods == null) continue;
|
|
||||||
|
|
||||||
if (!TryGetGoodsRarity(goods, out Rarity rarity))
|
if (!TryGetGoodsRarity(goods, out Rarity rarity))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -263,10 +245,6 @@ namespace SepCore.UI
|
|||||||
private bool TryGetGoodsRarity(GoodsConfig goods, out Rarity rarity)
|
private bool TryGetGoodsRarity(GoodsConfig goods, out Rarity rarity)
|
||||||
{
|
{
|
||||||
rarity = Rarity.None;
|
rarity = Rarity.None;
|
||||||
if (goods == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (goods.GoodsType == GoodsType.Prop)
|
if (goods.GoodsType == GoodsType.Prop)
|
||||||
{
|
{
|
||||||
@@ -293,30 +271,17 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
private static int CalculateRandomizedPrice(int basePrice, float priceRandomPercent)
|
private static int CalculateRandomizedPrice(int basePrice, float priceRandomPercent)
|
||||||
{
|
{
|
||||||
float normalizedPercent = priceRandomPercent > 1f ? priceRandomPercent / 100f : priceRandomPercent;
|
float offsetPercent = Random.Range(-priceRandomPercent, priceRandomPercent);
|
||||||
normalizedPercent = Mathf.Max(0f, normalizedPercent);
|
|
||||||
float offsetPercent = Random.Range(-normalizedPercent, normalizedPercent);
|
|
||||||
int finalPrice = Mathf.RoundToInt(basePrice * (1f + offsetPercent));
|
int finalPrice = Mathf.RoundToInt(basePrice * (1f + offsetPercent));
|
||||||
return Mathf.Max(1, finalPrice);
|
return Mathf.Max(GameEntry.Luban.Global.ShopMinPrice, finalPrice);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async UniTask<DisplayItemRawData> ApplyGoodsPurchaseAsync(ShopGoodsSelection goods)
|
private async UniTask<DisplayItemRawData> ApplyGoodsPurchaseAsync(ShopGoodsSelection goods)
|
||||||
{
|
{
|
||||||
if (goods == null || Player == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (goods.GoodsType == GoodsType.Prop)
|
if (goods.GoodsType == GoodsType.Prop)
|
||||||
{
|
{
|
||||||
PropConfig propConfig = GameEntry.Luban.Get<PropConfig>(goods.PropId);
|
PropConfig propConfig = GameEntry.Luban.Get<PropConfig>(goods.PropId);
|
||||||
if (propConfig.Modifiers == null || propConfig.Modifiers.Count == 0)
|
_player.AddProp(new PropItem(propConfig));
|
||||||
{
|
|
||||||
Log.Warning("ShopFormUseCase::ApplyGoodsPurchase: Prop modifiers are empty");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Player.AddProp(new PropItem(propConfig));
|
|
||||||
return new DisplayItemRawData
|
return new DisplayItemRawData
|
||||||
{
|
{
|
||||||
IconAssetName = propConfig.Sprite,
|
IconAssetName = propConfig.Sprite,
|
||||||
@@ -327,7 +292,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
if (goods.GoodsType == GoodsType.Weapon)
|
if (goods.GoodsType == GoodsType.Weapon)
|
||||||
{
|
{
|
||||||
if (Player.Weapons != null && Player.Weapons.Count >= Player.WeaponCapacity)
|
if (_player.Weapons.Count >= _player.WeaponCapacity)
|
||||||
{
|
{
|
||||||
Log.Warning("ShopFormUseCase::ApplyGoodsPurchase: Weapon capacity is full.");
|
Log.Warning("ShopFormUseCase::ApplyGoodsPurchase: Weapon capacity is full.");
|
||||||
return null;
|
return null;
|
||||||
@@ -335,9 +300,9 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
WeaponConfig weaponConfig = GameEntry.Luban.Get<WeaponConfig>((int)goods.WeaponType);
|
WeaponConfig weaponConfig = GameEntry.Luban.Get<WeaponConfig>((int)goods.WeaponType);
|
||||||
int id = GameEntry.Entity.NextId();
|
int id = GameEntry.Entity.NextId();
|
||||||
var weaponData = EntityDataFactory.Create(id, weaponConfig, Player.Id, CampType.Player);
|
var weaponData = EntityDataFactory.Create(id, weaponConfig, _player.Id, CampType.Player);
|
||||||
|
|
||||||
WeaponBase weapon = await Player.AddWeaponAsync(weaponData);
|
WeaponBase weapon = await _player.AddWeaponAsync(weaponData);
|
||||||
if (weapon == null)
|
if (weapon == null)
|
||||||
{
|
{
|
||||||
Log.Warning(
|
Log.Warning(
|
||||||
@@ -358,7 +323,7 @@ namespace SepCore.UI
|
|||||||
|
|
||||||
public bool TryRecycleWeapon(int argsIndex, int argsPrice)
|
public bool TryRecycleWeapon(int argsIndex, int argsPrice)
|
||||||
{
|
{
|
||||||
var weapons = Player.Weapons;
|
var weapons = _player.Weapons;
|
||||||
if (weapons.Count <= 1)
|
if (weapons.Count <= 1)
|
||||||
{
|
{
|
||||||
Log.Warning("ShopFormUseCase::TryRecycleWeapon: Can not recycle the last weapon.");
|
Log.Warning("ShopFormUseCase::TryRecycleWeapon: Can not recycle the last weapon.");
|
||||||
@@ -368,19 +333,19 @@ namespace SepCore.UI
|
|||||||
var weapon = weapons[argsIndex];
|
var weapon = weapons[argsIndex];
|
||||||
|
|
||||||
int recyclePrice = Mathf.Max(0, argsPrice);
|
int recyclePrice = Mathf.Max(0, argsPrice);
|
||||||
if (!Player.RemoveWeapon(weapon))
|
if (!_player.RemoveWeapon(weapon))
|
||||||
{
|
{
|
||||||
Log.Warning("ShopFormUseCase::TryRecycleWeapon: Remove weapon failed.");
|
Log.Warning("ShopFormUseCase::TryRecycleWeapon: Remove weapon failed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Player.Coin += recyclePrice;
|
_player.Coin += recyclePrice;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CalculateRecyclePrice(int basePrice)
|
public int CalculateRecyclePrice(int basePrice)
|
||||||
{
|
{
|
||||||
return Mathf.FloorToInt(basePrice * WeaponRecycleRate);
|
return Mathf.FloorToInt(basePrice * (GameEntry.Luban.Global.WeaponRecycleRate / 1000f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -3,6 +3,6 @@ namespace SepCore.UI
|
|||||||
public class SelectRoleRawData
|
public class SelectRoleRawData
|
||||||
{
|
{
|
||||||
public RoleItemRawData[] RoleItems;
|
public RoleItemRawData[] RoleItems;
|
||||||
public RolePropertyAreaRawData SelectedRole;
|
public ShowAreaRawData SelectedRole;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+14
-6
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using SepCore.Definition;
|
using SepCore.Definition;
|
||||||
using SepCore.Procedure;
|
using SepCore.Procedure;
|
||||||
|
using UnityGameFramework.Runtime;
|
||||||
using Random = UnityEngine.Random;
|
using Random = UnityEngine.Random;
|
||||||
|
|
||||||
namespace SepCore.UI
|
namespace SepCore.UI
|
||||||
@@ -62,16 +63,23 @@ namespace SepCore.UI
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
RoleConfig selectedRole = string.IsNullOrEmpty(selectedRoleName)
|
ShowAreaRawData selectedRoleData = null;
|
||||||
? null
|
if (!string.IsNullOrEmpty(selectedRoleName))
|
||||||
: roles.FirstOrDefault(r => r.Name == selectedRoleName);
|
{
|
||||||
RolePropertyAreaRawData selectedRoleData = selectedRole == null
|
RoleConfig selectedRole = roles.FirstOrDefault(r => r.Name == selectedRoleName);
|
||||||
? null
|
if (selectedRole == null)
|
||||||
: new RolePropertyAreaRawData
|
{
|
||||||
|
Log.Error("SelectRoleUseCase.BuildModel() role '{0}' is not found.", selectedRoleName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
selectedRoleData = new ShowAreaRawData
|
||||||
{
|
{
|
||||||
Name = selectedRole.Name,
|
Name = selectedRole.Name,
|
||||||
InitialProperties = selectedRole.Modifiers
|
InitialProperties = selectedRole.Modifiers
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new SelectRoleRawData
|
return new SelectRoleRawData
|
||||||
{
|
{
|
||||||
+1
-1
@@ -3,7 +3,7 @@ using SepCore.Definition;
|
|||||||
|
|
||||||
namespace SepCore.UI
|
namespace SepCore.UI
|
||||||
{
|
{
|
||||||
public class RolePropertyAreaRawData
|
public class ShowAreaRawData
|
||||||
{
|
{
|
||||||
public string Name;
|
public string Name;
|
||||||
public List<modifier> InitialProperties;
|
public List<modifier> InitialProperties;
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -695,7 +695,7 @@ RectTransform:
|
|||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
m_AnchorMin: {x: 0, y: 1}
|
m_AnchorMin: {x: 0, y: 1}
|
||||||
m_AnchorMax: {x: 1, y: 1}
|
m_AnchorMax: {x: 1, y: 1}
|
||||||
m_AnchoredPosition: {x: 2, y: 0.000006247098}
|
m_AnchoredPosition: {x: 2, y: 0.000906718}
|
||||||
m_SizeDelta: {x: -2, y: 300}
|
m_SizeDelta: {x: -2, y: 300}
|
||||||
m_Pivot: {x: 0, y: 1}
|
m_Pivot: {x: 0, y: 1}
|
||||||
--- !u!1 &3529895034686484875
|
--- !u!1 &3529895034686484875
|
||||||
@@ -1473,7 +1473,7 @@ MonoBehaviour:
|
|||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier:
|
m_EditorClassIdentifier:
|
||||||
_titleText: {fileID: 3147210475793208583}
|
_titleText: {fileID: 3147210475793208583}
|
||||||
_countText: {fileID: 0}
|
_countText: {fileID: 8582964069843304838}
|
||||||
_displayItemTemplate: {fileID: 8108003253794388876, guid: a76ff56683a2bc2479ebd63dcdd658b0,
|
_displayItemTemplate: {fileID: 8108003253794388876, guid: a76ff56683a2bc2479ebd63dcdd658b0,
|
||||||
type: 3}
|
type: 3}
|
||||||
_displayItemRoot: {fileID: 4930859207330783680}
|
_displayItemRoot: {fileID: 4930859207330783680}
|
||||||
@@ -1678,7 +1678,7 @@ MonoBehaviour:
|
|||||||
m_HandleRect: {fileID: 8982130026310361329}
|
m_HandleRect: {fileID: 8982130026310361329}
|
||||||
m_Direction: 2
|
m_Direction: 2
|
||||||
m_Value: 0
|
m_Value: 0
|
||||||
m_Size: 0.999986
|
m_Size: 0.96765774
|
||||||
m_NumberOfSteps: 0
|
m_NumberOfSteps: 0
|
||||||
m_OnValueChanged:
|
m_OnValueChanged:
|
||||||
m_PersistentCalls:
|
m_PersistentCalls:
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 787aba226302d5749bebd1cf8cd1fb2a
|
||||||
|
PrefabImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -185,7 +185,7 @@ RectTransform:
|
|||||||
m_AnchorMin: {x: 0, y: 0}
|
m_AnchorMin: {x: 0, y: 0}
|
||||||
m_AnchorMax: {x: 1, y: 1}
|
m_AnchorMax: {x: 1, y: 1}
|
||||||
m_AnchoredPosition: {x: 0, y: 0}
|
m_AnchoredPosition: {x: 0, y: 0}
|
||||||
m_SizeDelta: {x: 0, y: 0}
|
m_SizeDelta: {x: -10, y: -10}
|
||||||
m_Pivot: {x: 0.5, y: 0.5}
|
m_Pivot: {x: 0.5, y: 0.5}
|
||||||
--- !u!222 &1956022961265664776
|
--- !u!222 &1956022961265664776
|
||||||
CanvasRenderer:
|
CanvasRenderer:
|
||||||
|
|||||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user