【Unity】Model ImporterでSource Avatarが更新された時にUpdateボタンを押す処理を自動化する

Model ImporterでSource Avatarが更新された時にUpdateボタンを押す処理を自動化する方法です。

Unity2018.2.0f2

問題

今モデルとアニメーションを別のFBXで書き出しているとします。
Avatarの設定を使いまわすために、アニメーションFBXのRigのAvatarにモデルのFBXのAvatarを設定します。

f:id:halya_11:20181204194641p:plain
ユニティちゃんのダメージアニメーションにモデルのAvatarを設定

ここで、モデルFBXのAvatarが更新されたとします。
今回は試しにHumanoidのNeckをnullにしてみました。

f:id:halya_11:20181204194848p:plain

そしてアニメーションFBXを見ると、Avatarの横にUpdateボタンが現れています。
これを押さないとAvatarの設定が更新されないようです。

f:id:halya_11:20181204194953p:plain

一個一個Updateボタンを押していくのは人間の仕事ではないのでスクリプトからUpdateできるようにします。

ソースコード

Model ImporterにUpdate用のメソッドがあれば話は簡単なのですがどうやら公開されていないようです。
色々試した結果、次のように一度Avatarの参照を外してから戻すことで更新されました。

/// <summary>
/// SourceのAvatarに加わった変更を適用する
/// </summary>
public static void UpdateAvatar(ModelImporter importer)
{
    if (importer.sourceAvatar == null) {
        return;
    }
        
    var tmpAvatar               = importer.sourceAvatar;
    importer.sourceAvatar       = null;
    importer.SaveAndReimport();
    importer.sourceAvatar       = tmpAvatar;
    importer.SaveAndReimport();
}

モデルFBXのAvatarを参照しているものを一括更新する

元となるFBXのAvatarを更新したときに関連するFBXをすべて更新したいときには次のようにしたらよさそうです。

/// <summary>
/// FBXのパスを与えるとそのFBXのAvatarを参照しているModelImporterのSource Avatarを更新する
/// </summary>
public static void UpdateReferencedAvatars(string fbxAssetPath)
{
    var avatar          = AssetDatabase
        .LoadAllAssetsAtPath(fbxAssetPath)
        .Where(x => x is Avatar)
        .OfType<Avatar>()
        .FirstOrDefault();

    if (avatar == null) {
        return;
    }

    var modelAssetPaths = AssetDatabase
        .FindAssets("t:model")
        .Select(x => AssetDatabase.GUIDToAssetPath(x));

    foreach (var modelAssetPath in modelAssetPaths) {
        var modelImporter   = AssetImporter.GetAtPath(modelAssetPath) as ModelImporter;
        UpdateAvatar(modelImporter);
    }
}

/// <summary>
/// SourceのAvatarに加わった変更を適用する
/// </summary>
public static void UpdateAvatar(ModelImporter importer)
{
    if (importer.sourceAvatar == null) {
        return;
    }
        
    var tmpAvatar               = importer.sourceAvatar;
    importer.sourceAvatar       = null;
    importer.SaveAndReimport();
    importer.sourceAvatar       = tmpAvatar;
    importer.SaveAndReimport();
}

これでいいのか何とも怪しいですが、とりあえずこのバージョンではこれでいけました。
Animation TypeがGenericの場合もこれでいけました。

Unityバージョンにより挙動が違うかも?

以前Unity2017系で同じ現象に遭遇した時は上記の方法だとうまくいかず、
下記のエディタのコードとかを読んで頑張った記憶があります。

github.com

Model Impoterは結構変更あるし、もしかしたらこのあたりもいろいろ変わっている最中なのかもしれません。