修改 增加UIController

This commit is contained in:
陈思海 2025-12-01 16:44:19 +08:00
parent 94f9112465
commit dfef3f5199
23 changed files with 939 additions and 5 deletions

View File

@ -0,0 +1,51 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public static class SerializedClassDrawer
{
public static void DrawSerializableProperty(SerializedProperty prop, string title = null)
{
if (prop == null) return;
EditorGUILayout.BeginHorizontal();
EditorGUILayout.EndHorizontal();
if (!prop.hasVisibleChildren || IsEmptyClass(prop))
{
EditorGUILayout.PropertyField(prop, true);
}
else
{
SerializedProperty iterator = prop.Copy();
SerializedProperty end = iterator.GetEndProperty();
if (iterator.NextVisible(true))
{
while (!SerializedProperty.EqualContents(iterator, end))
{
if (iterator.name == "m_Script")
{
if (!iterator.NextVisible(false)) break;
else continue;
}
GUIContent label = new GUIContent(ObjectNames.NicifyVariableName(iterator.displayName));
EditorGUILayout.PropertyField(iterator, label, true);
if (!iterator.NextVisible(false)) break;
}
}
}
}
static bool IsEmptyClass(SerializedProperty prop)
{
SerializedProperty it = prop.Copy();
if (!it.NextVisible(true)) return true;
SerializedProperty end = prop.GetEndProperty();
return SerializedProperty.EqualContents(it, end);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 04d2930185f64726b690d03a849dc699
timeCreated: 1764138440

View File

@ -3,12 +3,14 @@ using System.Reflection;
using TMPro; using TMPro;
using TMPro.EditorUtilities; using TMPro.EditorUtilities;
using UnityEditor; using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEditor.UI; using UnityEditor.UI;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using Random = UnityEngine.Random;
internal class UXUIEditor : Editor public class UXCreateHelper : Editor
{ {
static object InvokeMethod(Type type, string methodName, object[] parameters = null) static object InvokeMethod(Type type, string methodName, object[] parameters = null)
{ {
@ -41,7 +43,7 @@ internal class UXUIEditor : Editor
#if TEXTMESHPRO_SUPPORT #if TEXTMESHPRO_SUPPORT
[MenuItem("GameObject/UI/UXTextMeshPro")] [MenuItem("GameObject/UI/UXTextMeshPro")]
private static void CreateUXTextMeshPro(MenuCommand menuCommand) public static void CreateUXTextMeshPro(MenuCommand menuCommand)
{ {
Type MenuOptionsType = typeof(ImageEditor).Assembly.GetType("UnityEditor.UI.MenuOptions"); Type MenuOptionsType = typeof(ImageEditor).Assembly.GetType("UnityEditor.UI.MenuOptions");
InvokeMethod(MenuOptionsType, "AddText", new object[] { menuCommand }); InvokeMethod(MenuOptionsType, "AddText", new object[] { menuCommand });
@ -85,14 +87,15 @@ internal class UXUIEditor : Editor
#endif #endif
[MenuItem("GameObject/UI/UXScrollView")] [MenuItem("GameObject/UI/UXScrollView")]
private static void CreateUxRecyclerView() public static void CreateUxRecyclerView()
{ {
GameObject selectionObject = Selection.activeGameObject; GameObject selectionObject = Selection.activeGameObject;
if (selectionObject == null) return; Transform parent = selectionObject != null ? selectionObject.transform : PrefabStageUtility.GetCurrentPrefabStage().prefabContentsRoot.transform;
const string prefabPath = "Packages/com.alicizax.unity.ui.extension/Editor/RecyclerView/Res/ScrollView.prefab"; const string prefabPath = "Packages/com.alicizax.unity.ui.extension/Editor/RecyclerView/Res/ScrollView.prefab";
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath); GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab, selectionObject.transform); GameObject instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab, parent);
PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.Completely, InteractionMode.UserAction); PrefabUtility.UnpackPrefabInstance(instance, PrefabUnpackMode.Completely, InteractionMode.UserAction);
instance.name = prefab.name + Random.Range(1, 1000);
Selection.activeGameObject = instance; Selection.activeGameObject = instance;
} }

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 03f1bde8358542e897147218725d1776
timeCreated: 1764127120

View File

@ -0,0 +1,86 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using AlicizaX.UI;
using AlicizaX.UI.Runtime;
public class StateTypeSearchProvider : ScriptableObject, ISearchWindowProvider
{
private Action<Type> _onSelect;
private List<SearchTreeEntry> _entries;
public void Init(Action<Type> onSelect)
{
_onSelect = onSelect;
BuildEntries();
}
private void BuildEntries()
{
_entries = new List<SearchTreeEntry>();
_entries.Add(new SearchTreeGroupEntry(new GUIContent("States")));
var all = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a =>
{
try { return a.GetTypes(); }
catch { return new Type[0]; }
})
.Where(t => typeof(ControllerStateBase).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
.Select(t =>
{
var attr = t.GetCustomAttribute<ControlerStateNameAttribute>();
var tag = attr != null ? attr.StateName : t.Name;
return (type: t, tag);
})
.OrderBy(x => x.tag)
.ToArray();
var groups = new Dictionary<string, List<(Type type, string fullTag, string display)>>();
foreach (var item in all)
{
string[] parts = item.tag.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
string g = parts.Length > 0 ? parts[0] : "Other";
string child = parts.Length > 1 ? string.Join("/", parts.Skip(1)) : (parts.Length == 1 ? parts[0] : item.type.Name);
string display = $"{child}";
if (!groups.TryGetValue(g, out var list)) { list = new List<(Type, string, string)>(); groups[g] = list; }
list.Add((item.type, item.tag, display));
}
foreach (var kv in groups.OrderBy(k => k.Key))
{
_entries.Add(new SearchTreeGroupEntry(new GUIContent(kv.Key)) { level = 1 });
foreach (var child in kv.Value.OrderBy(c => c.fullTag))
{
var entry = new SearchTreeEntry(new GUIContent(child.display)) { level = 2, userData = child.type };
_entries.Add(entry);
}
}
}
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
{
if (_entries == null) BuildEntries();
return _entries;
}
public bool OnSelectEntry(SearchTreeEntry entry, SearchWindowContext context)
{
var t = entry.userData as Type;
if (t != null)
{
_onSelect?.Invoke(t);
return true;
}
return false;
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 37183e7bcfe8411fba14fb9eb6bdfe9d
timeCreated: 1764136414

View File

@ -0,0 +1,73 @@
using UnityEditor;
using UnityEngine;
using AlicizaX.UI;
using AlicizaX.UI.Runtime;
[CustomEditor(typeof(UXController))]
public class UXControllerEditor : Editor
{
private SerializedProperty _controllersProp;
private void OnEnable()
{
_controllersProp = serializedObject.FindProperty("_controllers");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.LabelField("UX Controller", EditorStyles.boldLabel);
EditorGUILayout.Space();
EditorGUI.BeginDisabledGroup(true);
for (int i = 0; i < _controllersProp.arraySize; i++)
{
var el = _controllersProp.GetArrayElementAtIndex(i);
var nameProp = el.FindPropertyRelative("Name");
var lengthProp = el.FindPropertyRelative("Length");
EditorGUILayout.BeginVertical("box");
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField($"[{i}]", GUILayout.Width(30));
nameProp.stringValue = EditorGUILayout.TextField(nameProp.stringValue);
if (GUILayout.Button("Remove", GUILayout.Width(70)))
{
_controllersProp.DeleteArrayElementAtIndex(i);
break;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("Length", GUILayout.Width(50));
lengthProp.intValue = Mathf.Max(1, EditorGUILayout.IntField(lengthProp.intValue));
EditorGUILayout.EndHorizontal();
var ux = target as UXController;
if (ux != null && ux.Controllers != null && i < ux.Controllers.Count)
{
EditorGUILayout.LabelField("CurrentIndex", ux.Controllers[i].CurrentIndex.ToString());
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
}
var controller = target as UXController;
using (new EditorGUILayout.VerticalScope("Box"))
{
foreach (var recorder in controller.Recorders)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField($"ID:{recorder.ID}", EditorStyles.helpBox);
EditorGUILayout.ObjectField(recorder, typeof(UXControllerStateRecorder), true);
EditorGUILayout.EndHorizontal();
}
}
EditorGUI.EndDisabledGroup();
serializedObject.ApplyModifiedProperties();
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 627b7a51244f46ce8bcbd31e6cedf4f8
timeCreated: 1764127335

View File

@ -0,0 +1,358 @@
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEditor.Experimental.SceneManagement;
using UnityEngine;
using AlicizaX.UI;
using AlicizaX.UI.Runtime;
using UnityEditor.SceneManagement;
using Random = UnityEngine.Random;
[CustomEditor(typeof(UXControllerStateRecorder))]
public class UXControllerStateRecorderEditor : Editor
{
private SerializedProperty _stateEntriesProp;
private SerializedProperty _controller;
private SerializedProperty _id;
private GUIContent _removeIcon;
private UXController _cacheController;
private void OnEnable()
{
_controller = serializedObject.FindProperty("_controller");
_id = serializedObject.FindProperty("_id");
_stateEntriesProp = serializedObject.FindProperty("_stateEntries");
_removeIcon = EditorGUIUtility.IconContent("d_winbtn_win_close");
}
private void OnDestroy()
{
if (target == null)
{
UnRegisterSelf();
}
}
private void UnRegisterSelf()
{
UXController controller = (_cacheController != null) ? _cacheController : GetPrefabStageController();
if (controller == null) return;
var so = new SerializedObject(controller);
var recorders = so.FindProperty("_recorders");
for (int i = recorders.arraySize - 1; i >= 0; i--)
{
var el = recorders.GetArrayElementAtIndex(i);
if (el.objectReferenceValue == null)
{
recorders.DeleteArrayElementAtIndex(i);
break;
}
}
so.ApplyModifiedProperties();
EditorUtility.SetDirty(controller);
}
private void RegisterSelfToController(UXController controller)
{
var so = new SerializedObject(controller);
var recorders = so.FindProperty("_recorders");
int newIndex = recorders.arraySize;
recorders.InsertArrayElementAtIndex(newIndex);
var el = recorders.GetArrayElementAtIndex(newIndex);
el.objectReferenceValue = target;
so.ApplyModifiedProperties();
EditorUtility.SetDirty(controller);
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.Space();
var recorder = target as UXControllerStateRecorder;
UXController prefabCtrl = GetPrefabStageController();
if (_id.intValue <= 0)
{
_id.intValue = Random.Range(10000000, 99999999);
}
EditorGUILayout.LabelField($"ID:{_id.intValue}", EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal();
if (prefabCtrl != null && prefabCtrl != _controller.objectReferenceValue)
{
_controller.objectReferenceValue = prefabCtrl;
}
if (!prefabCtrl.HasRecorder(recorder))
{
RegisterSelfToController(prefabCtrl);
}
if (_controller.objectReferenceValue != null)
{
_cacheController = _controller.objectReferenceValue as UXController;
}
if (_controller.objectReferenceValue != null)
{
UXController ctl = _controller.objectReferenceValue as UXController;
EditorGUILayout.LabelField($"{ctl.name} ({ctl.Controllers.Count} controllers)", EditorStyles.helpBox);
}
else
EditorGUILayout.LabelField("(None)", EditorStyles.helpBox);
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
Rect addRect = EditorGUILayout.GetControlRect(false, GUILayout.Height(22));
if (GUI.Button(addRect, "Add State"))
{
var provider = ScriptableObject.CreateInstance<StateTypeSearchProvider>();
provider.Init(OnTypeSelected);
var screenPos = GUIUtility.GUIToScreenPoint(new Vector2(addRect.x, addRect.yMax));
SearchWindow.Open(new SearchWindowContext(screenPos), provider);
}
EditorGUILayout.Space();
for (int i = 0; i < _stateEntriesProp.arraySize; i++)
{
var entryProp = _stateEntriesProp.GetArrayElementAtIndex(i);
if (entryProp == null) continue;
var stateProp = entryProp.FindPropertyRelative("State");
var controllerNameProp = entryProp.FindPropertyRelative("ControllerName");
var controllerIndexProp = entryProp.FindPropertyRelative("ControllerIndex");
if (stateProp == null)
{
EditorGUILayout.HelpBox($"Unable to find 'State' on entry {i}.", MessageType.Error);
if (GUILayout.Button("Remove"))
{
_stateEntriesProp.DeleteArrayElementAtIndex(i);
break;
}
continue;
}
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
var t = stateProp.managedReferenceValue.GetType();
var attr = t.GetCustomAttribute<ControlerStateNameAttribute>();
var tag = attr != null ? attr.StateName : t.Name;
EditorGUILayout.LabelField($"State:{tag}", EditorStyles.helpBox);
EditorGUILayout.BeginHorizontal();
var controllerNames = GetControllerNamesForRecorder(recorder);
int selIdx = -1;
if (!string.IsNullOrEmpty(controllerNameProp.stringValue))
{
selIdx = controllerNames.IndexOf(controllerNameProp.stringValue);
}
GUILayoutOption ctrlWidth = GUILayout.Width(140);
if (controllerNames.Count > 0)
{
int newSel = EditorGUILayout.Popup(selIdx, controllerNames.ToArray(), ctrlWidth);
if (newSel >= 0 && newSel < controllerNames.Count)
controllerNameProp.stringValue = controllerNames[newSel];
}
else
{
if (!string.IsNullOrEmpty(controllerNameProp.stringValue))
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.HelpBox($"绑定控制器 [{controllerNameProp.stringValue}] 不存在", MessageType.Error);
EditorGUI.EndDisabledGroup();
}
else
{
EditorGUILayout.HelpBox($"暂无控制器", MessageType.Warning);
}
}
int ctrlLen = GetSelectedControllerLength(recorder, controllerNameProp.stringValue);
if (ctrlLen <= 0) ctrlLen = 1;
controllerIndexProp.intValue = Mathf.Clamp(controllerIndexProp.intValue, 0, ctrlLen - 1);
string[] idxOptions = new string[ctrlLen];
for (int j = 0; j < ctrlLen; j++) idxOptions[j] = j.ToString();
int curIdx = Mathf.Clamp(controllerIndexProp.intValue, 0, ctrlLen - 1);
if (controllerNames.Count > 0)
{
int newIdx = EditorGUILayout.Popup(curIdx, idxOptions, GUILayout.Width(60));
if (newIdx != curIdx) controllerIndexProp.intValue = newIdx;
}
GUILayout.FlexibleSpace();
if (GUILayout.Button(_removeIcon, GUILayout.Width(28), GUILayout.Height(20)))
{
_stateEntriesProp.DeleteArrayElementAtIndex(i);
break;
}
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(4);
if (stateProp.managedReferenceValue == null)
{
EditorGUILayout.HelpBox("State instance is null. Remove and re-add.", MessageType.Warning);
}
else
{
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField(new GUIContent("属性"), EditorStyles.boldLabel);
SerializedClassDrawer.DrawSerializableProperty(stateProp);
EditorGUILayout.EndVertical();
}
bool valid = true;
try
{
var inst = stateProp.managedReferenceValue as ControllerStateBase;
if (inst != null) valid = inst.Valid(recorder);
}
catch (Exception ex)
{
valid = false;
Debug.LogException(ex);
}
if (!valid)
{
EditorGUILayout.HelpBox($"当前状态核验失败 请查看{stateProp.managedReferenceValue.GetType().FullName}脚本Valid", MessageType.Error);
}
EditorGUILayout.EndVertical();
EditorGUILayout.Space();
}
serializedObject.ApplyModifiedProperties();
}
private void OnTypeSelected(Type type)
{
if (type == null) return;
// 撤销支持
Undo.RecordObject(serializedObject.targetObject, "Add Controller State");
serializedObject.Update();
int idx = _stateEntriesProp.arraySize;
_stateEntriesProp.InsertArrayElementAtIndex(idx);
var entryProp = _stateEntriesProp.GetArrayElementAtIndex(idx);
var stateProp = entryProp.FindPropertyRelative("State");
var controllerNameProp = entryProp.FindPropertyRelative("ControllerName");
var controllerIndexProp = entryProp.FindPropertyRelative("ControllerIndex");
var defaultValueProp = entryProp.FindPropertyRelative("DefaultValue");
ControllerStateBase instance = null;
try
{
instance = (ControllerStateBase)Activator.CreateInstance(type);
}
catch (Exception ex)
{
Debug.LogException(ex);
}
if (instance != null)
{
var recorder = serializedObject.targetObject as UXControllerStateRecorder;
instance.Init(recorder);
}
// 把实例写进 SerializedProperty需保证你的 State 字段用了 [SerializeReference]
if (stateProp != null) stateProp.managedReferenceValue = instance;
if (stateProp != null) stateProp.isExpanded = true;
controllerNameProp.stringValue = "";
controllerIndexProp.intValue = 0;
serializedObject.ApplyModifiedProperties();
EditorUtility.SetDirty(serializedObject.targetObject);
}
private UXController GetPrefabStageController()
{
var stage = PrefabStageUtility.GetCurrentPrefabStage();
if (stage == null) return null;
var root = stage.prefabContentsRoot;
if (root == null) return null;
return root.GetComponentInChildren<UXController>();
}
private List<string> GetControllerNamesForRecorder(UXControllerStateRecorder recorder)
{
var names = new List<string>();
var prefabCtrl = GetPrefabStageController();
if (prefabCtrl != null)
{
foreach (var cd in prefabCtrl.Controllers) names.Add(cd.Name);
return names;
}
var assigned = recorder.Controller;
if (assigned != null)
{
foreach (var cd in assigned.Controllers) names.Add(cd.Name);
return names;
}
var inScene = recorder.gameObject.GetComponentInParent<UXController>();
if (inScene != null)
{
foreach (var cd in inScene.Controllers) names.Add(cd.Name);
return names;
}
return names;
}
private int GetSelectedControllerLength(UXControllerStateRecorder recorder, string controllerName)
{
var prefabCtrl = GetPrefabStageController();
if (prefabCtrl != null)
{
var ch = prefabCtrl.Controllers.ToList().Find(x => x.Name == controllerName);
if (ch != null) return ch.Length;
}
var assigned = recorder.Controller;
if (assigned != null)
{
var ch = assigned.Controllers.ToList().Find(x => x.Name == controllerName);
if (ch != null) return ch.Length;
}
var inScene = recorder.gameObject.GetComponentInParent<UXController>();
if (inScene != null)
{
var ch = inScene.Controllers.ToList().Find(x => x.Name == controllerName);
if (ch != null) return ch.Length;
}
return 1;
}
}
#endif

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a754ad6b8cb341ada13bb87279b12b78
timeCreated: 1764134852

View File

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

View File

@ -0,0 +1,36 @@
// Assets/Scripts/AlicizaX/UI/IControllerState.cs
using System;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
public abstract class ControllerStateBase
{
public abstract void Init(UXControllerStateRecorder recorder);
public abstract void Execute(UXControllerStateRecorder recorder, int entryIndex, int selectionIndex);
public abstract bool Valid(UXControllerStateRecorder recorder);
}
[AttributeUsage(AttributeTargets.Class)]
public class ControlerStateNameAttribute : Attribute
{
public string StateName;
public ControlerStateNameAttribute(string stateName)
{
StateName = stateName;
}
}
[AttributeUsage(AttributeTargets.Class)]
public class ControlerStateAttachTypeAttribute : Attribute
{
public bool Repeat;
public ControlerStateAttachTypeAttribute(bool repeat)
{
Repeat = repeat;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 774b49ad28de40fda18249bd9a24e38b
timeCreated: 1763705196

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 97df53cd2751466dae0ab1d774d48f12
timeCreated: 1763705022

View File

@ -0,0 +1,113 @@
using System;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
namespace AlicizaX.UI.Runtime
{
[Serializable]
[ControlerStateName("GameObject/Visiblity")]
public class GameObjectPropertyStateBase : ControllerStateBase
{
public override void Init(UXControllerStateRecorder recorder)
{
}
public override void Execute(UXControllerStateRecorder recorder, int entryIndex, int selectionIndex)
{
if (recorder != null && recorder.gameObject != null)
{
var visible = entryIndex == selectionIndex;
recorder.gameObject.SetActive(visible);
}
}
public override bool Valid(UXControllerStateRecorder recorder)
{
return recorder != null;
}
}
[Serializable]
[ControlerStateName("Transform/Position")]
public class TransformPostionPropertyStateBase : ControllerStateBase
{
[SerializeField] private Vector2 _pos;
[HideInInspector] [SerializeField] private Vector2 _cachePos;
public override void Init(UXControllerStateRecorder recorder)
{
_cachePos = recorder.gameObject.GetComponent<RectTransform>().anchoredPosition;
}
public override void Execute(UXControllerStateRecorder recorder, int entryIndex, int selectionIndex)
{
if (recorder != null && recorder.gameObject != null)
{
var pos = entryIndex != selectionIndex ? _cachePos : _pos;
recorder.GetComponent<RectTransform>().anchoredPosition = pos;
}
}
public override bool Valid(UXControllerStateRecorder recorder)
{
return recorder != null;
}
}
[Serializable]
[ControlerStateName("Transform/Scale")]
public class TransformScalePropertyStateBase : ControllerStateBase
{
[SerializeField] private Vector3 _scale;
[HideInInspector] [SerializeField] private Vector3 _cacheScale;
public override void Init(UXControllerStateRecorder recorder)
{
_cacheScale = recorder.gameObject.GetComponent<RectTransform>().localScale;
}
public override void Execute(UXControllerStateRecorder recorder, int entryIndex, int selectionIndex)
{
if (recorder != null && recorder.gameObject != null)
{
var scale = entryIndex != selectionIndex ? _cacheScale : _scale;
recorder.GetComponent<RectTransform>().localScale = scale;
}
}
public override bool Valid(UXControllerStateRecorder recorder)
{
return recorder != null;
}
}
[Serializable]
[ControlerStateName("Text/Color")]
public class TextColorPropertyStateBase : ControllerStateBase
{
[SerializeField] private Color _color = Color.white;
[HideInInspector] [SerializeField] private Color _cacheColor;
public override void Init(UXControllerStateRecorder recorder)
{
_cacheColor = recorder.GetComponent<Text>().color;
}
public override void Execute(UXControllerStateRecorder recorder, int entryIndex, int selectionIndex)
{
var t = recorder.GetComponent<Text>();
if (t != null)
{
var color = entryIndex != selectionIndex ? _cacheColor : _color;
t.color = color;
}
}
public override bool Valid(UXControllerStateRecorder recorder)
{
return recorder != null && recorder.GetComponent<Text>() != null;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e3b8cb1ae89c4c7b8dfb1c5797d4e8a0
timeCreated: 1763705161

View File

@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace AlicizaX.UI.Runtime
{
[DisallowMultipleComponent]
[AddComponentMenu("Controller/控制器")]
public sealed class UXController : MonoBehaviour
{
[Serializable]
public class ControllerData
{
public string Name = "Controller";
public int Length = 2;
[NonSerialized] public int CurrentIndex = 0;
}
[SerializeField] private List<ControllerData> _controllers = new List<ControllerData>();
[SerializeField] private List<UXControllerStateRecorder> _recorders = new List<UXControllerStateRecorder>();
internal IReadOnlyList<UXControllerStateRecorder> Recorders => _recorders;
#region Runtime API
internal IReadOnlyList<ControllerData> Controllers => _controllers;
private void Awake()
{
Initialized();
}
private void OnValidate()
{
Initialized();
}
private void Initialized()
{
for (int i = 0; i < _controllers.Count; i++)
{
SetControllerIndex(i, 0);
}
}
public ControllerHandle GetController(string name)
{
var idx = _controllers.FindIndex(c => c.Name == name);
if (idx < 0) return null;
return new ControllerHandle(this, idx);
}
internal void SetControllerIndex(int controllerIndex, int selectedIndex)
{
if (controllerIndex < 0 || controllerIndex >= _controllers.Count) return;
var cd = _controllers[controllerIndex];
if (selectedIndex < 0) selectedIndex = 0;
if (selectedIndex >= cd.Length) selectedIndex = cd.Length - 1;
if (cd.CurrentIndex == selectedIndex) return;
cd.CurrentIndex = selectedIndex;
foreach (var r in _recorders)
{
if (r != null) r.OnControllerIndexChanged(cd.Name, selectedIndex);
}
}
internal bool HasRecorder(UXControllerStateRecorder recorder)
{
return _recorders.Find(t => t.ID == recorder.ID) != null;
}
#endregion
[Serializable]
public class ControllerHandle
{
private readonly UXController _owner;
private readonly int _controllerIndex;
public ControllerHandle(UXController owner, int controllerIndex)
{
_owner = owner;
_controllerIndex = controllerIndex;
}
public int SelectedIndex
{
get
{
if (_controllerIndex < 0 || _controllerIndex >= _owner._controllers.Count) return 0;
return _owner._controllers[_controllerIndex].CurrentIndex;
}
set { _owner.SetControllerIndex(_controllerIndex, value); }
}
public int Length
{
get
{
if (_controllerIndex < 0 || _controllerIndex >= _owner._controllers.Count) return 0;
return _owner._controllers[_controllerIndex].Length;
}
}
public string Name => (_controllerIndex >= 0 && _controllerIndex < _owner._controllers.Count) ? _owner._controllers[_controllerIndex].Name : null;
}
}
}

View File

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

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using Random = UnityEngine.Random;
namespace AlicizaX.UI.Runtime
{
[DisallowMultipleComponent]
[AddComponentMenu("Controller/控制器状态")]
public class UXControllerStateRecorder : MonoBehaviour
{
[SerializeField] private int _id;
[SerializeField] private UXController _controller;
internal int ID => _id;
internal UXController Controller => _controller;
[Serializable]
public class StateEntry
{
[SerializeReference] public ControllerStateBase State = null;
public string ControllerName = string.Empty;
public int ControllerIndex = 0;
}
[SerializeField] private List<StateEntry> _stateEntries = new List<StateEntry>();
// private void Awake()
// {
// foreach (var entry in _stateEntries)
// {
// entry.State.Init(this);
// }
// }
internal void OnControllerIndexChanged(string controllerName, int selectedIndex)
{
var entrys = _stateEntries.FindAll(t => t.ControllerName.Equals(controllerName));
if (entrys != null)
{
foreach (var entry in entrys)
{
entry.State.Execute(this, entry.ControllerIndex, selectedIndex);
}
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 35829408994740a08c32ac2f519442f0
timeCreated: 1763705324

View File

@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("AlicizaX.UI.Extension.Editor")]
[assembly: InternalsVisibleTo("AlicizaUXTool.Editor")]

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e33eadf8eacb4c799ebce6f6178d4659
timeCreated: 1764221559