美文网首页
【Unity】把美术导入的动画和模型自动生成为程序所需要的预制体

【Unity】把美术导入的动画和模型自动生成为程序所需要的预制体

作者: 木心Sepith | 来源:发表于2022-05-16 16:20 被阅读0次

    美术导入的资源格式千奇百怪,我们程序需要的预制体一般为标准格式,比如模型放一层,物理放一层,脚本放一层。所以我们需要手动设置一次结构,我做了个自动化工具就可以一键设置好结构了

    美术所给的动画文件

    image.png

    自动生成工具

    image.png

    生成一个程序预制体和一个Animator文件

    image.png

    自动生成的controller文件

    image.png

    自动生成的预制体结构

    image.png

    下面是源码

    using System.Collections;
    using System.Collections.Generic;
    using UnityEditor;
    using UnityEngine;
    
    namespace XFramework.AutoHandleAnimator
    {
        public class AutoHandleAnimatorEditorWindow : EditorWindow
        {
            //动画文件
            public static UnityEngine.Object aniObj;
            //Prefab
            public static UnityEngine.Object prefabObj;
    
            [MenuItem("美术工具/生成程序预制体")]
            public static void OpenWindow()
            {
                GetWindowWithRect<AutoHandleAnimatorEditorWindow>(new Rect(500, 500, 360, 360), false, "生成程序预制体", true);
            }
    
            private void OnGUI()
            {
                GUILayout.BeginVertical();
    
                aniObj = EditorGUILayout.ObjectField("动画文件", aniObj, typeof(UnityEngine.GameObject), false);
                prefabObj = EditorGUILayout.ObjectField("预制体文件", prefabObj, typeof(UnityEngine.GameObject), false);
    
                if (GUILayout.Button("生成"))
                {
                    if (aniObj != null && prefabObj != null)
                    {
                        Generate();
                        base.Close();
                    }
                }
    
                GUILayout.EndVertical();
            }
    
            private void Generate()
            {
                if (!aniObj.name.EndsWith("_Ani"))
                {
                    Debug.LogError("动画文件不以 _Ani 结尾");
                    return;
                }
    
                if (!prefabObj.name.EndsWith("_Skin"))
                {
                    Debug.LogError("预制体文件不以 _Skin 结尾");
                    return;
                }
    
                new AutoHandleAnimatorRequest().Generate(aniObj, prefabObj);
            }
        }
    }
    
    
    using System.Collections;
    using System.Collections.Generic;
    using UnityEditor;
    using UnityEditor.Animations;
    using UnityEngine;
    
    namespace XFramework.AutoHandleAnimator
    {
        public class AutoHandleAnimatorRequest
        {
    
            public void Generate(Object aniObj, Object prefabObj)
            {
                var aniPath = AssetDatabase.GetAssetPath(aniObj);
    
                Debug.Log("正在处理:" + aniPath);
    
                Object[] objs = AssetDatabase.LoadAllAssetsAtPath(aniPath);
    
                List<AnimationClip> clipsList = new List<AnimationClip>();
                for (int i = 0; i < objs.Length; i++)
                {
                    var obj = objs[i];
    
                    if (obj is AnimationClip)
                    {
                        AnimationClip ac = obj as AnimationClip;
                        if (ac.name.Contains("Take 001")) continue;//去除预览片段
                        if (ac.name.Contains("__preview__")) continue;//去除预览片段
    
                        clipsList.Add(ac);
                    }
                }
    
                var prefabName = GetModelName(aniPath);
    
                var aniController = CreateAnimatorController(prefabName, clipsList);
    
                CreatePrefab(prefabName, aniController, prefabObj);
            }
    
            private void CreatePrefab(string prefabName, AnimatorController controller, Object prefabObj)
            {
                GameObject prefabRoot = new GameObject(prefabName);
    
                GameObject modelGo = GameObject.Instantiate(prefabObj as GameObject);
                modelGo.name = prefabObj.name;
                modelGo.transform.parent = prefabRoot.transform;
    
                var animator = modelGo.AddComponent<Animator>();
                animator.cullingMode = AnimatorCullingMode.AlwaysAnimate;
                animator.runtimeAnimatorController = controller;
    
                var targetPath = AutoHandleAnimatorPathSetting.ModelExportPath + prefabName + ".prefab";
    
                //保存角色预制体
                PrefabUtility.SaveAsPrefabAssetAndConnect(prefabRoot, targetPath, InteractionMode.UserAction);
    
                GameObject.DestroyImmediate(prefabRoot);
    
                Debug.Log("create gameobject: " + targetPath);
            }
    
            private AnimatorController CreateAnimatorController(string prefabName, List<AnimationClip> clips)
            {
                var path = AutoHandleAnimatorPathSetting.ModelExportPath + prefabName + "_ani.controller";
    
                AssetDatabase.DeleteAsset(path);
    
                AnimatorController animatorController = AnimatorController.CreateAnimatorControllerAtPath(path);
    
                int rowCount = 8;//一列几个 
                int currentRow = 0;//当前列数
                int currentRowCount = 0;//当前列clip个数
                AnimatorStateMachine baseLayerMachine = animatorController.layers[0].stateMachine;
    
                //空状态
                baseLayerMachine.entryPosition = Vector3.zero;
                baseLayerMachine.anyStatePosition = new Vector3(0f, 200f);
                baseLayerMachine.AddState("null", new Vector3(0, 100f));
                baseLayerMachine.exitPosition = new Vector3(0f, 300f);
    
                //添加片段
                for (int i = 0; i < clips.Count; i++)
                {
                    if (i % rowCount == 0)
                    {
                        currentRow += 1;
                        currentRowCount = 0;
                    }
                    var x = currentRow * 300;
                    var y = currentRowCount * 100;
                    currentRowCount++;
    
                    AnimationClip clip = clips[i];
                    AnimatorState tempState = baseLayerMachine.AddState(clip.name, new Vector3(x, y, 0));
                    tempState.motion = clip;
                    EditorUtility.SetDirty(tempState);
                }
    
                EditorUtility.SetDirty(baseLayerMachine);
                EditorUtility.SetDirty(animatorController);
    
                Debug.Log("create controller: " + path);
    
                return animatorController;
            }
    
            private string GetModelName(string path)
            {
                //女主角_Ani.FBX
                var strs = path.Split('/');
                strs = strs[strs.Length - 1].Split('.');
                strs = strs[0].Split('_');
                var modelName = strs[0];
                return modelName;
            }
        }
    }
    

    路径设置

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    namespace XFramework.AutoHandleAnimator
    {
        public static class AutoHandleAnimatorPathSetting
        {
            /// <summary>
            /// 需要处理的模型根目录
            /// </summary>
            public const string ModelRootPath = "Assets/Arts/模型/角色/";
    
            /// <summary>
            /// 模型动画导出路径
            /// </summary>
            public const string ModelExportPath = "Assets/HotUpdateResources/Prefab/AutoCreateModel/";
        }
    }
    

    最后再附带一个动画预览小工具

    image.png
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEditor;
    using System.Linq;
    
    [ExecuteAlways]
    [CustomEditor(typeof(Animator), true)]//bool 子类是否可用
    public class AniEditor : Editor
    {
        //目标组件
        private Animator animator;
        //运行时状态机控制器
        private RuntimeAnimatorController controller;
        //动画片段
        private AnimationClip[] clips;
        //选中的片段下标
        private int curIndex;
        //模拟时间
        private float timer;
        //上一帧时间
        private float lastFrameTime;
        //是在播放中
        private bool isPlaying;
    
        public void OnEnable()
        {
            Init();
        }
    
        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            isPlaying = false;
            curIndex = 0;
            timer = 0f;
            animator = target as Animator;
            controller = animator.runtimeAnimatorController;
    
            if (controller)
            {
                clips = controller.animationClips;
            }
            else
            {
                clips = new AnimationClip[0];
            }
        }
    
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();
    
            if (clips.Length == 0)
            {
                EditorGUILayout.HelpBox("没有动画片段!", MessageType.Warning);
                return;
            };
    
            //检查代码块中是否有任何控件被更改
            EditorGUI.BeginChangeCheck();
    
            GUILayout.BeginHorizontal();
            GUILayout.Space(10);
            GUILayout.Label("===========");
            GUILayout.Label("<color=#00F5FF>动画预览</color>", new GUIStyle() { richText = true, fontStyle = FontStyle.Normal, fontSize = 16 });
            GUILayout.Label("===========");
            GUILayout.Space(10);
            GUILayout.EndHorizontal();
    
    
            GUILayout.Label("当前片段:");
            //创建一个通用的弹出式选择字段。
            curIndex = EditorGUILayout.Popup(curIndex, clips.Select(p => p.name).ToArray()); //还原clip状态
            AnimationClip clip = clips[curIndex];
    
            GUILayout.Label("播放长度:");
            timer = EditorGUILayout.Slider(timer, 0, clip.length);
            string playAniBtnStr = isPlaying ? "暂停" : "播放动画";
    
            if (GUILayout.Button(playAniBtnStr))
            {
                SetPlayAni(timer >= clip.length);
            }
    
            if (GUILayout.Button("返回到当前片段第一帧"))
            {
                isPlaying = false;
                timer = 0;
                clip.SampleAnimation(animator.gameObject, timer);
            }
    
            if (isPlaying)
            {
                timer += Time.realtimeSinceStartup - lastFrameTime;
                if (timer >= clip.length)
                {
                    if (clip.isLooping)
                    {
                        timer = 0;
                    }
                    else
                    {
                        isPlaying = false;
                        timer = clip.length;
                    }
                }
            }
    
            //重新绘制显示此编辑器的检查器。
            Repaint();
            lastFrameTime = Time.realtimeSinceStartup;
            if (EditorGUI.EndChangeCheck() || isPlaying)
            {
                clip.SampleAnimation(animator.gameObject, timer);
            }
        }
    
        /// <summary>
        /// 播放动画
        /// </summary>
        /// <param name="rePlay"></param>
        private void SetPlayAni(bool rePlay)
        {
            if (rePlay)
            {
                timer = 0f;
                isPlaying = true;
            }
            else
            {
                isPlaying = !isPlaying;
            }
        }
    
    
    }
    
    

    相关文章

      网友评论

          本文标题:【Unity】把美术导入的动画和模型自动生成为程序所需要的预制体

          本文链接:https://www.haomeiwen.com/subject/earwurtx.html