🐙

[Unity]Trilib2でゲームの実行時にFbxのアニメーションをロードする

2021/11/12に公開

Trilib2を使用してUnityでゲームの実行時にFbxのアニメーションをロードするメモです。

  • Unity 2020.3
  • Trilib2
  • MixamoでダウンロードしたFbxファイルからモデルとアニメーションをロードする
  • 任意のHumanoidモデルにロードしたMixamoのアニメーションを適用する

Trilib2について

Unityは、Editorでは簡単にFbxをロードできますが、ゲームの実行中にFbxをロードする方法は提供されていません。
ゲームの実行中にFbxをロードする場合、Trilib2という有料アセットを使用することで簡単に実現することができます。

公式サイト
https://ricardoreis.net/trilib-2/

アセットストア
https://assetstore.unity.com/packages/tools/modeling/trilib-2-model-loading-package-157548?locale=ja-JP

Trilib2の使い方に関しては、以下のQiitaの記事が非常に参考になります。
https://qiita.com/jyouryuusui/items/46e70da01e4281864cee

ファイルパスを指定してFbxファイルを読み込む

AssetLoader.LoadModelFromFileメソッドで指定したパスからモデルをロードします。
Materialまでロードが完了した後、OnMaterialsLoadメソッドがコールバックで呼び出されます。
読み込んだモデルは、AssetLoaderContext.RootGameObjectから参照できます。

MyFbxLoader.cs
using TriLibCore;

public class MyFbxLoader : MonoBehaviour
{
    public void Load(string loadPath)
    {
        var asetLoaderOptions = AssetLoader.CreateDefaultLoaderOptions();
        AssetLoader.LoadModelFromFile(loadPath, null, OnMaterialsLoad, null, null, null, assetLoaderOptions);
    }

    private void OnMaterialsLoad(AssetLoaderContext context)
    {
        var loadedGameObject = context.RootGameObject;

        // goを色々処理
    }
}

Humanoid対応のモデルのAvatarを読み込む

Humanoid対応のモデルを読み込む場合、AssetLoaderOptionsを適切に設定することで、自動的にAvatarを構成してくれるようになります。

  • assetLoaderOptions.AnimationTypeAnimationType.Humanoidに設定
  • assetLoaderOptions.HumanoidAvatarMapperにモデルと対応したHumanoidAvatarMapperを指定

Mixamoからダウンロードしたモデルをロードする場合、MixamoAndBipedByNameHumanoidAvatarMapperというマッパーがデフォルトで含まれているため、こちらを使用することができます。

MyFbxLoader.cs
using TriLibCore;
using TriLibCore.General;
using TriLibCore.Mappers;

// ~~ 中略 ~~

// MixamoAndBipedByNameHumanoidAvatarMapperを参照しておく
[SerializeField]
private HumanoidAvatarMapper avatarMapper;

private void Load(string loadPath)
{
    var asetLoaderOptions = AssetLoader.CreateDefaultLoaderOptions();
    
    // AnimationTypeをHumanoidに設定
    assetLoaderOptions.AnimationType = AnimationType.Humanoid;
    
    // HumanoidAvatarMapperを設定
    assetLoaderOptions.HumanoidAvatarMapper = avatarMapper;
    
    AssetLoader.LoadModelFromFile(loadPath, null, OnMaterialsLoad, null, null, null, assetLoaderOptions);
}

private void OnMaterialsLoad(AssetLoaderContext context)
{
    var loadedGameObject = context.RootGameObject;
    
    // Animator経由でAvatarを取得
    var animator = loadedGameObject.GetComponent<Animator>();
    var avatar = animator.avatar;
}

Fbx内のアニメーションを読み込む

Trilib2は、残念ながら動的にMecanimのアニメーションをインポートすることはできません。
公式のKnown Issues/LimitationsのページにTriLib can't import Mecanim Animation Clips due to a Unity limitation.と記載があり、Unityの制限によりMecanimアニメーションはインポートできないそうです。

https://ricardoreis.net/trilibwiki/index.php?title=Known_Issues/Limitations

そのため、アニメーションを再生する場合は、以下の設定を行い、レガシーアニメーションとしてロードする必要があります。

  • assetLoaderOptions.AnimationTypeAniamtionType.Legacyを指定
MyFbxLoader.cs
using TriLibCore;
using TriLibCore.General;
using TriLibCore.Extensions;

// ~~ 中略 ~~

private void Load(string loadPath)
{
    var asetLoaderOptions = AssetLoader.CreateDefaultLoaderOptions();
    
    // AnimationTypeにLegacyを設定
    assetLoaderOptions.AnimationType = AnimationType.Legacy;
    
    AssetLoader.LoadModelFromFile(loadPath, null, OnMaterialsLoad, null, null, null, assetLoaderOptions);
}

private void OnMaterialsLoad(AssetLoaderContext context)
{
    var loadedGameObject = context.RootGameObject;
    
    // Animation経由でレガシーアニメーションのクリップを取得
    var animation = loadedGameObject.GetComponent<Animation>();

    // GetAllAnimationClipsはTriLibCore.Extensionsに定義されている
    // 全てのアニメーションクリップを取得する
    var clips = animation.GetAllAnimationClips();
}

読み込んだアニメーションを(無理やり)他のHumanoidモデルに適用する

Trilib2で読み込んだアニメーションはレガシーのため、基本的に他のHumanoidモデルに適用することはできません。
ただ、動かすだけであれば、以下の方法で無理やり動かすことはできました。

  • Humanoid対応のモデルをAvatar付きで読み込む
  • Humanoid対応のモデルのAnimationを読み込む
  • Avatar付きのモデル上でAnimationを再生する
  • Avatar付きのモデルのHumanPoseを、アニメーションを適用したいモデルのHumanPoseにコピーする

HumanPose経由でポーズを複製することで、それぞれのHumanoidモデルに合わせた状態でモデルを動かすことができます。
元のモデルを動かしておく必要があるので、使えるケースは限られますが、何でもいいから動けばいい場合であれば、一応使えるかなと思います。

Avatar付きのモデル上でAnimationを再生する

AvatarGameObjectとAnimationGameObjectにそれぞれAvatarとAnimationが読み込まれている状態で、AvatarGameObjectにAnimationを複製します。

MyFbxLoader.cs
var animator = AvatarGameObject.GetComponent<Animator>();

// AnimatorはOFFにしておく
animator.enabled = false;

// Avatar側にAnimationを追加
var animation = AvatarGameObject.AddComponent<Animation>();

// 作成したAnimationにクリップを追加
foreach (var clip in clips)
{
	// ループする場合
	clip.wrapMode = WrapMode.Loop;
	animation.AddClip(clip, clip.name);
}

// クリップを指定してアニメーションを再生
animation.clip = clips[0];
animation.Play();

Update内でHumanPoseを複製する

アニメーションを適用したいモデルに下記のスクリプトを適用し、srcAnimatorにアバターをロードしたモデルを格納します。
アバターをロードしたモデルでアニメーションを再生すると、このモデル上でも一緒にアニメーションが動作します。

public class HumanPoseCopier : MonoBehaviour
{
	// 
	[SerializeField]
	private Animator srcAnimator;
	
	private Animator animator;
	private HumanPoseHandler handler;
	private HumanPoseHandler srcHandler;
	private HumanPose humanPose;
	
	private void Start()
	{
		animator = GetComponent<Animator>();
		handler = new HumanPoseHandler(animator.avatar, transform);
		srcHandler = new HumanPoseHandler(srcAnimator.avatar, srcAnimator.transform);
	}
	
	private void Update()
	{
		if (srcAnimator == null) return;
		
		srcHandler.GetHumanPose(ref humanPose);
		handler.SetHumanPose(ref humanPose);
	}
}

後記

他のモデルへのアニメーションの適用は、とりあえず動く……という程度なので、もう少しマシな方法を模索したいところです。

Discussion