【Unity】【エディタ】アセットがUnityエディタ外で変更されたことを検知する

アセットがUnityエディタ外で変更されたことを検知する方法についてまとめました。

Unity2020.1.17f1

やりたいことと方針

YAMLを直接編集したり、バージョン管理ツールを使用するなどしてアセットをUnityエディタの外側で変更することがあります。
本記事ではこのような操作がアセットになされたことを検知するための方法についてまとめます。

実装の方針として、まず外部で変更されたアセットにはUnityエディタにフォーカスが戻ってきたときにインポート処理が走ります。
したがってAssetPostProcessorで当該アセットがインポートされたことを検知します。
なおAssetPostProcessorについては以下の記事にまとめていますので、必要に応じて参照してください。

light11.hatenadiary.com

ただし、インポート処理は外部でアセットが変更された時以外のインポート時(Reimportしたときなど)にも走ってしまいます。
エディタ外で変更された時のみ処理するためには、Unityエディタがバックグラウンドから復帰したことを検知する必要があります。
この方法については以下の記事にまとめています。

light11.hatenadiary.com

実装

前節の方針で実装したものが以下になります。

using UnityEditor;
using UnityEditorInternal;
using UnityEngine;

public sealed class Example : AssetPostprocessor
{
    private static bool _isUnityEditorFocused;
    private static bool _needReloading;

    public static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets,
        string[] movedFromAssetPaths)
    {
        foreach (var importedAsset in importedAssets)
        {
            // TODO: 対象のアセットを絞り込む
            var isTarget = importedAsset.Contains("target");
            if (!isTarget)
            {
                continue;
            }

            // Unity外から戻ってきた場合には、delayCallまでの間に_needReloadingにtrueが代入される
            _needReloading = false;
            EditorApplication.delayCall += OnAssetImported;
            return;
        }
    }

    [InitializeOnLoadMethod]
    private static void InitializeOnLoad()
    {
        EditorApplication.update += Update;
    }

    private static void Update()
    {
        if (!_isUnityEditorFocused && InternalEditorUtility.isApplicationActive)
        {
            _isUnityEditorFocused = InternalEditorUtility.isApplicationActive;
            OnApplicationFocused();
        }
        else if (_isUnityEditorFocused && !InternalEditorUtility.isApplicationActive)
        {
            _isUnityEditorFocused = InternalEditorUtility.isApplicationActive;
        }
    }

    private static void OnApplicationFocused()
    {
        _needReloading = true;
    }

    private static void OnAssetImported()
    {
        // この時点で_needReloadingがtrueなら、アセットに変更が加わった、かつUnity外からフォーカスが戻ってきたと判断できる
        if (!_needReloading)
            return;

        Debug.Log("Unity外でアセットが変更されたことを検知しました");
    }
}

これをEditorフォルダに配置すると、名前にtargetが含まれるアセットがUnityエディタ外で変更されたことを検知できることが確認できます。
(名前などの条件は適宜変更してください

関連

light11.hatenadiary.com

light11.hatenadiary.com