using System; using System.IO; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; public class RootMotionEditor : EditorWindow { static string unitFolder; static string unitAnimationDataFolder { get { return unitFolder + "AnimationData/"; } } static string unitRootMotionFolder { get { return unitFolder + "RootMotion/"; } } static string unitAnimationClipFolder { get { return unitFolder + "AnimationClip/"; } } static string unitDataFolder { get { return unitFolder + "UnitData/"; } } // 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("Erika/RootMotion/Create")] static void OpenTool() { RootMotionEditor editor = GetWindow(); } private void OnEnable() { titleContent = new GUIContent("RootMotion Editor"); m_SelectAnimations = new HashSet(); } private void OnDisable() { } 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) { string path = AssetDatabase.GetAssetPath(prefab); string folder = Path.GetDirectoryName(path).Replace('\\', '/'); folder = folder.Substring(0, folder.LastIndexOf('/')); unitFolder = folder + "/"; 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 = unitAnimationClipFolder; 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", "目录 " + unitRootMotionFolder + m_Prefab.name, "OK"); } void ExportRootMotion(string animation) { string animPath = unitAnimationClipFolder + animation + ".anim"; string assetPath = unitRootMotionFolder + 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; sampleTime += kDeltaTime; if(sampleTime >= sampleDuration) { Vector3 pos = m_Transform.position; pos.x = 0; //pos.y = pos.y < 0 ? 0 : pos.y; rootmotion.positionList.Add(pos); sampleTime = sampleTime - sampleDuration; } if (t == timeLen) break; t += kDeltaTime; if (t > timeLen) t = timeLen; } rootmotion.frameCount = rootmotion.positionList.Count; AssetDatabase.CreateAsset((RootMotionData)rootmotion, assetPath); } }