移除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;
using System.Linq; using System.Reflection;
using AlicizaX.AnimationFlow.Runtime; using AlicizaX.AnimationFlow.Runtime;
using Sirenix.OdinInspector.Editor;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using UnityEngine.UIElements; using UnityEngine.UIElements;
@ -10,36 +9,175 @@ namespace AlicizaX.AnimationFlow.Editor
{ {
public class GraphInspector : VisualElement public class GraphInspector : VisualElement
{ {
private PropertyTree propertyTree; private ActionNode currentActionNode;
private NodeView currentNodeView;
public void DrawNode(NodeView nodeView) public void DrawNode(NodeView nodeView)
{ {
Clear(); Clear();
if (nodeView.actionNode == null) if (nodeView?.actionNode == null)
{ {
return; return;
} }
// 使用 Odin PropertyTree 来处理非 UnityEngine.Object 对象 currentActionNode = nodeView.actionNode;
propertyTree = PropertyTree.Create(nodeView.actionNode); currentNodeView = nodeView;
var imguiContainer = new IMGUIContainer(() => var imguiContainer = new IMGUIContainer(() =>
{ {
if (propertyTree == null) if (currentActionNode == null) return;
EditorGUI.BeginChangeCheck();
DrawActionNodeInspector();
if (EditorGUI.EndChangeCheck())
{ {
return; currentNodeView?.RefreshState();
}
propertyTree.Draw(false);
// 手动调用 Odin 的 Apply处理数据更新后的逻辑
if (propertyTree.ApplyChanges())
{
nodeView.RefreshState();
} }
}); });
Add(imguiContainer); 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.Collections.Generic;
using System.Linq; using System.Linq;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime 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 #endif
#endregion #endregion
@ -44,10 +38,10 @@ namespace AlicizaX.AnimationFlow.Runtime
[HideInInspector] [SerializeReference] public List<EntryNode> AnimationNodes = new List<EntryNode>(); [HideInInspector] [SerializeReference] public List<EntryNode> AnimationNodes = new List<EntryNode>();
[LabelText("动画片段")] [BoxGroup("基础设置", true)] [ValueDropdown("GetAllAnimationClips", ExpandAllMenuItems = true)] [SerializeField]
private string _defaultPlayName = "None"; private string _defaultPlayName = "None";
[LabelText("自动播放")] [BoxGroup("基础设置", true)] [SerializeField]
private bool _enableAutoPlay; private bool _enableAutoPlay;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,54 +1,58 @@
using UnityEngine; using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime { namespace AlicizaX.AnimationFlow.Runtime
{
[Category("Animation")] [Category("Animation")]
public class SpriteRendererAlphaTo : ActionNode { public class SpriteRendererAlphaTo : AnimationBaseNode<float>
public float duration = 1f; {
public EaseType easyType;
public SpriteRenderer target; 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; 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); target.color = new Color(target.color.r, target.color.g, target.color.b, orgValue);
} }
public override void OnEnter() { public override void OnEnter()
if (setFrom) { {
if (setFrom)
{
target.color = target.color = new Color(target.color.r, target.color.g, target.color.b, from); target.color = 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); 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() { public override bool Valid()
{
return target != null && duration > 0; return target != null && duration > 0;
} }
public override float Duration() { public override float Duration()
{
return duration; return duration;
} }
public override bool HasSubTitle() { public override bool HasSubTitle()
{
return true; return true;
} }
public override string SubTitle() { public override string SubTitle()
{
return target != null ? target.name : null; return target != null ? target.name : null;
} }
} }
} }

View File

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

View File

@ -1,28 +1,13 @@
using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime namespace AlicizaX.AnimationFlow.Runtime
{ {
[Category("Animation")] [Category("Animation")]
[System.Serializable] [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; 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() public override void OnInit()
{ {
orgValue = target.localPosition; orgValue = target.localPosition;

View File

@ -3,16 +3,9 @@ using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime namespace AlicizaX.AnimationFlow.Runtime
{ {
[Category("RectTransform")] [Category("RectTransform")]
public class UISizeDelta : ActionNode public class UISizeDelta : AnimationBaseNode<Vector2>
{ {
public float duration = 1f;
public EaseType easyType;
public RectTransform target; public RectTransform target;
public bool setFrom;
public Vector2 from;
public Vector2 to;
protected Vector2 orgValue;
protected Vector2 enterValue;
public override void OnInit() 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
using Sirenix.OdinInspector;
using UnityEngine; using UnityEngine;
namespace AlicizaX.AnimationFlow.Runtime namespace AlicizaX.AnimationFlow.Runtime
@ -18,8 +16,8 @@ namespace AlicizaX.AnimationFlow.Runtime
[System.Serializable] [System.Serializable]
public class EntryNode : ActionNode public class EntryNode : ActionNode
{ {
[LabelText("循环次数")] public int LoopCount = 1; // 控制流程循环的次数 [Header("循环次数")] public int LoopCount = 1; // 控制流程循环的次数
[LabelText("入口名称")] public string Name; // 节点名称(可以是技能名称、关卡名称等) [Header("入口名称")] public string Name; // 节点名称(可以是技能名称、关卡名称等)
} }
[System.Serializable] [System.Serializable]
@ -138,4 +136,4 @@ namespace AlicizaX.AnimationFlow.Runtime
State = EState.None; State = EState.None;
} }
} }
} }