• Split Animation Clip From FBX and Multiply Mode Sprite


    Use Script To Creat 2D Animation Clip From Multiply Mode Sprite

    很多时候美工拿过来的一张序列帧图片,我们需要转换成 Multiply Mode,切片生成动画,然后绑定Prefab,可以考虑些一个脚本省略重复劳动。直接上代码

    结果:

    Main Procede

    using UnityEngine;
    using UnityEditor;
    using UnityEditorInternal; ///AnimatorController
    public static string Altas_FrameConfigAssetPath = "Assets/test_frameConfig.txt";
    [MenuItem("MyTest/Test")]
    public static void Test()
    {
        ///Get sprites
        Sprite[] sprites
            = AssetDatabase.LoadAllAssetRepresentationsAtPath(Altas_AssetPath).Select(x => x as Sprite).ToArray();
    
        ///Get frame config
        ///run:0-11
        ///idle:12-21
        ///charge:22-33
        ///attack:34-49
        ///dead:50-63
        TextAsset textAsset = AssetDatabase.LoadAssetAtPath(Altas_FrameConfigAssetPath, typeof(TextAsset)) as TextAsset;
        string[] configs = textAsset.text.Split(new string[] { "
    " }, StringSplitOptions.RemoveEmptyEntries);
    
    
        ///Create clips
        List<AnimationClip> clips = new List<AnimationClip>();
        foreach (string curConfig in configs)
        {
            /// run:0-11
            string clipName = curConfig.Split(":"[0])[0];
            string length = curConfig.Split(":"[0])[1];
    
            /// 0-11
            int startIndex = int.Parse(length.Split("-"[0])[0]);
            int endIndex = int.Parse(length.Split("-"[0])[1]);
    
            Sprite[] curSprites = sprites.Where((t, index) => index >= startIndex && index <= endIndex).ToArray();
    
            AnimationClip curClip = BuildAnimationClip(curSprites, "Assets/" + clipName + ".anim", true);
            clips.Add(curClip);
        }
    
        ///Create AnimatorController with clips
        AnimatorController curAnimatorController = BuildAnimationController(clips, "Assets/test.controller");
    
        ///Create Prefab add AnimatorController
        BuildPrefab(curAnimatorController, "Assets/test.prefab");
    }
    

    BuildAnimationClip

    static AnimationClip BuildAnimationClip(Sprite[] Sprites, string AssetPath, bool IsNeedLoop)
    {
        AnimationClip clip = new AnimationClip();
    
        /// Setting it as generic allows you to use the animation clip in the animation controller
        /// public enum ModelImporterAnimationType
        /// {
        ///    None = 0,
        ///    Legacy = 1,
        ///    Generic = 2,
        ///    Human = 3,
        ///}
        AnimationUtility.SetAnimationType(clip, ModelImporterAnimationType.Generic);
    
    
        /// First you need to create e Editor Curve Binding
        EditorCurveBinding curveBinding = new EditorCurveBinding();
        /// I want to change the sprites of the sprite renderer, so I put the typeof(SpriteRenderer) as the binding type.
        curveBinding.type = typeof(SpriteRenderer);
        /// Regular path to the gameobject that will be changed (empty string means root)
        curveBinding.path = "";
        curveBinding.propertyName = "m_Sprite";
    
        /// An array to hold the object keyframes
        ObjectReferenceKeyframe[] keyFrames = new ObjectReferenceKeyframe[Sprites.Length];
    
        ///动画长度是按秒为单位,1/10就表示1秒切10张图片,根据项目的情况可以自己调节
        float frameTime = 1 / 10f;
    
        for (int i = 0; i < Sprites.Length; i++)
        {
            keyFrames[i] = new ObjectReferenceKeyframe();
            keyFrames[i].time = frameTime * i;
            keyFrames[i].value = Sprites[i];
        }
    
        ///动画帧率,30比较合适 
        ///也就是说1秒 切成 30份,然后 每3份 切换一次图片
        ///如果动态修改clip.frameRate = 15 
        ///就是说1秒 切成 15份,然后 每3份 切换一次图片, 但是每一份的时间加倍了
        clip.frameRate = 30;
    
        //set AnimationClip loop
    
        //clip.wrapMode = WrapMode.Loop; //no use
    
        //UnityEditor.AnimationClipSettings clipSettings = AnimationUtility.GetAnimationClipSettings(clip); //no use
        //clipSettings.loopTime = true;
    
        if (IsNeedLoop) //use
        {
            SerializedObject serializedClip = new SerializedObject(clip);
            AnimationClipSettings clipSettings = new AnimationClipSettings(serializedClip.FindProperty("m_AnimationClipSettings"));
            clipSettings.loopTime = true;
            serializedClip.ApplyModifiedProperties();
        }
    
        AnimationUtility.SetObjectReferenceCurve(clip, curveBinding, keyFrames);
    
        ///Create animation
        AssetDatabase.CreateAsset(clip, AssetPath);
        AssetDatabase.SaveAssets();
    
        return clip;
    }
    

    BuildAnimationController

    static AnimatorController BuildAnimationController(List<AnimationClip> clips, string AssetPath)
    {
        AnimatorController animatorController = AnimatorController.CreateAnimatorControllerAtPath(AssetPath);
        AnimatorControllerLayer layer = animatorController.GetLayer(0);
        UnityEditorInternal.StateMachine sm = layer.stateMachine;
        foreach (AnimationClip newClip in clips)
        {
            State state = sm.AddState(newClip.name);
            state.SetAnimationClip(newClip, layer);
            //Transition trans = sm.AddAnyStateTransition(state);
            //trans.RemoveCondition(0);
        }
    
        AssetDatabase.SaveAssets();
    
        return animatorController;
    }
    

    BuildPrefab

    static void BuildPrefab(AnimatorController animatorCountorller, string AssetPath)
    {
        GameObject go = new GameObject();
        go.name = "test";
        SpriteRenderer spriteRender = go.AddComponent<SpriteRenderer>();
        Animator animator = go.AddComponent<Animator>();
    
        animator.runtimeAnimatorController = animatorCountorller;
    
        PrefabUtility.CreatePrefab(AssetPath, go);
    }
    

    AnimationClipSettings.cs

    public 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; } }
    }
    

    参考:

    Unity3D研究院之Machine动画脚本自动生成AnimatorController(七十一)

    Unity2D研究院之自动生成动画、AnimationController、Prefab(一)

    lack-of-scripting-functionality-for-creating-2d-animation-clips-by-code

    accessing-loop-time-through-c

    can-mecanim-animation-clip-properties-be-edited-in-script

    Use Script To Split ModelImporterClipAnimation From FBX

    同理,有些美工导出的FBX只有一个动画,但是带有帧数说明,需要我们写个脚本自动切

    结果:

    public class mTest2 : AssetPostprocessor
    {
        public static string FBX_FrameConfigAssetPath = "Assets/test2/test2_frameConfig.txt";
        
        //Add this function in a subclass to get a notification when a model has completed importing.
        public void OnPostprocessModel(GameObject go)
        {
            Debug.Log(assetPath);
    
            ///Get frame config
            ///run:0-20
            ///idle:21-31
            ///charge:32-42
            ///attack:43-58
            ///dead:59-75
            TextAsset textAsset = AssetDatabase.LoadAssetAtPath(FBX_FrameConfigAssetPath, typeof(TextAsset)) as TextAsset;
            string[] configs = textAsset.text.Split(new string[] { "
    " }, StringSplitOptions.RemoveEmptyEntries);
    
            ///Model importer lets you modify model import settings from editor scripts.
            ModelImporter modelImporter = assetImporter as ModelImporter;
    
            List<ModelImporterClipAnimation> clips = new List<ModelImporterClipAnimation>();
            foreach (string curConfig in configs)
            {
                clips.Add(ParseAnimationClip(curConfig));
            }
    
            modelImporter.clipAnimations = clips.ToArray();
    
            ///Import any changed assets.
            ///call OnPostprocessModel twice cause call once nerver show split anim clips on FBX in project view 
            AssetDatabase.Refresh();
        }
    
    
        static ModelImporterClipAnimation ParseAnimationClip(string curConfig)
        {
            ///run:0-20
            string clipName = curConfig.Split(":"[0])[0];
            string length = curConfig.Split(":"[0])[1];
    
    
            /// 0-20
            int startIndex = int.Parse(length.Split("-"[0])[0]);
            int endIndex = int.Parse(length.Split("-"[0])[1]);
    
            ModelImporterClipAnimation clip = new ModelImporterClipAnimation();
            clip.firstFrame = Convert.ToSingle(startIndex);
            clip.lastFrame = Convert.ToSingle(endIndex);
            clip.loopTime = true;
            clip.name = clipName;
    
            return clip;
        }
    }
    

    参考:

    how-to-split-animations-via-script-in-editor

    项目工程源码

    Split Animation Tool

  • 相关阅读:
    高性能IO模型浅析
    使用vbs脚本进行批量编码转换
    Linux模块机制浅析
    源文件移动后gdb不显示代码的原因
    Linux的原子操作与同步机制
    ARM的常数表达式
    安装卡巴 OFFICE链接 出现这个过程被中断,由于本机的限制
    selenium “could not be scrolled into view”
    bs4 FeatureNotFound: Couldn't find a tree builder with the features you requested: lxml. Do you need to install a parser library?
    vim实现实时自动保存
  • 原文地址:https://www.cnblogs.com/chongxin/p/4546451.html
Copyright © 2020-2023  润新知