(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

(:3[kanのメモ帳]


本ブログの運営者kan.kikuchiが個人で開発したゲームです!


特定のアセットの変更や移動、削除を禁止したり確認を出したり出来る AssetModificationProcessor【Unity】【エディタ拡張】


このエントリーをはてなブックマークに追加



この記事でのバージョン
Unity 6000.0.34f1


はじめに

UnityはAssetPostprocessorを使えば、アセットが変更され後の処理を実装する事可能ですが、



あくまで変更された後なので、変更をキャンセルしたりはできません。


という事で今回は、アセットの変更や移動、削除を禁止したり確認を出したりする方法の紹介です。

「このアセットは基本的に削除や移動してほしくない」みたいな時に役立ちます。



アセットの変更や移動、削除を禁止したり確認を出したりする方法

さっそくですが、アセットが変更される前に処理したい場合はAssetModificationProcessorを使います。



使い方はAssetModificationProcessorを継承したクラスを作成し、

作成前に処理したい場合は、OnWillCreateAsset、保存前に処理したい場合は、OnWillSaveAssets

削除前に処理したい場合は、OnWillDeleteAsset、移動前に処理したい場合は、OnWillMoveAsset

を実装します。

using UnityEditor;
using UnityEngine;
using System.IO;
using System.Collections.Generic;

//AssetModificationProcessorのサンプル
public class SampleAssetModificationProcessor : AssetModificationProcessor {

  //アセット作成直前に呼ばれる
  public static void OnWillCreateAsset(string assetPath) {
    Debug.Log($"アセットが作成されました: {assetPath}");
  }

  //アセット保存直前に呼ばれる
  public static string[] OnWillSaveAssets(string[] assetPaths) {
    foreach (var assetPath in assetPaths) {
      Debug.Log($"アセットが保存されました: {assetPath}");
    }
    return assetPaths;
  }

  //アセット削除直前に呼ばれる
  public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions options) {
    Debug.Log($"アセットが削除されました: {assetPath}");
    return AssetDeleteResult.DidNotDelete;
  }

  //アセット移動直前に呼ばれる
  public static AssetMoveResult OnWillMoveAsset(string oldPath, string newPath) {
    Debug.Log($"アセットが移動されました: {oldPath} -> {newPath}");
    return AssetMoveResult.DidNotMove;
  }
}


これをEditorフォルダ入れば、自動で実行されるようになります。


なお、アセットの新規作成時は保存も呼ばれ、metaファイルの作成や保存も検知します。


次はそれぞれをキャンセルする方法ですが、メソッドの返り値を変える事で実装できます。

ただし、作成だけはキャンセルできないので、

作られたアセットを即削除することで擬似的にキャンセルを実装する感じになります。

(※DeleteAssetで削除した時も、削除のキャンセルは働くので、削除と作成は同時にキャンセルできない)

using UnityEditor;
using UnityEngine;
using System.IO;
using System.Collections.Generic;

//AssetModificationProcessorのサンプル
public class SampleAssetModificationProcessor : AssetModificationProcessor {

  //アセット作成直前に呼ばれる
  public static void OnWillCreateAsset(string assetPath) {
    Debug.Log($"アセットが作成されましたましたが、削除しました: {assetPath}");
    //作成されたアセットのキャンセルは出来ないので削除
    AssetDatabase.DeleteAsset(assetPath);
  }

  //アセット保存直前に呼ばれる
  public static string[] OnWillSaveAssets(string[] assetPaths) {
    foreach (var assetPath in assetPaths) {
      Debug.Log($"アセットが保存されそうになりましたが、キャンセルしました: {assetPath}");
    }
  
    //配列から抜いたものはキャンセルされる( = 空の配列を返すと全てキャンセル)
    return new string[0];
  }

  //アセット削除直前に呼ばれる
  public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions options) {
    Debug.Log($"アセットが削除されそうになりましたが、キャンセルしました: {assetPath}");
    return AssetDeleteResult.FailedDelete;
  }

  //アセット移動直前に呼ばれる
  public static AssetMoveResult OnWillMoveAsset(string oldPath, string newPath) {
    Debug.Log($"アセットが移動されそうになりましたが、キャンセルしました: {oldPath} -> {newPath}");
    return AssetMoveResult.FailedMove;
  }
}


なお、上記の例では全てをキャンセルしていますが、まずこうすることはないと思うので、

実際はパスからフォルダを特定し、一部のフォルダ内のものはキャンセルする感じになると思います。


ちなみに禁止ではなく、確認や警告を出したいだけの場合は、

EditorUtility.DisplayDialogという汎用のダイアログを使うと便利です。



例えばPrefabを消そうとした時に確認を出すと以下のよう感じになります。

//アセット削除直前に呼ばれる
public static AssetDeleteResult OnWillDeleteAsset(string assetPath, RemoveAssetOptions options) {

  //Prefabを消そうとしている時は確認ダイアログを出す
  if (Path.GetExtension(assetPath) == ".prefab") {
    var isDelete = EditorUtility.DisplayDialog(
      "Prefabを削除しようとしています!!",
      $"{assetPath}を削除しようとしています。本当に消していいやつですか?",
      "多分大丈夫なので消します",
      "怖いのでやめておきます"
    );
 
    return isDelete ? AssetDeleteResult.DidDelete : AssetDeleteResult.FailedDelete;
  }

  //Prefab以外は通常通り削除
  return AssetDeleteResult.DidDelete;
}