FBXに含まれるAnimation Clipの一括変換 (Unity)

以前以下の記事を書いたのだが、一つ一つ同じ設定をしてたら時間が足りないので一括で設定を変更できるコードを書いた qiita.com

使用条件

  • MixamoでダウンロードしたアニメーションのFBXであること
  • Assets/Resources/Avatar/直下にMixamoでダウンロードしたキャラクターのFBXが存在すること、またファイル名がcharacterであること
  • characterの設定を以下のように変更すること
    Mixamo_character_rig

今回MixamoのアニメーションをVRMで再生するときに足の向きがおかしくなる現象に遭遇したのでそれの対応も含めている 以下のサイトを参考にさせていただきました

MixamoアニメーションがUnityで動かない?足がおかしくなる?時の対処法【アバターの適用が重要】 | YouDoYou Blog ~スマートかつ快適に創造を楽しむ~

解決法としてはMixamoでダウンロードしたアニメーションのFBXのRigに対してMixamoで出力したキャラクターについているAvaterを設定することで正しく動く

使用方法

設定を変更したいFBXを選択してメニューバーにあるAssets/Set Animation Optionsを選択することで変更可能

Assets/Set LoopではアニメーションクリップのLoop Timeにチェックを入れることができる

using UnityEngine;
using UnityEditor;

public class AnimationProcessor : AssetPostprocessor
{
    static void SetAnimationImporterSettings(ModelImporter importer)
    {
        //rigの設定
        importer.animationType = ModelImporterAnimationType.Human;
        importer.avatarSetup = ModelImporterAvatarSetup.CopyFromOther;
        var avatar = Resources.Load<GameObject>("Avatar/character");
        if (avatar != null)
        {
            importer.sourceAvatar = avatar.GetComponent<Animator>().avatar;
        }
        //animationClipの設定
        var clips = importer.clipAnimations;

        if (clips.Length == 0) clips = importer.defaultClipAnimations;

        foreach (var clip in clips)
        {
            
            clip.name = System.IO.Path.GetFileNameWithoutExtension(importer.assetPath);
            clip.keepOriginalOrientation = true;
            clip.keepOriginalPositionY = true;
            clip.keepOriginalPositionXZ = true;

            clip.lockRootRotation = true;
            clip.lockRootHeightY = true;
            clip.lockRootPositionXZ = false;
            
            
            clip.lockRootHeightY = true;
            clip.heightFromFeet = false;
            clip.keepOriginalPositionY = true;
            
            clip.heightOffset = 0f;
            clip.cycleOffset = 0f;
            clip.rotationOffset = 0f;
        }

        importer.clipAnimations = clips;
    }
    
    static void SetAnimationLoopSettings(ModelImporter importer)
    {
        //animationClipの設定
        var clips = importer.clipAnimations;

        if (clips.Length == 0) clips = importer.defaultClipAnimations;

        foreach (var clip in clips)
        {
            clip.loop = true;
        }
        importer.clipAnimations = clips;
    }

    void OnPreprocessAnimation()
    {
        // 自動的に実行
        //SetAnimationImporterSettings(assetImporter as ModelImporter);
    }

    [MenuItem("Assets/Set Animation Options", true)]
    static bool SetAnimationOptionsValidate()
    {
        return Selection.GetFiltered(typeof(GameObject), SelectionMode.Assets).Length > 0;
    }

    [MenuItem("Assets/Set Animation Options")]
    static void SetAnimationOptions()
    {
        var filtered = Selection.GetFiltered(typeof(GameObject), SelectionMode.Assets);
        foreach (var go in filtered)
        {
            var path = AssetDatabase.GetAssetPath(go);
            var importer = AssetImporter.GetAtPath(path);
            SetAnimationImporterSettings(importer as ModelImporter);
            AssetDatabase.ImportAsset(path);
        }

        Selection.activeObject = null;
    }
    
    [MenuItem("Assets/Set Loop", true)]
    static bool SetLoopValidate()
    {
        return Selection.GetFiltered(typeof(GameObject), SelectionMode.Assets).Length > 0;
    }

    [MenuItem("Assets/Set Loop")]
    static void SetLoop()
    {
        var filtered = Selection.GetFiltered(typeof(GameObject), SelectionMode.Assets);
        foreach (var go in filtered)
        {
            var path = AssetDatabase.GetAssetPath(go);
            var importer = AssetImporter.GetAtPath(path);
            SetAnimationLoopSettings(importer as ModelImporter);
            AssetDatabase.ImportAsset(path);
        }

        Selection.activeObject = null;
    }
}