标签:
昨天研究了下Unity中AnimatorController自动生成。今天稍微完善了操作流程,并且补充说明了在MMO中如何使用模型资源(AssetBundle),这个使用方法是MMO这样模型资源非常大的情况下才需要的。如果是比较小的情况下,直接塞到Resources目录下,加载Prefab就OK了,根本不用操心这些问题。
先附上最终修改后的脚本。它创建了一个菜单选项,选择一个文件夹(或者文件),右键选择CreateAnimation就可以自动遍历所有的模型资源,然后创建AnimatorController并创建Prefab文件。在Prefab都生成完毕后(或者在生成的同时),可以使用创建AssetBundle的功能在磁盘任意目录创建AssetBundle。
using UnityEngine; using UnityEditor; using UnityEditorInternal; using System.IO; using System.Collections; using System.Collections.Generic; public class CreateAnimation : Editor { //生成出的Prefab的路径 private static readonly string PREFAB_PATH = "Assets/Prefab/"; private static readonly string PATH_TAG = "Assets/"; private static bool _ingnoreExist = true; // 这些动画是循环播放的(除此之外的循环动画需要手动创建) private static readonly string[] LOOP_TAG = new string[] { "idle", "walk", "run" }; [MenuItem("Assets/CreateAnimation")] static void DoCreateAnimation() { UnityEngine.Object obj = Selection.activeObject; string path = AssetDatabase.GetAssetPath(Selection.activeObject); if (Directory.Exists(path)) { // 如果是路径的话 Walk(path, (string fbxPath)=>{ fbxPath = fbxPath.Replace("\\", "/").Replace(".FBX", ".fbx"); string relativePath = fbxPath.Substring(fbxPath.IndexOf(PATH_TAG)); if (!relativePath.EndsWith(".fbx")) { return; } DoCreateController(relativePath); DoCreatePrefab(relativePath); }); } else if (File.Exists(path)) { // 如果是文件的话 string fbxPath = path.Replace("\\", "/").Replace(".FBX", ".fbx"); string relativePath = fbxPath.Substring(fbxPath.IndexOf(PATH_TAG)); if (!relativePath.EndsWith(".fbx")) { return; } DoCreateController(relativePath); DoCreatePrefab(relativePath); } else { // 路径不存在 Debug.LogError(string.Format("路径不存在: {0}", path)); } AssetDatabase.Refresh(); Debug.Log(string.Format("转换完毕: {0}", path)); } // 遍历文件夹 delegate void PathCallBack(string path); static void Walk(string dir, PathCallBack callback) { DirectoryInfo d = new DirectoryInfo(dir); if (d.GetDirectories() != null) { FileInfo[] allf = d.GetFiles(); for (int i = 0; i < allf.Length; i++) { if (callback != null) { callback(allf[i].FullName); } } DirectoryInfo[] alldr = d.GetDirectories(); if (alldr != null) { for (int i = 0; i < alldr.Length; i++) { Walk(alldr[i].FullName, callback); } } } } static void DoCreateController(string assetPath) { // 获取animationclip List<AnimationClip> clips = new List<AnimationClip>(); UnityEngine.Object[] objects = AssetDatabase.LoadAllAssetsAtPath(assetPath); foreach (var obj in objects) { AnimationClip clip = obj as AnimationClip; if (clip != null && clip.name.IndexOf("__preview__") == -1) { clips.Add(clip); } } if (clips.Count <= 0) { return; } // controller文件可以和fbx放在一起 string acPath = assetPath.Replace(".fbx", ".controller"); // 创建动画控制器 AnimatorController animatorController = null; // 更新controller无法及时更新,所以创建controller,但是不能单独prefab,否则会丢失关联 animatorController = AnimatorController.CreateAnimatorControllerAtPath(acPath); AnimatorControllerLayer layer = animatorController.GetLayer(0); UnityEditorInternal.StateMachine sm = layer.stateMachine; Vector3 anyStatePosition = sm.anyStatePosition; float OFFSET_X = 220; float OFFSET_Y = 60; float ITEM_PER_LINE = 4; float originX = anyStatePosition.x - OFFSET_X * (ITEM_PER_LINE / 2.5f); float originY = anyStatePosition.y + OFFSET_Y; float x = originX; float y = originY; State defaultState = null; foreach (AnimationClip newClip in clips) { string clipName = newClip.name.ToLower(); State state = sm.AddState(clipName); // 设置defaultState,优先选取idle动画 if (defaultState == null) { if (clipName.IndexOf("idle") != -1) { sm.defaultState = state; defaultState = state; } } else { if (clipName == "idle") { sm.defaultState = state; defaultState = state; } } foreach (var tag in LOOP_TAG) { //有些动画我希望天生它就动画循环 if (clipName.IndexOf(tag) != -1) { SerializedObject serializedClip = new SerializedObject(newClip); AnimationClipSettings clipSettings = new AnimationClipSettings(serializedClip.FindProperty("m_AnimationClipSettings")); clipSettings.loopTime = true; clipSettings.loopBlend = true; serializedClip.ApplyModifiedProperties(); break; } } state.SetAnimationClip(newClip, layer); state.position = new Vector3(x, y, 0); x += OFFSET_X; if (x >= originX + OFFSET_X * ITEM_PER_LINE) { x = originX; y += OFFSET_Y; } } AssetDatabase.SaveAssets(); } static void DoCreatePrefab(string assetPath) { // 创建对应文件夹 string destPath = assetPath; if (!destPath.StartsWith(PREFAB_PATH)) { destPath = assetPath.Replace("Assets/", PREFAB_PATH); } string dirPath = System.IO.Path.GetDirectoryName(destPath); if (!System.IO.Directory.Exists(dirPath)) { System.IO.Directory.CreateDirectory(dirPath); } string acPath = assetPath.Replace(".fbx", ".controller"); string prefabPath = destPath.Replace(".fbx", ".prefab"); string name = assetPath.Substring(assetPath.LastIndexOf("/") + 1).Replace(".fbx", ""); // 创建prefab GameObject fbx = AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject)) as GameObject; GameObject go = Instantiate(fbx) as GameObject; go.name = name; // 如果controller文件存在,则创建对应的animator if (System.IO.File.Exists(acPath)) { AnimatorController animatorController = AssetDatabase.LoadAssetAtPath(acPath, typeof(AnimatorController)) as AnimatorController; Animator animator = go.GetComponent<Animator>(); if (animator == null) { animator = go.AddComponent<Animator>(); } animator.applyRootMotion = false; animator.runtimeAnimatorController = animatorController; } // 创建prefab if (System.IO.File.Exists(prefabPath)) { // 替换原来的prefab (每个prefab都有对应的udid不能直接使用新的,否则所有相关引用都将失效) GameObject prefab = AssetDatabase.LoadAssetAtPath(prefabPath, typeof(GameObject)) as GameObject; PrefabUtility.ReplacePrefab(go, prefab); } else { // 创建新的prefab PrefabUtility.CreatePrefab(prefabPath, go, ReplacePrefabOptions.ConnectToPrefab); } GameObject.DestroyImmediate(go, true); AssetDatabase.SaveAssets(); } class AnimationClipSettings { SerializedProperty m_Property; private SerializedProperty Get (string property) { return m_Property.FindPropertyRelative(property); } public AnimationClipSettings(SerializedProperty prop) { m_Property = prop; } public float startTime { get { return Get("m_StartTime").floatValue; } set { Get("m_StartTime").floatValue = value; } } public float stopTime { get { return Get("m_StopTime").floatValue; } set { Get("m_StopTime").floatValue = value; } } public float orientationOffsetY { get { return Get("m_OrientationOffsetY").floatValue; } set { Get("m_OrientationOffsetY").floatValue = value; } } public float level { get { return Get("m_Level").floatValue; } set { Get("m_Level").floatValue = value; } } public float cycleOffset { get { return Get("m_CycleOffset").floatValue; } set { Get("m_CycleOffset").floatValue = value; } } public bool loopTime { get { return Get("m_LoopTime").boolValue; } set { Get("m_LoopTime").boolValue = value; } } public bool loopBlend { get { return Get("m_LoopBlend").boolValue; } set { Get("m_LoopBlend").boolValue = value; } } public bool loopBlendOrientation { get { return Get("m_LoopBlendOrientation").boolValue; } set { Get("m_LoopBlendOrientation").boolValue = value; } } public bool loopBlendPositionY { get { return Get("m_LoopBlendPositionY").boolValue; } set { Get("m_LoopBlendPositionY").boolValue = value; } } public bool loopBlendPositionXZ { get { return Get("m_LoopBlendPositionXZ").boolValue; } set { Get("m_LoopBlendPositionXZ").boolValue = value; } } public bool keepOriginalOrientation { get { return Get("m_KeepOriginalOrientation").boolValue; } set { Get("m_KeepOriginalOrientation").boolValue = value; } } public bool keepOriginalPositionY { get { return Get("m_KeepOriginalPositionY").boolValue; } set { Get("m_KeepOriginalPositionY").boolValue = value; } } public bool keepOriginalPositionXZ { get { return Get("m_KeepOriginalPositionXZ").boolValue; } set { Get("m_KeepOriginalPositionXZ").boolValue = value; } } public bool heightFromFeet { get { return Get("m_HeightFromFeet").boolValue; } set { Get("m_HeightFromFeet").boolValue = value; } } public bool mirror { get { return Get("m_Mirror").boolValue; } set { Get("m_Mirror").boolValue = value; } } } }
1、生成AnimatorController和Prefab的时间不快,所以一次转换的模型不要太多。
2、原本可以更新AnimatorController内容会更好些,但是没有尝试成功,所以创建完controller就要更新Prefab,这样关联信息不会丢失(miss animator)
3、创建Prefab的时候,如果已存在,则更新Prefab。这样可以保证其他使用Prefab的地方不会丢失其信息。因为创建一个新的Prefab,则会生成一个新的uuid,这样原来的引用就不存在了。
4、在生成controller的时候,有判断animationclip,如果它是休闲或者跑步动作,则自动设置其为loop模式。这个不在脚本中处理,就需要自己一个模型一个模型去修改。如果不属于LOOP_TAG中的动画,然后又需要设置loop可以自己在Unity中操作修改。这里原本我希望可以通过ModelImporter来修改,但是没有找到正确的方法。所以参考雨松的方式,直接修改AnimationClip的属性。
这种资源(AssetBundle)使用流程有这么几个好处:
1、开发项目工程不会很大。以程序的思路加载资源。否则一个大的MMO,资源量可能几G。运行Unity都需要等几分钟。
2、AssetBundle可以完全由美术搞定,程序只关心资源加载使用。
3、创建AssetBundle的项目可以分类。比如场景美术一个项目,角色美术一个项目等等。这样可以进一步减小项目,并且减少冲突的机会。
标签:
原文地址:http://blog.csdn.net/langresser_king/article/details/43888771