zoukankan      html  css  js  c++  java
  • Unity3D读取模型文件自动生成AnimatorController简单实例

    前几天接到一个任务,做一个导入、控制模型动画的工具类,没有太具体的要求,于是就自行思考实际需求,最终根据宣雨松老师的一篇博客,自己规范了一下写了一个工具类。相关工具代码及测试用例已上传至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。因和其贴图一起放置在 AssetsResourcesfbxwukong目录中,然后改名为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

    如有问题请指正,谢谢!

  • 相关阅读:
    Java中@Override的作用
    微软面试题: LeetCode 152. 乘积最大子数组 出现次数:2
    微软面试题: LeetCode 300. 最长递增子序列 出现次数:2
    微软面试题: LeetCode 76. 最小覆盖子串 出现次数:2
    微软面试题:剑指 Offer 52. 两个链表的第一个公共节点 出现次数:2
    微软面试题: LeetCode 79. 单词搜索 出现次数:2
    微软面试题: LeetCode 39. 组合总和 出现次数:2
    微软面试题: LeetCode 151. 翻转字符串里的单词 出现次数:2
    微软面试题: LeetCode 415. 字符串相加 出现次数:2
    微软面试题: LeetCode 110. 平衡二叉树 出现次数:2
  • 原文地址:https://www.cnblogs.com/yaoh/p/5149568.html
Copyright © 2011-2022 走看看