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[] { "\r\n" }, 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[] { "\r\n" }, 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

posted @ 2015-06-02 14:45  灵魂重新  阅读(1699)  评论(0编辑  收藏  举报