From 3db514468fac20693a257179d35592d0b78e2936 Mon Sep 17 00:00:00 2001 From: chai Date: Wed, 7 Jul 2021 22:13:48 +0800 Subject: +RootMotion --- .../Unit/RootMotion/Editor/RootMotionEditor.cs | 247 ++++++++++++++++++++- 1 file changed, 246 insertions(+), 1 deletion(-) (limited to 'Assets/Scripts/Unit/RootMotion/Editor/RootMotionEditor.cs') diff --git a/Assets/Scripts/Unit/RootMotion/Editor/RootMotionEditor.cs b/Assets/Scripts/Unit/RootMotion/Editor/RootMotionEditor.cs index facf295b..3c8c55a9 100644 --- a/Assets/Scripts/Unit/RootMotion/Editor/RootMotionEditor.cs +++ b/Assets/Scripts/Unit/RootMotion/Editor/RootMotionEditor.cs @@ -1,12 +1,40 @@ -using System.Collections; +using System; +using System.IO; +using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class RootMotionEditor : EditorWindow { + static string s_AnimFolder = "Assets/Art/Animations/"; + static string s_Controller = "Assets/Scripts/Unit/RootMotion/controller_rootmotion.controller"; + static string s_RootMotionDataFolder = "Assets/Data/RootMotionData/"; + + GameObject m_Prefab; + string m_PrefabName { get { return m_Prefab.name; } } + GameObject m_Unit; + Transform m_Transform { get { return m_Unit.transform; } } + string m_SearchText = ""; + HashSet m_SelectAnimations; + List m_AnimList = new List(); + + Animator m_Animator; + AnimatorOverrideController m_OverrideController; + + const string kEmptyClipName = "Empty"; + const string kStateName = "Action"; + + [MenuItem("Custom/RootMotion/Create")] + static void OpenTool() + { + RootMotionEditor editor = GetWindow(); + } + private void OnEnable() { + titleContent = new GUIContent("RootMotion Editor"); + m_SelectAnimations = new HashSet(); } private void OnDisable() @@ -14,4 +42,221 @@ public class RootMotionEditor : EditorWindow } + private void OnGUI() + { + if (m_SelectAnimations == null) m_SelectAnimations = new HashSet(); + if(m_AnimList == null) + { + m_AnimList = new List(); + if (m_Prefab != null) + CollectAnimations(m_Prefab.name); + } + + EditorGUILayout.LabelField("Select Unit:"); + GameObject prefab = EditorGUILayout.ObjectField(m_Prefab, typeof(GameObject), false) as GameObject; + if(prefab != m_Prefab) + { + OnSelectPrefab(prefab); + } + if(m_Prefab == null) + { + EditorGUILayout.HelpBox("选择角色", MessageType.Warning); + } + GUI_SelectAnimation(); + GUI_Export(); + } + + private void OnSelectPrefab(GameObject prefab) + { + m_Prefab = prefab; + if(m_Prefab != null) + { + m_Unit = PrefabUtility.InstantiatePrefab(m_Prefab) as GameObject; + InitializeUnit(m_Unit); + m_SelectAnimations.Clear(); + CollectAnimations(m_Prefab.name); + } + else + { + m_SelectAnimations.Clear(); + m_Unit = null; + } + } + + void InitializeUnit(GameObject unit) + { + if (unit == null) + return; + + m_Animator = unit.GetComponent(); + m_Animator.applyRootMotion = true; + + RuntimeAnimatorController controller = AssetDatabase.LoadAssetAtPath(s_Controller, typeof(RuntimeAnimatorController)) as RuntimeAnimatorController; + if (controller) + { + m_OverrideController = new AnimatorOverrideController(controller); + m_Animator.runtimeAnimatorController = m_OverrideController; + } + } + + void CollectAnimations(string prefabName) + { + m_AnimList.Clear(); + string animFolder = s_AnimFolder + m_Prefab.name + "/"; + string[] animfiles = Directory.GetFiles(animFolder/*, "*.anim"*/); + if (animfiles != null && animfiles.Length > 0) + { + for (int i = 0; i < animfiles.Length; ++i) + { + string file = animfiles[i]; + if (file.Contains(".meta")) + continue; + string animName = Path.GetFileNameWithoutExtension(file); + m_AnimList.Add(animName); + } + } + } + + bool IsAllSelected() + { + if (m_SelectAnimations == null || m_SelectAnimations.Count == 0) + return false; + if (m_AnimList == null || m_AnimList.Count == 0) + return false; + return m_AnimList.Count == m_SelectAnimations.Count; + } + + Vector2 m_AnimtionListScroll; + private void GUI_SelectAnimation() + { + if (m_Prefab == null) + return; + EditorGUILayout.LabelField("Select Animations:"); + m_SearchText = GUILayout.TextField(m_SearchText, "SearchTextField", GUILayout.Width(position.width - 20)).ToLower(); + + bool isAllSelected = IsAllSelected(); + bool allselcted = GUILayout.Toggle(isAllSelected, "全选"); + if(allselcted && !isAllSelected) + { + m_SelectAnimations.Clear(); + m_AnimList.ForEach(s =>m_SelectAnimations.Add(s)); + } + else if(!allselcted && isAllSelected) + { + m_SelectAnimations.Clear(); + } + + if (m_AnimList != null && m_AnimList.Count > 0) + { + m_AnimtionListScroll = EditorGUILayout.BeginScrollView(m_AnimtionListScroll, GUILayout.Height(400)); + + for (int i = 0; i < m_AnimList.Count; ++i) + { + string file = m_AnimList[i]; + if (file.Contains(".meta")) + continue; + string animName = Path.GetFileNameWithoutExtension(file); + bool show = m_SearchText == string.Empty || m_SearchText == "" || animName.ToLower().Contains(m_SearchText); + if (!show) + continue; + bool bChecked = m_SelectAnimations.Contains(animName); + bool check = GUILayout.Toggle(bChecked, animName, GUILayout.Height(15)); + if (check && !bChecked) + m_SelectAnimations.Add(animName); + else if(!check && bChecked) + m_SelectAnimations.Remove(animName); + } + + EditorGUILayout.EndScrollView(); + } + } + + void GUI_Export() + { + EditorGUILayout.Space(); + EditorGUILayout.Space(); + + GUI.enabled = m_SelectAnimations != null && m_SelectAnimations.Count > 0; + if (GUILayout.Button("Export")) + { + ExportRootMotions(); + } + GUI.enabled = true; + } + + void ExportRootMotions() + { + if (m_SelectAnimations == null || m_SelectAnimations.Count == 0) + return; + + foreach(var animation in m_SelectAnimations) + { + ExportRootMotion(animation); + } + + EditorUtility.DisplayDialog("Export RootMotion", "目录 " + s_RootMotionDataFolder + m_Prefab.name, "OK"); + } + + void ExportRootMotion(string animation) + { + string animPath = s_AnimFolder + m_PrefabName + "/" + animation + ".anim"; + string assetPath = s_RootMotionDataFolder + m_PrefabName + "/" + animation + ".asset"; + AnimationClip clip = AssetDatabase.LoadAssetAtPath(animPath); + if(clip == null) + { + Debug.LogError("[RootMotion Editor] 没有对应的animation, " + animPath); + return; + } + m_OverrideController[kEmptyClipName] = clip; + + m_Transform.position = Vector3.zero; + m_Transform.rotation = Quaternion.identity; + + float frameRate = clip.frameRate; + float timeLen = clip.length; + + float sampleRate = 30; // rootmotion 采样频率,每秒采样30帧 + float sampleDuration = 1 / sampleRate; + + RootMotionData rootmotion = new RootMotionData(); + rootmotion.animationName = animation; + rootmotion.animationLength = timeLen; + rootmotion.fps = sampleRate; + + rootmotion.positionList = new List(); + rootmotion.positionList.Add(Vector3.zero); + + const float kDeltaTime = 1 / 100f; + float t = 0; + float sampleTime = 0; + + while(true) + { + m_Animator.speed = 1; + m_Animator.Play(kStateName, 0, t / timeLen); + m_Animator.Update(kDeltaTime); + m_Animator.speed = 0; + + //m_Transform.rotation *= m_Animator.deltaRotation; + //m_Transform.position += m_Animator.deltaPosition; + + sampleTime += kDeltaTime; + if(sampleTime >= sampleDuration) + { + rootmotion.positionList.Add(m_Transform.position); + sampleTime = sampleTime - sampleDuration; + } + + if (t == timeLen) + break; + t += kDeltaTime; + if (t > timeLen) + t = timeLen; + } + + rootmotion.frameCount = rootmotion.positionList.Count; + + AssetDatabase.CreateAsset((RootMotionData)rootmotion, assetPath); + } + } -- cgit v1.1-26-g67d0