2025-12-09 15:08:41 +08:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
2025-12-19 20:26:22 +08:00
|
|
|
|
using System.Text;
|
|
|
|
|
|
using UnityEditor.AnimatedValues;
|
|
|
|
|
|
using UnityEditor.Animations;
|
|
|
|
|
|
using UnityEditor.DrawUtils;
|
|
|
|
|
|
using UnityEditor.Utils;
|
|
|
|
|
|
using UnityEditorInternal;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using UnityEngine.UI;
|
2025-12-19 20:26:22 +08:00
|
|
|
|
using AnimatorController = UnityEditor.Animations.AnimatorController;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
using AnimatorControllerParameterType = UnityEngine.AnimatorControllerParameterType;
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
namespace UnityEditor.UI
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
|
|
|
|
|
[CanEditMultipleObjects]
|
|
|
|
|
|
[CustomEditor(typeof(UXSelectable), true)]
|
2025-12-19 20:26:22 +08:00
|
|
|
|
internal class UXSelectableEditor : SelectableEditor
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
private SerializedProperty m_ChildTransitions;
|
|
|
|
|
|
private ReorderableList m_ChildTransitionList;
|
|
|
|
|
|
public TabbedInspector _tabs;
|
|
|
|
|
|
|
|
|
|
|
|
SerializedProperty m_ChildInteractableProperty;
|
|
|
|
|
|
SerializedProperty m_ChildTargetGraphicProperty;
|
|
|
|
|
|
SerializedProperty m_ChildTransitionProperty;
|
|
|
|
|
|
SerializedProperty m_ChildColorBlockProperty;
|
|
|
|
|
|
SerializedProperty m_ChildSpriteStateProperty;
|
|
|
|
|
|
SerializedProperty m_ChildAnimTriggerProperty;
|
|
|
|
|
|
SerializedProperty m_ChildNavigationProperty;
|
|
|
|
|
|
|
|
|
|
|
|
AnimBool m_ChildShowColorTint = new AnimBool();
|
|
|
|
|
|
AnimBool m_ChildShowSpriteTrasition = new AnimBool();
|
|
|
|
|
|
AnimBool m_ChildShowAnimTransition = new AnimBool();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
protected GUISkin customSkin;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
GUIContent m_ChildVisualizeNavigation = EditorGUIUtility.TrTextContent("Visualize", "Show navigation flows between selectable UI elements.");
|
|
|
|
|
|
private static bool s_ChildShowNavigation = false;
|
|
|
|
|
|
private static string s_ChildShowNavigationKey = "SelectableEditor.ShowNavigation";
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
private static Color darkZebraEven = new Color(0.22f, 0.22f, 0.22f);
|
|
|
|
|
|
private static Color darkZebraOdd = new Color(0.27f, 0.27f, 0.27f);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
protected override void OnEnable()
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
base.OnEnable();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
m_ChildInteractableProperty = serializedObject.FindProperty("m_Interactable");
|
|
|
|
|
|
m_ChildTargetGraphicProperty = serializedObject.FindProperty("m_TargetGraphic");
|
|
|
|
|
|
m_ChildTransitionProperty = serializedObject.FindProperty("m_Transition");
|
|
|
|
|
|
m_ChildColorBlockProperty = serializedObject.FindProperty("m_Colors");
|
|
|
|
|
|
m_ChildSpriteStateProperty = serializedObject.FindProperty("m_SpriteState");
|
|
|
|
|
|
m_ChildAnimTriggerProperty = serializedObject.FindProperty("m_AnimationTriggers");
|
|
|
|
|
|
m_ChildNavigationProperty = serializedObject.FindProperty("m_Navigation");
|
|
|
|
|
|
m_ChildTransitions = serializedObject.FindProperty("m_ChildTransitions");
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
CreateChildTransitionList();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
m_ChildTransitions = serializedObject.FindProperty("m_ChildTransitions");
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
var trans = GetTransition(m_ChildTransitionProperty);
|
|
|
|
|
|
m_ChildShowColorTint.value = (trans == Selectable.Transition.ColorTint);
|
|
|
|
|
|
m_ChildShowSpriteTrasition.value = (trans == Selectable.Transition.SpriteSwap);
|
|
|
|
|
|
m_ChildShowAnimTransition.value = (trans == Selectable.Transition.Animation);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
m_ChildShowColorTint.valueChanged.AddListener(Repaint);
|
|
|
|
|
|
m_ChildShowSpriteTrasition.valueChanged.AddListener(Repaint);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
_tabs = new TabbedInspector(typeof(UXSelectable).FullName + ".TabbedIndex");
|
|
|
|
|
|
_tabs.EnsureDefaultTab("Image", "d_Texture Icon", DrawBaseButtonInspector);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
customSkin = AssetDatabase.LoadAssetAtPath<GUISkin>("Packages/com.alicizax.unity.ui.extension/Editor/Res/GUISkin/UIExtensionGUISkin.guiskin");
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
protected override void OnDisable()
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
base.OnDisable();
|
|
|
|
|
|
m_ChildShowColorTint.valueChanged.RemoveListener(Repaint);
|
|
|
|
|
|
m_ChildShowSpriteTrasition.valueChanged.RemoveListener(Repaint);
|
|
|
|
|
|
_tabs.UnregisterTab("Image");
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
public override void OnInspectorGUI()
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
serializedObject.Update();
|
|
|
|
|
|
_tabs.DrawTabs();
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
void DrawBaseButtonInspector()
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
serializedObject.Update();
|
|
|
|
|
|
|
|
|
|
|
|
var interactable = GUILayoutHelper.DrawToggle(m_ChildInteractableProperty.boolValue, customSkin, "Interactable");
|
|
|
|
|
|
if (interactable != m_ChildInteractableProperty.boolValue)
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
m_ChildInteractableProperty.boolValue = interactable;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUILayout.PropertyField(m_ChildNavigationProperty);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUI.BeginChangeCheck();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
Rect toggleRect = EditorGUILayout.GetControlRect();
|
|
|
|
|
|
toggleRect.xMin += EditorGUIUtility.labelWidth;
|
2025-12-19 20:26:22 +08:00
|
|
|
|
s_ChildShowNavigation = GUI.Toggle(toggleRect, s_ChildShowNavigation, m_ChildVisualizeNavigation, EditorStyles.miniButton);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
if (EditorGUI.EndChangeCheck())
|
|
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorPrefs.SetBool(s_ChildShowNavigationKey, s_ChildShowNavigation);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
SceneView.RepaintAll();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
GUILayout.Space(1);
|
|
|
|
|
|
GUILayout.BeginVertical(EditorStyles.helpBox);
|
|
|
|
|
|
EditorGUILayout.LabelField("Main Transition", EditorStyles.boldLabel);
|
|
|
|
|
|
var trans = GetTransition(m_ChildTransitionProperty);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
var graphic = m_ChildTargetGraphicProperty.objectReferenceValue as Graphic;
|
|
|
|
|
|
if (graphic == null)
|
|
|
|
|
|
graphic = (target as Selectable).GetComponent<Graphic>();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
var animator = (target as Selectable).GetComponent<Animator>();
|
|
|
|
|
|
m_ChildShowColorTint.target = (!m_ChildTransitionProperty.hasMultipleDifferentValues && trans == Button.Transition.ColorTint);
|
|
|
|
|
|
m_ChildShowSpriteTrasition.target = (!m_ChildTransitionProperty.hasMultipleDifferentValues && trans == Button.Transition.SpriteSwap);
|
|
|
|
|
|
m_ChildShowAnimTransition.target = (!m_ChildTransitionProperty.hasMultipleDifferentValues && trans == Button.Transition.Animation);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUILayout.PropertyField(m_ChildTransitionProperty);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-12-16 16:48:44 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
if (trans == Selectable.Transition.ColorTint || trans == Selectable.Transition.SpriteSwap)
|
2025-12-16 16:51:40 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUILayout.PropertyField(m_ChildTargetGraphicProperty);
|
2025-12-16 16:51:40 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
switch (trans)
|
|
|
|
|
|
{
|
|
|
|
|
|
case Selectable.Transition.ColorTint:
|
|
|
|
|
|
if (graphic == null)
|
|
|
|
|
|
EditorGUILayout.HelpBox("You must have a Graphic target in order to use a color transition.", MessageType.Warning);
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case Selectable.Transition.SpriteSwap:
|
|
|
|
|
|
if (graphic as Image == null)
|
|
|
|
|
|
EditorGUILayout.HelpBox("You must have a Image target in order to use a sprite swap transition.", MessageType.Warning);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2025-12-16 16:48:44 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
if (EditorGUILayout.BeginFadeGroup(m_ChildShowColorTint.faded))
|
2025-12-16 16:48:44 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUILayout.PropertyField(m_ChildColorBlockProperty);
|
2025-12-16 16:48:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUILayout.EndFadeGroup();
|
2025-12-16 16:48:44 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
if (EditorGUILayout.BeginFadeGroup(m_ChildShowSpriteTrasition.faded))
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUILayout.PropertyField(m_ChildSpriteStateProperty);
|
|
|
|
|
|
}
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUILayout.EndFadeGroup();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
if (EditorGUILayout.BeginFadeGroup(m_ChildShowAnimTransition.faded))
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUILayout.PropertyField(m_ChildAnimTriggerProperty);
|
|
|
|
|
|
|
|
|
|
|
|
if (animator == null || animator.runtimeAnimatorController == null)
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
Rect buttonRect = EditorGUILayout.GetControlRect();
|
|
|
|
|
|
buttonRect.xMin += EditorGUIUtility.labelWidth;
|
|
|
|
|
|
if (GUI.Button(buttonRect, "Auto Generate Animation", EditorStyles.miniButton))
|
|
|
|
|
|
{
|
|
|
|
|
|
var controller = GenerateSelectableAnimatorContoller((target as Selectable).animationTriggers, target as Selectable);
|
|
|
|
|
|
if (controller != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (animator == null)
|
|
|
|
|
|
animator = (target as Selectable).gameObject.AddComponent<Animator>();
|
|
|
|
|
|
|
|
|
|
|
|
Animations.AnimatorController.SetAnimatorController(animator, controller);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-12-19 20:26:22 +08:00
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.EndFadeGroup();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.Space();
|
|
|
|
|
|
|
|
|
|
|
|
GUILayout.EndVertical();
|
|
|
|
|
|
GUILayout.Space(5);
|
|
|
|
|
|
|
|
|
|
|
|
m_ChildTransitionList.DoLayoutList();
|
|
|
|
|
|
|
2025-12-09 15:08:41 +08:00
|
|
|
|
GUILayout.Space(1);
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
#region ChildTransition
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
private void CreateChildTransitionList()
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
m_ChildTransitionList = new ReorderableList(serializedObject, m_ChildTransitions, true, false, true, true);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
m_ChildTransitionList.drawElementBackgroundCallback = (rect, index, isActive, isFocused) =>
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
var background = index % 2 == 0 ? darkZebraEven : darkZebraOdd;
|
|
|
|
|
|
EditorGUI.DrawRect(rect, background);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
m_ChildTransitionList.drawElementCallback = (rect, index, isActive, isFocused) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var element = m_ChildTransitionList.serializedProperty.GetArrayElementAtIndex(index);
|
|
|
|
|
|
rect.y += 2;
|
|
|
|
|
|
|
|
|
|
|
|
string elementTitle = $"Null Transition";
|
|
|
|
|
|
var targetProp = element.FindPropertyRelative("targetGraphic");
|
|
|
|
|
|
if (targetProp.objectReferenceValue != null)
|
|
|
|
|
|
elementTitle = targetProp.objectReferenceValue.name;
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUI.LabelField(new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight),
|
|
|
|
|
|
elementTitle, EditorStyles.boldLabel);
|
|
|
|
|
|
|
|
|
|
|
|
rect.y += EditorGUIUtility.singleLineHeight + 2;
|
|
|
|
|
|
DrawTransitionData(new Rect(rect.x, rect.y, rect.width, 0), element);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
m_ChildTransitionList.elementHeightCallback = (index) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
return EditorGUIUtility.singleLineHeight +
|
|
|
|
|
|
CalculateTransitionDataHeight(m_ChildTransitionList.serializedProperty.GetArrayElementAtIndex(index)) +
|
|
|
|
|
|
10;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
m_ChildTransitionList.onAddCallback = (list) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
list.serializedProperty.arraySize++;
|
|
|
|
|
|
serializedObject.ApplyModifiedProperties();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
m_ChildTransitionList.onRemoveCallback = (list) => { ReorderableList.defaultBehaviours.DoRemoveButton(list); };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void DrawTransitionData(Rect position, SerializedProperty transitionData)
|
|
|
|
|
|
{
|
|
|
|
|
|
SerializedProperty targetGraphic = transitionData.FindPropertyRelative("targetGraphic");
|
|
|
|
|
|
SerializedProperty transition = transitionData.FindPropertyRelative("transition");
|
|
|
|
|
|
SerializedProperty colorBlock = transitionData.FindPropertyRelative("colors");
|
|
|
|
|
|
SerializedProperty spriteState = transitionData.FindPropertyRelative("spriteState");
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUI.indentLevel++;
|
|
|
|
|
|
float lineHeight = EditorGUIUtility.singleLineHeight;
|
|
|
|
|
|
float spacing = 2f;
|
|
|
|
|
|
float y = position.y;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
|
|
|
|
|
var currentTransition = GetTransition(transition);
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
if (currentTransition == Selectable.Transition.Animation)
|
|
|
|
|
|
{
|
|
|
|
|
|
Rect warningRect = new Rect(position.x, y, position.width, lineHeight);
|
|
|
|
|
|
EditorGUI.HelpBox(warningRect, "Animation 过渡仅允许用于 Main Transition,子 Transition 不支持 Animation(已显示为 ColorTint)", MessageType.Warning);
|
|
|
|
|
|
y += lineHeight + spacing;
|
|
|
|
|
|
currentTransition = Selectable.Transition.ColorTint;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-09 15:08:41 +08:00
|
|
|
|
switch (currentTransition)
|
|
|
|
|
|
{
|
|
|
|
|
|
case Selectable.Transition.ColorTint:
|
|
|
|
|
|
case Selectable.Transition.SpriteSwap:
|
2025-12-19 20:26:22 +08:00
|
|
|
|
Rect targetRect = new Rect(position.x, y, position.width, lineHeight);
|
|
|
|
|
|
EditorGUI.PropertyField(targetRect, targetGraphic);
|
|
|
|
|
|
y += lineHeight + spacing;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
Rect transitionRect = new Rect(position.x, y, position.width, lineHeight);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
string[] options = new[] { "None", "Color Tint", "Sprite Swap" };
|
|
|
|
|
|
int[] values = new[] { (int)Selectable.Transition.None, (int)Selectable.Transition.ColorTint, (int)Selectable.Transition.SpriteSwap };
|
|
|
|
|
|
|
|
|
|
|
|
int curVal = transition.enumValueIndex;
|
|
|
|
|
|
int selIdx = Array.IndexOf(values, curVal);
|
|
|
|
|
|
if (selIdx < 0) selIdx = 0;
|
|
|
|
|
|
|
|
|
|
|
|
selIdx = EditorGUI.Popup(transitionRect, "Transition", selIdx, options);
|
|
|
|
|
|
transition.enumValueIndex = values[selIdx];
|
|
|
|
|
|
|
|
|
|
|
|
currentTransition = GetTransition(transition);
|
|
|
|
|
|
y += lineHeight + spacing;
|
|
|
|
|
|
|
|
|
|
|
|
var graphic = targetGraphic.objectReferenceValue as Graphic;
|
2025-12-15 13:42:08 +08:00
|
|
|
|
|
2025-12-09 15:08:41 +08:00
|
|
|
|
switch (currentTransition)
|
|
|
|
|
|
{
|
|
|
|
|
|
case Selectable.Transition.ColorTint:
|
|
|
|
|
|
if (graphic == null)
|
2025-12-19 20:26:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
Rect warningRect = new Rect(position.x, y, position.width, lineHeight);
|
|
|
|
|
|
EditorGUI.HelpBox(warningRect, "需要Graphic组件来使用颜色过渡", MessageType.Warning);
|
|
|
|
|
|
y += lineHeight + spacing;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-09 15:08:41 +08:00
|
|
|
|
break;
|
2025-12-19 20:26:22 +08:00
|
|
|
|
|
2025-12-09 15:08:41 +08:00
|
|
|
|
case Selectable.Transition.SpriteSwap:
|
|
|
|
|
|
if (!(graphic is Image))
|
2025-12-19 20:26:22 +08:00
|
|
|
|
{
|
|
|
|
|
|
Rect warningRect = new Rect(position.x, y, position.width, lineHeight);
|
|
|
|
|
|
EditorGUI.HelpBox(warningRect, "需要Image组件来使用精灵切换", MessageType.Warning);
|
|
|
|
|
|
y += lineHeight + spacing;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-09 15:08:41 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (currentTransition)
|
|
|
|
|
|
{
|
|
|
|
|
|
case Selectable.Transition.ColorTint:
|
2025-12-19 20:26:22 +08:00
|
|
|
|
CheckAndSetColorDefaults(colorBlock, targetGraphic);
|
|
|
|
|
|
Rect colorRect = new Rect(position.x, y, position.width, EditorGUI.GetPropertyHeight(colorBlock));
|
|
|
|
|
|
|
|
|
|
|
|
if (EditorGUILayout.BeginFadeGroup(m_ChildShowColorTint.faded))
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUI.PropertyField(colorRect, colorBlock);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EditorGUILayout.EndFadeGroup();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
break;
|
2025-12-19 20:26:22 +08:00
|
|
|
|
|
2025-12-09 15:08:41 +08:00
|
|
|
|
case Selectable.Transition.SpriteSwap:
|
2025-12-19 20:26:22 +08:00
|
|
|
|
CheckAndSetColorDefaults(colorBlock, targetGraphic);
|
|
|
|
|
|
Rect spriteRect = new Rect(position.x, y, position.width, EditorGUI.GetPropertyHeight(spriteState));
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
if (EditorGUILayout.BeginFadeGroup(m_ChildShowSpriteTrasition.faded))
|
|
|
|
|
|
{
|
|
|
|
|
|
EditorGUI.PropertyField(spriteRect, spriteState);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUILayout.EndFadeGroup();
|
2025-12-09 15:08:41 +08:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (graphic != null && currentTransition != (Selectable.Transition)transition.enumValueIndex &&
|
|
|
|
|
|
((Selectable.Transition)transition.enumValueIndex == Selectable.Transition.Animation ||
|
|
|
|
|
|
(Selectable.Transition)transition.enumValueIndex == Selectable.Transition.None))
|
|
|
|
|
|
{
|
|
|
|
|
|
graphic.canvasRenderer.SetColor(Color.white);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
EditorGUI.indentLevel--;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private float CalculateTransitionDataHeight(SerializedProperty transitionData)
|
|
|
|
|
|
{
|
|
|
|
|
|
float height = 0;
|
|
|
|
|
|
SerializedProperty transition = transitionData.FindPropertyRelative("transition");
|
|
|
|
|
|
var currentTransition = GetTransition(transition);
|
|
|
|
|
|
|
|
|
|
|
|
bool isChild = m_ChildTransitions != null &&
|
|
|
|
|
|
!string.IsNullOrEmpty(m_ChildTransitions.propertyPath) &&
|
|
|
|
|
|
transitionData.propertyPath.StartsWith(m_ChildTransitions.propertyPath);
|
|
|
|
|
|
|
|
|
|
|
|
if (isChild && currentTransition == Selectable.Transition.Animation)
|
|
|
|
|
|
{
|
|
|
|
|
|
currentTransition = Selectable.Transition.ColorTint;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
height += EditorGUIUtility.singleLineHeight * 1.5f;
|
|
|
|
|
|
height += EditorGUIUtility.singleLineHeight;
|
|
|
|
|
|
|
|
|
|
|
|
SerializedProperty targetGraphic = transitionData.FindPropertyRelative("targetGraphic");
|
|
|
|
|
|
var graphic = targetGraphic.objectReferenceValue as Graphic;
|
|
|
|
|
|
|
|
|
|
|
|
switch (currentTransition)
|
|
|
|
|
|
{
|
|
|
|
|
|
case Selectable.Transition.ColorTint:
|
|
|
|
|
|
if (graphic == null) height += EditorGUIUtility.singleLineHeight;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case Selectable.Transition.SpriteSwap:
|
|
|
|
|
|
if (!(graphic is Image)) height += EditorGUIUtility.singleLineHeight;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch (currentTransition)
|
|
|
|
|
|
{
|
|
|
|
|
|
case Selectable.Transition.ColorTint:
|
|
|
|
|
|
height += EditorGUI.GetPropertyHeight(transitionData.FindPropertyRelative("colors"));
|
|
|
|
|
|
break;
|
|
|
|
|
|
case Selectable.Transition.SpriteSwap:
|
|
|
|
|
|
height += EditorGUI.GetPropertyHeight(transitionData.FindPropertyRelative("spriteState"));
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return height;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected void CheckAndSetColorDefaults(SerializedProperty colorBlock, SerializedProperty targetGraphic)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (colorBlock == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
bool isDirty = false;
|
|
|
|
|
|
string[] colorProps = new string[] { "m_NormalColor", "m_HighlightedColor", "m_PressedColor", "m_SelectedColor", "m_DisabledColor" };
|
|
|
|
|
|
foreach (var propName in colorProps)
|
|
|
|
|
|
{
|
|
|
|
|
|
SerializedProperty prop = colorBlock.FindPropertyRelative(propName);
|
|
|
|
|
|
if (prop == null) continue;
|
|
|
|
|
|
Color color = prop.colorValue;
|
|
|
|
|
|
if (color.r == 0 && color.g == 0 && color.b == 0 && color.a == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
isDirty = true;
|
|
|
|
|
|
if (prop.name == "m_PressedColor")
|
|
|
|
|
|
{
|
|
|
|
|
|
prop.colorValue = new Color(0.7843137f, 0.7843137f, 0.7843137f, 1.0f);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (prop.name == "m_DisabledColor")
|
|
|
|
|
|
{
|
|
|
|
|
|
prop.colorValue = new Color(0.7843137f, 0.7843137f, 0.7843137f, 0.5f);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
prop.colorValue = Color.white;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SerializedProperty fadeDuration = colorBlock.FindPropertyRelative("m_FadeDuration");
|
|
|
|
|
|
SerializedProperty m_ColorMultiplier = colorBlock.FindPropertyRelative("m_ColorMultiplier");
|
|
|
|
|
|
if (isDirty)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (m_ColorMultiplier != null) m_ColorMultiplier.floatValue = 1f;
|
|
|
|
|
|
if (fadeDuration != null) fadeDuration.floatValue = 0.1f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var graphic = targetGraphic != null ? targetGraphic.objectReferenceValue as Graphic : null;
|
|
|
|
|
|
if (graphic != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!EditorApplication.isPlaying)
|
|
|
|
|
|
{
|
|
|
|
|
|
SerializedProperty normalColorProp = colorBlock.FindPropertyRelative("m_NormalColor");
|
|
|
|
|
|
if (normalColorProp != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Color color = normalColorProp.colorValue;
|
|
|
|
|
|
graphic.canvasRenderer.SetColor(color);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Static Method
|
|
|
|
|
|
|
|
|
|
|
|
static Selectable.Transition GetTransition(SerializedProperty transition)
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
return (Selectable.Transition)transition.enumValueIndex;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static AnimatorController GenerateSelectableAnimatorContoller(AnimationTriggers animationTriggers, Selectable target)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (target == null)
|
2025-12-09 15:08:41 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
// Where should we create the controller?
|
|
|
|
|
|
var path = GetSaveControllerPath(target);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
if (string.IsNullOrEmpty(path))
|
|
|
|
|
|
return null;
|
2025-12-19 20:26:22 +08:00
|
|
|
|
|
|
|
|
|
|
// figure out clip names
|
|
|
|
|
|
var normalName = string.IsNullOrEmpty(animationTriggers.normalTrigger) ? "Normal" : animationTriggers.normalTrigger;
|
|
|
|
|
|
var highlightedName = string.IsNullOrEmpty(animationTriggers.highlightedTrigger) ? "Highlighted" : animationTriggers.highlightedTrigger;
|
|
|
|
|
|
var pressedName = string.IsNullOrEmpty(animationTriggers.pressedTrigger) ? "Pressed" : animationTriggers.pressedTrigger;
|
|
|
|
|
|
var selectedName = string.IsNullOrEmpty(animationTriggers.selectedTrigger) ? "Selected" : animationTriggers.selectedTrigger;
|
|
|
|
|
|
var disabledName = string.IsNullOrEmpty(animationTriggers.disabledTrigger) ? "Disabled" : animationTriggers.disabledTrigger;
|
|
|
|
|
|
|
|
|
|
|
|
// Create controller and hook up transitions.
|
|
|
|
|
|
var controller = AnimatorController.CreateAnimatorControllerAtPath(path);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
GenerateTriggerableTransition(normalName, controller);
|
|
|
|
|
|
GenerateTriggerableTransition(highlightedName, controller);
|
|
|
|
|
|
GenerateTriggerableTransition(pressedName, controller);
|
|
|
|
|
|
GenerateTriggerableTransition(selectedName, controller);
|
|
|
|
|
|
GenerateTriggerableTransition(disabledName, controller);
|
|
|
|
|
|
|
|
|
|
|
|
AssetDatabase.ImportAsset(path);
|
|
|
|
|
|
|
|
|
|
|
|
return controller;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
private static string GetSaveControllerPath(Selectable target)
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
var defaultName = target.gameObject.name;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
var message = string.Format("Create a new animator for the game object '{0}':", defaultName);
|
|
|
|
|
|
return EditorUtility.SaveFilePanelInProject("New Animation Contoller", defaultName, "controller", message);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
private static void SetUpCurves(AnimationClip highlightedClip, AnimationClip pressedClip, string animationPath)
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
string[] channels = { "m_LocalScale.x", "m_LocalScale.y", "m_LocalScale.z" };
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
var highlightedKeys = new[] { new Keyframe(0f, 1f), new Keyframe(0.5f, 1.1f), new Keyframe(1f, 1f) };
|
|
|
|
|
|
var highlightedCurve = new AnimationCurve(highlightedKeys);
|
|
|
|
|
|
foreach (var channel in channels)
|
|
|
|
|
|
AnimationUtility.SetEditorCurve(highlightedClip, EditorCurveBinding.FloatCurve(animationPath, typeof(Transform), channel), highlightedCurve);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
var pressedKeys = new[] { new Keyframe(0f, 1.15f) };
|
|
|
|
|
|
var pressedCurve = new AnimationCurve(pressedKeys);
|
|
|
|
|
|
foreach (var channel in channels)
|
|
|
|
|
|
AnimationUtility.SetEditorCurve(pressedClip, EditorCurveBinding.FloatCurve(animationPath, typeof(Transform), channel), pressedCurve);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
private static string BuildAnimationPath(Selectable target)
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
// if no target don't hook up any curves.
|
|
|
|
|
|
var highlight = target.targetGraphic;
|
|
|
|
|
|
if (highlight == null)
|
|
|
|
|
|
return string.Empty;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
var startGo = highlight.gameObject;
|
|
|
|
|
|
var toFindGo = target.gameObject;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
var pathComponents = new Stack<string>();
|
|
|
|
|
|
while (toFindGo != startGo)
|
2025-12-09 15:08:41 +08:00
|
|
|
|
{
|
2025-12-19 20:26:22 +08:00
|
|
|
|
pathComponents.Push(startGo.name);
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
// didn't exist in hierarchy!
|
|
|
|
|
|
if (startGo.transform.parent == null)
|
|
|
|
|
|
return string.Empty;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
startGo = startGo.transform.parent.gameObject;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
// calculate path
|
|
|
|
|
|
var animPath = new StringBuilder();
|
|
|
|
|
|
if (pathComponents.Count > 0)
|
|
|
|
|
|
animPath.Append(pathComponents.Pop());
|
2025-12-09 15:08:41 +08:00
|
|
|
|
|
2025-12-19 20:26:22 +08:00
|
|
|
|
while (pathComponents.Count > 0)
|
|
|
|
|
|
animPath.Append("/").Append(pathComponents.Pop());
|
|
|
|
|
|
|
|
|
|
|
|
return animPath.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static AnimationClip GenerateTriggerableTransition(string name, AnimatorController controller)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Create the clip
|
|
|
|
|
|
var clip = AnimatorController.AllocateAnimatorClip(name);
|
|
|
|
|
|
AssetDatabase.AddObjectToAsset(clip, controller);
|
|
|
|
|
|
|
|
|
|
|
|
// Create a state in the animatior controller for this clip
|
|
|
|
|
|
var state = controller.AddMotion(clip);
|
|
|
|
|
|
|
|
|
|
|
|
// Add a transition property
|
|
|
|
|
|
controller.AddParameter(name, AnimatorControllerParameterType.Trigger);
|
|
|
|
|
|
|
|
|
|
|
|
// Add an any state transition
|
|
|
|
|
|
var stateMachine = controller.layers[0].stateMachine;
|
|
|
|
|
|
var transition = stateMachine.AddAnyStateTransition(state);
|
|
|
|
|
|
transition.AddCondition(AnimatorConditionMode.If, 0, name);
|
|
|
|
|
|
return clip;
|
2025-12-09 15:08:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|