前幾天接到一個任務,做一個導入、控制模型動畫的工具類,沒有太具體的要求,於是就自行思考實際需求,最終根據宣雨松老師的一篇博客,自己規范了一下寫了一個工具類。相關工具代碼及測試用例已上傳至Github。
https://github.com/hcy12321/UnityAnimatorControllerMaker
該demo需在導入Unity后執行菜單Tools/CreateAnimator項后再執行。
1.需求及規范
需求是指實際使用時需要實現的地方,規范是最終使用這套工具需要遵守的規則。
1.1 實際需求
1. 能自動遍歷fbx文件,且生成對應的AnimatorController文件。
2. 能獲取fbx文件中所有的動畫片段(AnimationClip),並存入第一步生成的AnimatorController的狀態機中。
3.(個人假設需求)狀態機默認指向一個空的動畫。
1.2 使用規范
1. 所有fbx文件需放置在Assets/Resources/fbx目錄(該目錄可在代碼中更改)中的子目錄中,該子目錄以fbx名稱(不可包含中文)命名,將fbx文件和貼圖放到該子目錄中,然后將fbx文件重命名為原名_model。
如:wukong.fbx。因和其貼圖一起放置在 Assets\Resources\fbx\wukong目錄中,然后改名為wukong_model.fbx。
2.功能實現
該部分主要介紹邏輯功能代碼
2.1 生成菜單方法
在Editor目錄下添加類文件AnimatorTool.cs,該類共有三個方法:
void CreateAnimationAssets(): 工具菜單方法,內有遍歷目錄生成動畫控制器、生成預設的邏輯
using System; using UnityEngine; using System.Collections; using System.IO; using System.Linq; using UnityEditor; using UnityEditor.Animations; public class AnimatorTool : MonoBehaviour { /// <summary> /// 菜單方法,遍歷文件夾創建Animation Controller /// </summary> [MenuItem("Tools/CreateAnimator")] static void CreateAnimationAssets() { string rootFolder = "Assets/Resources/fbx/"; if (!Directory.Exists(rootFolder)) { Directory.CreateDirectory(rootFolder); return; } // 遍歷目錄,查找生成controller文件 var folders = Directory.GetDirectories(rootFolder); foreach (var folder in folders) { DirectoryInfo info = new DirectoryInfo(folder); string folderName = info.Name; // 創建animationController文件 AnimatorController aController = AnimatorController.CreateAnimatorControllerAtPath(string.Format("{0}/animation.controller", folder)); // 得到其layer var layer = aController.layers[0]; // 綁定動畫文件 AddStateTranstion(string.Format("{0}/{1}_model.fbx", folder, folderName), layer); // 創建預設 GameObject go = LoadFbx(folderName); PrefabUtility.CreatePrefab(string.Format("{0}/{1}.prefab", folder, folderName), go); DestroyImmediate(go); } } /// <summary> /// 添加動畫狀態機狀態 /// </summary> /// <param name="path"></param> /// <param name="layer"></param> private static void AddStateTranstion(string path, AnimatorControllerLayer layer) { AnimatorStateMachine sm = layer.stateMachine; // 根據動畫文件讀取它的AnimationClip對象 var datas = AssetDatabase.LoadAllAssetsAtPath(path); if (datas.Length == 0) { Debug.Log(string.Format("Can't find clip in {0}", path)); return; } // 先添加一個默認的空狀態 var emptyState = sm.AddState("empty"); sm.AddAnyStateTransition(emptyState); // 遍歷模型中包含的動畫片段,將其加入狀態機中 foreach (var data in datas) { if (!(data is AnimationClip)) continue; var newClip = data as AnimationClip; if (newClip.name.StartsWith("__")) continue; // 取出動畫名字,添加到state里面 var state = sm.AddState(newClip.name); state.motion = newClip; // 把State添加在Layer里面 sm.AddAnyStateTransition(state); } } /// <summary> /// 生成帶動畫控制器的對象 /// </summary> /// <param name="name"></param> /// <returns></returns> public static GameObject LoadFbx(string name) { var obj = Instantiate(Resources.Load(string.Format("fbx/{0}/{0}_model", name))) as GameObject; obj.GetComponent<Animator>().runtimeAnimatorController = Resources.Load<RuntimeAnimatorController>(string.Format("fbx/{0}/animation", name)); return obj; } }
2.2 測試用例
測試用例中主要包含如何調用播放動畫、暫停動畫、重播動畫等功能。
using UnityEngine; using System.Collections; public class AnimatorTest : MonoBehaviour { private Animator animator; public string animationName = "run"; public float Speed = 1.0f; // Use this for initialization void Start() { animator = GetComponent<Animator>(); } /// <summary> /// 添加一些測試功能按鈕 /// </summary> void OnGUI() { #if UNITY_EDITOR if (GUILayout.Button("Play")) { Play(animationName); } if (GUILayout.Button("Replay")) { RePlay(animationName); } if (GUILayout.Button("Pause")) { Pause(); } #endif } /// <summary> /// 設置速度 /// </summary> /// <param name="speed"></param> public void SetSpeed(float speed) { Speed = speed; } /// <summary> /// 重新播放指定名稱動畫 /// </summary> /// <param name="name"></param> public void RePlay(string name) { animator.speed = Speed; animator.Play(name, 0, 0.0f); } /// <summary> /// 播放指定名稱動畫 /// </summary> /// <param name="name"></param> public void Play(string name) { animator.speed = Speed; animator.Play(name); } /// <summary> /// 暫停動畫 /// </summary> public void Pause() { animator.speed = 0.0f; } }
3.總結
該工具在項目實際使用中還有許多待優化的地方,如只考慮了一個模型,如果是一個人物有多個模型那么還要根據需求再重新設計。還有狀態機中還沒有根據模型直接生成結構動畫、序列動畫的功能,需要在以后繼續改進。
本工具參考了宣雨松老師的一篇博客:http://www.xuanyusong.com/archives/2811
如有問題請指正,謝謝!
