码迷,mamicode.com
首页 > 编程语言 > 详细

Unity模型资源使用流程

时间:2015-02-20 14:08:47      阅读:403      评论:0      收藏:0      [点我收藏+]

标签:

        昨天研究了下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的项目可以分类。比如场景美术一个项目,角色美术一个项目等等。这样可以进一步减小项目,并且减少冲突的机会。

Unity模型资源使用流程

标签:

原文地址:http://blog.csdn.net/langresser_king/article/details/43888771

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!