【Unity】【エディタ拡張】AssetPostProcessorで他のアセットを削除する際の注意点

エディタ拡張でAssetPostProcessorで他のアセットを削除する際の注意点です。

Unity2020.1.10
AssetDatabase v2

はじめに

この記事ではAssetPostProcessorで処理対象のアセット以外のアセットを削除する際に起こる現象とその対策についてまとめます。

AssetPostProcessorについては必要に応じて以下の記事を参照してください。

light11.hatenadiary.com

問題点

さていまPrefab1とPrefab2という二つのPrefabがあることを考えます。
そしてPrefab1を削除したらそれに伴ってPrefab2まで削除されるという処理をAssetPostProcessorで行います。

f:id:halya_11:20210124112548p:plain
一緒に削除

AssetPostProcessorは以下のようになります。

using UnityEditor;
using UnityEngine;

public class ExamplePostProcessor : AssetPostprocessor
{
    private static void OnPostprocessAllAssets(string[] importedAssetPaths, string[] deletedAssetPaths,
        string[] movedAssetPaths, string[] movedFromAssetPaths)
    {
        foreach (var deletedAssetPath in deletedAssetPaths)
        {
            if (deletedAssetPath == "Assets/Example/Prefab1.prefab")
            {
                AssetDatabase.DeleteAsset("Assets/Example/Prefab2.prefab");
            }
        }
    }
}

これは一見正常に動きそうですが、この状態でPrefab1を削除するとUnity上にPrefab2だけが削除されずに残った状態になります。

f:id:halya_11:20210124112931p:plain
Prefab2だけが残る

このときPrefab2のファイル自体は削除されているのですが、AssetDatabaseのキャッシュだけ残ってしまっているようです。
したがってファイル本体がないのにAssetDatabase.AssetPathToGUID()などのAPIは値を返してしまいます。
またこの状態でPrefab2を削除しようとすると以下のようなエラーダイアログが出ます。

f:id:halya_11:20210124113337p:plain
エラーダイアログ

この現象は次回AssetDatabaseがリフレッシュされるまで続きます。
Prefab2はその時点で消えます。

対策

Unityのバグ感もありますが、一応対策としてはAssetDatabaseのリフレッシュを強制的に走らせるという方法があります。
ただしPostProcessと同じタイミングでリフレッシュしても削除されないので、EditorApplication.delayCallでタイミングを遅らせます。

using UnityEditor;

public class ExamplePostProcessor : AssetPostprocessor
{
    private static void OnPostprocessAllAssets(string[] importedAssetPaths, string[] deletedAssetPaths,
        string[] movedAssetPaths, string[] movedFromAssetPaths)
    {
        var deleted = false;
        foreach (var deletedAssetPath in deletedAssetPaths)
        {
            if (deletedAssetPath == "Assets/Example/Prefab1.prefab")
            {
                AssetDatabase.DeleteAsset("Assets/Example/Prefab2.prefab");
                deleted = true;
            }
        }

        if (deleted)
        {
            // 他のアセットを削除したらAssetDatabaseのリフレッシュをdelayCallする
            EditorApplication.delayCall += AssetDatabase.Refresh;
        }
    }
}

これでPrefab1を削除したらPrefab2も削除されるようになりました。

関連

light11.hatenadiary.com