197 lines
6.2 KiB
C#
197 lines
6.2 KiB
C#
using UnityEngine;
|
|||
|
|
using UnityEditor;
|
||
|
|
using UnityEditor.Animations;
|
||
|
|
using UnityEditorInternal;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using AnimatorController = UnityEditor.Animations.AnimatorController;
|
||
|
|
|
||
|
|
public class AnimatorLinkWindow : EditorWindow
|
||
|
|
{
|
||
|
|
private AnimatorController animatorController;
|
||
|
|
private List<AnimationClip> clips = new List<AnimationClip>();
|
||
|
|
private ReorderableList reorderableList;
|
||
|
|
|
||
|
|
// 排版参数
|
||
|
|
private int statesPerColumn = 5;
|
||
|
|
private float horizontalSpacing = 200f;
|
||
|
|
private float verticalSpacing = 80f;
|
||
|
|
|
||
|
|
[MenuItem("Tools/Animator Link Window")]
|
||
|
|
public static void ShowWindow()
|
||
|
|
{
|
||
|
|
GetWindow<AnimatorLinkWindow>("Animator Linker");
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnEnable()
|
||
|
|
{
|
||
|
|
reorderableList = new ReorderableList(clips, typeof(AnimationClip), true, true, true, true);
|
||
|
|
|
||
|
|
reorderableList.drawHeaderCallback = (Rect rect) =>
|
||
|
|
{
|
||
|
|
EditorGUI.LabelField(rect, "Animation Clips (可拖拽排序)");
|
||
|
|
};
|
||
|
|
|
||
|
|
reorderableList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) =>
|
||
|
|
{
|
||
|
|
clips[index] = (AnimationClip)EditorGUI.ObjectField(
|
||
|
|
new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight),
|
||
|
|
clips[index], typeof(AnimationClip), false);
|
||
|
|
};
|
||
|
|
|
||
|
|
reorderableList.onAddCallback = (ReorderableList list) =>
|
||
|
|
{
|
||
|
|
clips.Add(null);
|
||
|
|
};
|
||
|
|
|
||
|
|
reorderableList.onRemoveCallback = (ReorderableList list) =>
|
||
|
|
{
|
||
|
|
clips.RemoveAt(list.index);
|
||
|
|
};
|
||
|
|
}
|
||
|
|
private Vector2 scrollPos;
|
||
|
|
private void OnGUI()
|
||
|
|
{
|
||
|
|
|
||
|
|
GUILayout.Label("Animator Controller", EditorStyles.boldLabel);
|
||
|
|
animatorController = (AnimatorController)EditorGUILayout.ObjectField(
|
||
|
|
"Controller", animatorController, typeof(AnimatorController), false);
|
||
|
|
|
||
|
|
GUILayout.Space(10);
|
||
|
|
|
||
|
|
// 排版参数输入框
|
||
|
|
statesPerColumn = EditorGUILayout.IntField("每列多少个状态", statesPerColumn);
|
||
|
|
horizontalSpacing = EditorGUILayout.FloatField("同列水平间隔", horizontalSpacing);
|
||
|
|
verticalSpacing = EditorGUILayout.FloatField("列与列垂直间隔", verticalSpacing);
|
||
|
|
// 开始滚动区域
|
||
|
|
scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
|
||
|
|
GUILayout.Space(10);
|
||
|
|
|
||
|
|
reorderableList.DoLayoutList();
|
||
|
|
EditorGUILayout.EndScrollView(); // 结束滚动区域
|
||
|
|
|
||
|
|
Rect dropArea = GUILayoutUtility.GetRect(0.0f, 50.0f, GUILayout.ExpandWidth(true));
|
||
|
|
GUI.Box(dropArea, "拖拽 AnimationClip 到这里 (支持多选)");
|
||
|
|
HandleDragAndDrop(dropArea);
|
||
|
|
|
||
|
|
GUILayout.Space(10);
|
||
|
|
if (GUILayout.Button("生成状态并连线"))
|
||
|
|
{
|
||
|
|
GenerateStatesAndTransitions();
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
private void HandleDragAndDrop(Rect dropArea)
|
||
|
|
{
|
||
|
|
Event evt = Event.current;
|
||
|
|
if (!dropArea.Contains(evt.mousePosition)) return;
|
||
|
|
|
||
|
|
switch (evt.type)
|
||
|
|
{
|
||
|
|
case EventType.DragUpdated:
|
||
|
|
case EventType.DragPerform:
|
||
|
|
if (DragAndDrop.objectReferences.Length > 0)
|
||
|
|
{
|
||
|
|
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
|
||
|
|
|
||
|
|
if (evt.type == EventType.DragPerform)
|
||
|
|
{
|
||
|
|
DragAndDrop.AcceptDrag();
|
||
|
|
foreach (Object obj in DragAndDrop.objectReferences)
|
||
|
|
{
|
||
|
|
if (obj is AnimationClip clip)
|
||
|
|
{
|
||
|
|
clips.Add(clip);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Debug.Log($"已添加 {DragAndDrop.objectReferences.Length} 个动画");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Event.current.Use();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void GenerateStatesAndTransitions()
|
||
|
|
{
|
||
|
|
if (animatorController == null)
|
||
|
|
{
|
||
|
|
Debug.LogError("请先指定 AnimatorController");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (clips.Count == 0)
|
||
|
|
{
|
||
|
|
Debug.LogError("请至少添加一个 AnimationClip");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var rootStateMachine = animatorController.layers[0].stateMachine;
|
||
|
|
|
||
|
|
// 判断是否已有状态
|
||
|
|
if (rootStateMachine.states.Length > 0)
|
||
|
|
{
|
||
|
|
int option = EditorUtility.DisplayDialogComplex(
|
||
|
|
"已有状态",
|
||
|
|
"当前 AnimatorController 已经有状态,是否清空后重新生成?",
|
||
|
|
"清空并重新生成",
|
||
|
|
"从尾部继续添加",
|
||
|
|
"取消"
|
||
|
|
);
|
||
|
|
|
||
|
|
if (option == 0)
|
||
|
|
{
|
||
|
|
// 清空
|
||
|
|
rootStateMachine.states = new ChildAnimatorState[0];
|
||
|
|
}
|
||
|
|
else if (option == 2)
|
||
|
|
{
|
||
|
|
// 取消
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
// option == 1 表示从尾部继续添加,不清空
|
||
|
|
}
|
||
|
|
|
||
|
|
AnimatorState prevState = null;
|
||
|
|
if (rootStateMachine.states.Length > 0)
|
||
|
|
{
|
||
|
|
// 如果已有状态,从最后一个开始连线
|
||
|
|
prevState = rootStateMachine.states[rootStateMachine.states.Length - 1].state;
|
||
|
|
}
|
||
|
|
|
||
|
|
int existingCount = rootStateMachine.states.Length;
|
||
|
|
|
||
|
|
for (int i = 0; i < clips.Count; i++)
|
||
|
|
{
|
||
|
|
var clip = clips[i];
|
||
|
|
if (clip == null) continue;
|
||
|
|
|
||
|
|
// ✅ 计算偏移量:已有状态数量 + 新的索引
|
||
|
|
int index = existingCount + i;
|
||
|
|
int col = index / statesPerColumn;
|
||
|
|
int row = index % statesPerColumn;
|
||
|
|
|
||
|
|
var state = rootStateMachine.AddState(
|
||
|
|
clip.name,
|
||
|
|
new Vector3(col * horizontalSpacing, row * verticalSpacing, 0)
|
||
|
|
);
|
||
|
|
state.motion = clip;
|
||
|
|
|
||
|
|
if (prevState != null)
|
||
|
|
{
|
||
|
|
var transition = prevState.AddTransition(state);
|
||
|
|
transition.hasExitTime = true;
|
||
|
|
transition.exitTime = 1f;
|
||
|
|
transition.duration = 0f;
|
||
|
|
}
|
||
|
|
|
||
|
|
prevState = state;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Debug.Log("生成完成:已按顺序创建状态并排版连线");
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
}
|