移除odin依赖 优化

This commit is contained in:
陈思海 2025-11-18 11:27:53 +08:00
parent 0aa9e4f666
commit 6925badfec
17 changed files with 375 additions and 209 deletions

View File

@ -0,0 +1,19 @@
using UnityEditor;
using UnityEngine;
namespace AlicizaX.AnimationFlow.Editor
{
[CanEditMultipleObjects]
[CustomEditor(typeof(Runtime.AnimationFlow), true)]
public class AnimationFlowInspector : UnityEditor.Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Open Graph"))
{
UnityEditor.EditorApplication.ExecuteMenuItem("Window/AnimationGraph");
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bc1dbfa84fd94a41b4895ad2d058ac5d
timeCreated: 1763433819

View File

@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Reflection;
using AlicizaX.AnimationFlow.Runtime;
using Sirenix.OdinInspector.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
@ -10,36 +9,175 @@ namespace AlicizaX.AnimationFlow.Editor
{
public class GraphInspector : VisualElement
{
private PropertyTree propertyTree;
private ActionNode currentActionNode;
private NodeView currentNodeView;
public void DrawNode(NodeView nodeView)
{
Clear();
if (nodeView.actionNode == null)
if (nodeView?.actionNode == null)
{
return;
}
// 使用 Odin PropertyTree 来处理非 UnityEngine.Object 对象
propertyTree = PropertyTree.Create(nodeView.actionNode);
currentActionNode = nodeView.actionNode;
currentNodeView = nodeView;
var imguiContainer = new IMGUIContainer(() =>
{
if (propertyTree == null)
{
return;
}
if (currentActionNode == null) return;
propertyTree.Draw(false);
// 手动调用 Odin 的 Apply处理数据更新后的逻辑
if (propertyTree.ApplyChanges())
EditorGUI.BeginChangeCheck();
DrawActionNodeInspector();
if (EditorGUI.EndChangeCheck())
{
nodeView.RefreshState();
currentNodeView?.RefreshState();
}
});
Add(imguiContainer);
propertyTree.Dispose();
}
private void DrawActionNodeInspector()
{
EditorGUILayout.BeginVertical(GUI.skin.box);
DrawQualifiedFields(currentActionNode);
EditorGUILayout.EndVertical();
}
private void DrawQualifiedFields(object targetObject)
{
if (targetObject == null) return;
Type objectType = targetObject.GetType();
// 获取所有实例字段(公共和非公共)
var allFields = objectType.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var field in allFields)
{
// 跳过不应该绘制的字段
if (ShouldSkipField(field)) continue;
DrawFieldViaReflection(targetObject, field);
}
}
private bool ShouldSkipField(FieldInfo field)
{
// 跳过编译器生成的字段
if (field.IsDefined(typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false))
return true;
// 跳过非序列化字段但如果标记了SerializeField则仍然绘制
if (field.IsDefined(typeof(System.NonSerializedAttribute), false) && !field.IsDefined(typeof(SerializeField), false))
return true;
// 跳过静态字段
if (field.IsStatic)
return true;
// 跳过在ActionNode基类中声明的字段
if (field.DeclaringType == typeof(ActionNode))
return true;
// 跳过受保护字段protected
if (field.IsFamily)
return true;
// 关键修改只绘制公共字段或者私有但包含SerializeField特性的字段
if (!field.IsPublic && !field.IsDefined(typeof(SerializeField), false))
return true;
return false;
}
private void DrawFieldViaReflection(object targetObject, FieldInfo field)
{
try
{
var fieldValue = field.GetValue(targetObject);
var fieldType = field.FieldType;
string displayName = GetFieldDisplayName(field);
EditorGUILayout.BeginHorizontal();
var newValue = DrawFieldByType(displayName, fieldValue, fieldType);
if (!Equals(newValue, fieldValue))
{
field.SetValue(targetObject, newValue);
}
EditorGUILayout.EndHorizontal();
}
catch (Exception e)
{
EditorGUILayout.LabelField(field.Name, $"绘制错误: {e.Message}");
}
}
private string GetFieldDisplayName(FieldInfo field)
{
// 检查是否有Header特性
var headerAttr = field.GetCustomAttribute<HeaderAttribute>();
if (headerAttr != null)
{
return headerAttr.header;
}
// 将驼峰命名转换为空格分隔的友好名称
return ObjectNames.NicifyVariableName(field.Name);
}
private object DrawFieldByType(string label, object value, Type fieldType)
{
if (fieldType == typeof(string))
{
return EditorGUILayout.TextField(label, (string)value);
}
else if (fieldType == typeof(int))
{
return EditorGUILayout.IntField(label, (int)value);
}
else if (fieldType == typeof(float))
{
return EditorGUILayout.FloatField(label, (float)value);
}
else if (fieldType == typeof(bool))
{
return EditorGUILayout.Toggle(label, (bool)value);
}
else if (fieldType == typeof(Vector2))
{
return EditorGUILayout.Vector2Field(label, (Vector2)value);
}
else if (fieldType == typeof(Vector3))
{
return EditorGUILayout.Vector3Field(label, (Vector3)value);
}
else if (fieldType == typeof(Color))
{
return EditorGUILayout.ColorField(label, (Color)value);
}
else if (fieldType.IsEnum)
{
return EditorGUILayout.EnumPopup(label, (Enum)value);
}
else if (fieldType == typeof(UnityEngine.Object) || fieldType.IsSubclassOf(typeof(UnityEngine.Object)))
{
return EditorGUILayout.ObjectField(label, (UnityEngine.Object)value, fieldType, true);
}
else
{
// 对于不支持的类型,显示为只读标签
EditorGUILayout.LabelField(label, value?.ToString() ?? "null");
return value;
}
}
}
}

View File

@ -3,7 +3,6 @@ using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Sirenix.OdinInspector;
using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime
@ -32,11 +31,6 @@ namespace AlicizaX.AnimationFlow.Runtime
}
}
[Button("节点编辑器", ButtonSizes.Large), GUIColor(0, 1, 0)]
private void OpenGraphWindow()
{
UnityEditor.EditorApplication.ExecuteMenuItem("Window/AnimationGraph");
}
#endif
#endregion
@ -44,10 +38,10 @@ namespace AlicizaX.AnimationFlow.Runtime
[HideInInspector] [SerializeReference] public List<EntryNode> AnimationNodes = new List<EntryNode>();
[LabelText("动画片段")] [BoxGroup("基础设置", true)] [ValueDropdown("GetAllAnimationClips", ExpandAllMenuItems = true)] [SerializeField]
private string _defaultPlayName = "None";
[LabelText("自动播放")] [BoxGroup("基础设置", true)] [SerializeField]
private bool _enableAutoPlay;

View File

@ -1,60 +1,61 @@
using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime {
namespace AlicizaX.AnimationFlow.Runtime
{
[Category("Animation")]
public class CanvasGroupAlphaTo : ActionNode {
public float duration = 1f;
public EaseType easyType;
public CanvasGroup target;
public bool setFrom;
public float from;
public float to;
protected float orgValue;
protected float enterValue;
public class CanvasGroupAlphaTo : AnimationBaseNode<float>
{
[SerializeField]
private CanvasGroup _target;
public override void OnInit()
{
orgValue = target.alpha;
orgValue = _target.alpha;
}
public override void OnReset()
{
target.alpha = orgValue;
_target.alpha = orgValue;
}
public override void OnEnter() {
if (setFrom) {
target.alpha = from;
public override void OnEnter()
{
if (setFrom)
{
_target.alpha = from;
}
if (Condition())
{
target.alpha = to;
_target.alpha = to;
}
enterValue = target.alpha;
enterValue = _target.alpha;
}
public override void OnUpdate(float dt) {
target.alpha = Easing.Ease(easyType, enterValue, to, elapsedTime / duration);
public override void OnUpdate(float dt)
{
_target.alpha = Easing.Ease(easyType, enterValue, to, elapsedTime / duration);
}
public override bool Valid() {
return target != null;
public override bool Valid()
{
return _target != null;
}
public override float Duration() {
public override float Duration()
{
return duration;
}
public override bool HasSubTitle() {
public override bool HasSubTitle()
{
return true;
}
public override string SubTitle() {
return target != null ? target.name : null;
public override string SubTitle()
{
return _target != null ? _target.name : null;
}
}
}

View File

@ -1,54 +1,58 @@
using UnityEngine;
using UnityEngine.UI;
namespace AlicizaX.AnimationFlow.Runtime {
namespace AlicizaX.AnimationFlow.Runtime
{
[Category("Animation")]
public class GraphicAlphaTo : ActionNode {
public float duration = 1f;
public EaseType easyType;
public Graphic target;
public bool setFrom;
public float from;
public float to;
public class GraphicAlphaTo : AnimationBaseNode<float>
{
[SerializeField]
private Graphic _target;
protected float orgValue;
protected float enterValue;
public override void OnInit() {
orgValue = target.color.a;
public override void OnInit()
{
orgValue = _target.color.a;
}
public override void OnReset() {
target.color = new Color(target.color.r, target.color.g, target.color.b, orgValue);
public override void OnReset()
{
_target.color = new Color(_target.color.r, _target.color.g, _target.color.b, orgValue);
}
public override void OnEnter() {
if (setFrom) {
target.color = new Color(target.color.r, target.color.g, target.color.b, from);
public override void OnEnter()
{
if (setFrom)
{
_target.color = new Color(_target.color.r, _target.color.g, _target.color.b, from);
}
enterValue = target.color.a;
enterValue = _target.color.a;
}
public override void OnUpdate(float dt) {
public override void OnUpdate(float dt)
{
float a = Easing.Ease(easyType, enterValue, to, elapsedTime / duration);
target.color = new Color(target.color.r, target.color.g, target.color.b, a);
_target.color = new Color(_target.color.r, _target.color.g, _target.color.b, a);
}
public override bool Valid() {
return target != null && duration > 0;
public override bool Valid()
{
return _target != null && duration > 0;
}
public override float Duration() {
public override float Duration()
{
return duration;
}
public override bool HasSubTitle() {
public override bool HasSubTitle()
{
return true;
}
public override string SubTitle() {
return target != null ? target.name : null;
public override string SubTitle()
{
return _target != null ? _target.name : null;
}
}
}

View File

@ -4,50 +4,44 @@ using UnityEngine.UI;
namespace AlicizaX.AnimationFlow.Runtime
{
[Category("Animation")]
public class GraphicColorTo : ActionNode
public class GraphicColorTo : AnimationBaseNode<Color>
{
public float duration = 1f;
public EaseType easyType;
public Graphic target;
public bool setFrom;
public Color from = Color.white;
public Color to = Color.white;
protected Color orgValue;
protected Color enterValue;
[SerializeField]
private Graphic _target;
public override void OnInit()
{
orgValue = target.color;
orgValue = _target.color;
}
public override void OnReset()
{
target.color = orgValue;
_target.color = orgValue;
}
public override void OnEnter()
{
if (setFrom)
{
target.color = from;
_target.color = from;
}
if (Condition())
{
target.color = to;
_target.color = to;
}
enterValue = target.color;
enterValue = _target.color;
}
public override void OnUpdate(float dt)
{
target.color = Easing.Ease(easyType, enterValue, to, elapsedTime / duration);
_target.color = Easing.Ease(easyType, enterValue, to, elapsedTime / duration);
}
public override bool Valid()
{
return target != null;
return _target != null;
}
public override float Duration()
@ -62,7 +56,7 @@ namespace AlicizaX.AnimationFlow.Runtime
public override string SubTitle()
{
return target != null ? target.name : null;
return _target != null ? _target.name : null;
}
}
}

View File

@ -1,53 +1,56 @@
using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime {
namespace AlicizaX.AnimationFlow.Runtime
{
[Category("Animation")]
public class RotateTo : ActionNode {
public float duration = 1f;
public EaseType easyType;
public class RotateTo : AnimationBaseNode<Vector3>
{
public Transform target;
public bool setFrom;
public Vector3 from;
public Vector3 to;
protected Vector3 orgValue;
protected Vector3 enterValue;
public override void OnInit() {
public override void OnInit()
{
orgValue = target.localEulerAngles;
}
public override void OnReset() {
public override void OnReset()
{
target.localEulerAngles = orgValue;
}
public override void OnEnter() {
if (setFrom) {
public override void OnEnter()
{
if (setFrom)
{
target.localEulerAngles = from;
}
enterValue = target.localEulerAngles;
}
public override void OnUpdate(float dt) {
public override void OnUpdate(float dt)
{
target.localEulerAngles = Easing.Ease(easyType, enterValue, to, elapsedTime / duration);
}
public override bool Valid() {
public override bool Valid()
{
return target != null && duration > 0;
}
public override float Duration() {
public override float Duration()
{
return duration;
}
public override bool HasSubTitle() {
public override bool HasSubTitle()
{
return true;
}
public override string SubTitle() {
public override string SubTitle()
{
return target != null ? target.name : null;
}
}
}

View File

@ -1,52 +1,55 @@
using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime {
namespace AlicizaX.AnimationFlow.Runtime
{
[Category("Animation")]
public class ScaleTo : ActionNode {
public float duration = 1f;
public EaseType easyType;
public class ScaleTo : AnimationBaseNode<Vector3>
{
public Transform target;
public bool setFrom;
public Vector3 from;
public Vector3 to = Vector3.one;
protected Vector3 orgValue;
protected Vector3 enterValue;
public override void OnInit() {
public override void OnInit()
{
orgValue = target.localScale;
}
public override void OnReset() {
public override void OnReset()
{
target.localScale = orgValue;
}
public override void OnEnter() {
if (setFrom) {
public override void OnEnter()
{
if (setFrom)
{
target.localScale = from;
}
enterValue = target.localScale;
}
public override void OnUpdate(float dt) {
public override void OnUpdate(float dt)
{
target.localScale = Easing.Ease(easyType, enterValue, to, elapsedTime / duration);
}
public override bool Valid() {
public override bool Valid()
{
return target != null && duration > 0;
}
public override float Duration() {
public override float Duration()
{
return duration;
}
public override bool HasSubTitle() {
public override bool HasSubTitle()
{
return true;
}
public override string SubTitle() {
public override string SubTitle()
{
return target != null ? target.name : null;
}
}

View File

@ -1,54 +1,58 @@
using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime {
namespace AlicizaX.AnimationFlow.Runtime
{
[Category("Animation")]
public class SpriteRendererAlphaTo : ActionNode {
public float duration = 1f;
public EaseType easyType;
public class SpriteRendererAlphaTo : AnimationBaseNode<float>
{
public SpriteRenderer target;
public bool setFrom;
public float from;
public float to;
protected float orgValue;
protected float enterValue;
public override void OnInit() {
public override void OnInit()
{
orgValue = target.color.a;
}
public override void OnReset() {
public override void OnReset()
{
target.color = new Color(target.color.r, target.color.g, target.color.b, orgValue);
}
public override void OnEnter() {
if (setFrom) {
public override void OnEnter()
{
if (setFrom)
{
target.color = target.color = new Color(target.color.r, target.color.g, target.color.b, from);
}
enterValue = target.color.a;
}
public override void OnUpdate(float dt) {
public override void OnUpdate(float dt)
{
float a = Easing.Ease(easyType, enterValue, to, elapsedTime / duration);
target.color = new Color(target.color.r, target.color.g, target.color.b, a);
}
public override bool Valid() {
public override bool Valid()
{
return target != null && duration > 0;
}
public override float Duration() {
public override float Duration()
{
return duration;
}
public override bool HasSubTitle() {
public override bool HasSubTitle()
{
return true;
}
public override string SubTitle() {
public override string SubTitle()
{
return target != null ? target.name : null;
}
}
}

View File

@ -1,54 +1,57 @@
using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime {
namespace AlicizaX.AnimationFlow.Runtime
{
[Category("Animation")]
public class SpriteRendererColorTo : ActionNode {
public float duration = 1f;
public EaseType easyType;
public class SpriteRendererColorTo : AnimationBaseNode<Color>
{
public SpriteRenderer target;
public bool setFrom;
public Color from = Color.white;
public Color to = Color.white;
protected Color orgValue;
protected Color enterValue;
public override void OnInit() {
public override void OnInit()
{
orgValue = target.color;
}
public override void OnReset() {
public override void OnReset()
{
target.color = orgValue;
}
public override void OnEnter() {
if (setFrom) {
public override void OnEnter()
{
if (setFrom)
{
target.color = from;
}
enterValue = target.color;
}
public override void OnUpdate(float dt) {
public override void OnUpdate(float dt)
{
target.color = Easing.Ease(easyType, enterValue, to, elapsedTime / duration);
}
public override bool Valid() {
public override bool Valid()
{
return target != null && duration > 0;
}
public override float Duration() {
public override float Duration()
{
return duration;
}
public override bool HasSubTitle() {
public override bool HasSubTitle()
{
return true;
}
public override string SubTitle() {
public override string SubTitle()
{
return target != null ? target.name : null;
}
}
}

View File

@ -1,28 +1,13 @@
using Sirenix.OdinInspector;
using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime
{
[Category("Animation")]
[System.Serializable]
public class TranslateTo : ActionNode
public class TranslateTo : AnimationBaseNode<Vector3>
{
[LabelText("持续时间")] public float duration = 1f;
[LabelText("过渡类型")] public EaseType easyType;
[ChildGameObjectsOnly] [LabelText("对象")]
public Transform target;
[LabelText("设置起点")] public bool setFrom;
[LabelText("开始")] [ShowIf("setFrom", true)]
public Vector3 from;
[LabelText("结束")] public Vector3 to;
protected Vector3 orgValue;
protected Vector3 enterValue;
public override void OnInit()
{
orgValue = target.localPosition;

View File

@ -3,16 +3,9 @@ using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime
{
[Category("RectTransform")]
public class UISizeDelta : ActionNode
public class UISizeDelta : AnimationBaseNode<Vector2>
{
public float duration = 1f;
public EaseType easyType;
public RectTransform target;
public bool setFrom;
public Vector2 from;
public Vector2 to;
protected Vector2 orgValue;
protected Vector2 enterValue;
public override void OnInit()
{

3
Runtime/Core/Base.meta Normal file
View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b61695d4d191450e9a298bb9e925771a
timeCreated: 1763435401

View File

@ -0,0 +1,18 @@
using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime
{
public abstract class AnimationBaseNode<T> : ActionNode where T : unmanaged
{
[Header("持续事件")] public float duration = 1f;
[Header("动画类型")] public EaseType easyType;
[Header("设置起始")] public bool setFrom;
[Header("起始值")] public T from;
[Header("目标值")] public T to;
protected T orgValue;
protected T enterValue;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 954800fde7334242b74b7a36262bdae6
timeCreated: 1763435457

View File

@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using Sirenix.OdinInspector;
using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime
@ -18,8 +16,8 @@ namespace AlicizaX.AnimationFlow.Runtime
[System.Serializable]
public class EntryNode : ActionNode
{
[LabelText("循环次数")] public int LoopCount = 1; // 控制流程循环的次数
[LabelText("入口名称")] public string Name; // 节点名称(可以是技能名称、关卡名称等)
[Header("循环次数")] public int LoopCount = 1; // 控制流程循环的次数
[Header("入口名称")] public string Name; // 节点名称(可以是技能名称、关卡名称等)
}
[System.Serializable]