359 lines
12 KiB
C#
359 lines
12 KiB
C#
|
|
#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
|