Version: 2020.3
言語: 日本語
ストリーミングアセット
スクリプトによるソースアセットの変更

アセットにフォルダーごとのデフォルトを適用

大きなプロジェクトの場合、同じタイプのアセットをインポートするためにいくつかのプリセットを使用する場合があります。例えば、テクスチャアセットの場合、デフォルトのテクスチャをインポートするためのプリセットと、ライトマップテクスチャのためのプリセットが必要な場合があります。プロジェクトの Assets フォルダーには、これらのテクスチャのタイプごとに別々のフォルダーがあります。

TexturesDefault と TexturesLighting フォルダー。それぞれのプリセットがあります
TexturesDefaultTexturesLighting フォルダー。それぞれのプリセットがあります

次のスクリプトは、アセットを加えるフォルダーに基づいてプリセットを適用します。このスクリプトは、アセットと同じフォルダーにあるプリセットを選択します。フォルダーにプリセットがない場合、このスクリプトは親フォルダーを検索します。親フォルダーにプリセットがない場合、Preset ウィンドウで指定されているデフォルトのプリセットを使用します。

このスクリプトを使用するには、Project ウィンドウで Editor という名前の新しいフォルダーを作成し、このフォルダーに新しい C# スクリプトを作成し、このスクリプトをコピーして貼り付けます。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEditor.Experimental;
using UnityEditor.Presets;
using UnityEngine;

namespace PresetsPerFolder
{
    /// <サマリー>
   /// このサンプルクラスは、プリセットとサブフォルダーを含むフォルダー内のアセットにプリセットを自動的に適用します。
   /// コードは、すべてのアセットのインポーターが確実に決定論的であるように、インポーターの依存関係を設定する 3 つの部分に分割されています。
   ///
   /// OnPreprocessAsset:
   /// このメソッドは、インポートされた各アセットのルートフォルダーから Asset フォルダーに移動し、
///後でプリセットが追加/削除された場合に備えて、CustomDependency を各フォルダーに登録します。
   /// 次に、そのフォルダーからすべてのプリセットをロードし、それらをアセットインポーターに適用しようとします。
   /// 適用される場合、メソッドは各プリセットに直接依存関係を追加して、プリセット値が変更されたときにアセットを再インポートできるようにします。
   /// </サマリー>
    public class EnforcePresetPostProcessor : AssetPostprocessor
    {
        void OnPreprocessAsset()
        {
            //   if(assetPath....) 行は、アセットパスが "Assets/" で始まることを保証し、AssetPostprocessor がパッケージ内のアセットに適用されないようにします。
            // アセット拡張子を .cs で終了して、プリセットが作成または削除されるたびにコードコンパイルがトリガーされないようにすることはできません。
            // アセット拡張子を .preset で終わらせることはできないため、プリセットはそれ自体に依存しません。これにより、無限のインポートループが発生します。
            // プロジェクトによっては、さらにここに追加する例外がある場合があります。
            if (assetPath.StartsWith("Assets/") && !assetPath.EndsWith(".cs") && !assetPath.EndsWith(".preset"))
            {
                var path = Path.GetDirectoryName(assetPath);
                ApplyPresetsFromFolderRecursively(path);
            }
        }

        void ApplyPresetsFromFolderRecursively(string folder)
        {
            // 親フォルダーからアセットに向かって順番にプリセットを適用し、アセットに最も近いプリセットが最後に適用されるようにします。
            var parentFolder = Path.GetDirectoryName(folder);
            if (!string.IsNullOrEmpty(parentFolder))
                ApplyPresetsFromFolderRecursively(parentFolder);

            // Preset カスタムキーフォルダーに依存関係を追加します
             //したがって、プリセットがこのフォルダーに追加またはこのフォルダーから削除されるたびに、アセットが再インポートされます。
            context.DependsOnCustomDependency($"PresetPostProcessor_{folder}");

            // このフォルダー内のすべてのプリセットアセットを検索します。 AssetDatabase の代わりに System.Directory メソッドを使用します。
//なぜなら、インポートは、AssetDatabase がグローバル検索を実行できないようにする別のプロセスで実行される可能性があるためです。
            var presetPaths =
                Directory.EnumerateFiles(folder, "*.preset", SearchOption.TopDirectoryOnly)
                    .OrderBy(a => a);

            foreach (var presetPath in presetPaths)
            {
                // プリセットをロードし、インポーターに適用します
                var preset = AssetDatabase.LoadAssetAtPath<Preset>(presetPath);

                // このスクリプトは、次の 2 つの場合にプリセットの依存関係をアセットに追加します。
                 // 1. アセットがプリセットの前にインポートされた場合、プリセットはまだインポートされていないため、ロードされません。
                 //アセットとプリセットの間に依存関係を追加すると、アセットを再インポートできるため、
//Unity は割り当てられたプリセットをロードし、その値の適用を試みることができます。
                 // 2. プリセットが正常に読み込まれた場合、プリセットがこのアセットのインポート設定に適用されると、ApplyTo メソッドは true を返します。
                 //アセットへの依存関係としてプリセットを追加する場合、プリセット値に変更があると、新しい値を使用してアセットが再インポートされます。
                if (preset == null || preset.ApplyTo(assetImporter))
                {
                    // プリセットはネイティブアセットであり、DependsOnSourceAsset の使用は機能しないため、ここで DependsOnArtifact を使用します。
                    context.DependsOnArtifact(presetPath);
                }
            }
        }
    }

    /// <サマリー>
    /// InitPresetDependencies:
    ///このメソッドは、プロジェクトがロードされるときに呼び出されます。プロジェクト内のインポートされたすべてのプリセットが検索されます。
    ///プリセットを含むフォルダーごとに、フォルダー名から CustomDependency を作成し、フォルダー内のプリセット名とタイプのリストからハッシュを作成します。
    ///
    /// OnAssetsModified:
    ///プリセットがフォルダに追加、削除、または移動されるたびに、このフォルダーの CustomDependency を更新する必要があります
    ///したがって、これらのプリセットに依存する可能性のあるアセットが再インポートされます。
    ///
    /// TODO: 理想的には、各 CustomDependency も PresetType に依存している必要があります。
    ///そのため、フォルダーに新しい FBXImporterPreset を追加しても、テクスチャは再インポートされません。
    ///これにより、InitPresetDependencies メソッドと OnPostprocessAllAssets メソッドがこの例の目的にしては複雑になりすぎます。
    /// Unityは、CustomDependency が "Preset_{presetType}_{folder}" の形式に従い、ハッシュにそのフォルダーで指定された presetType のプリセットのみを含むことを指示します。
    /// </ summary>
    public class UpdateFolderPresetDependency : AssetsModifiedProcessor
    {
        /// <サマリー>
         ///このメソッドは、プロジェクトがロードされるか、コードがコンパイルされるたびに InitializeOnLoadMethod と一緒に呼び出されます。
         ///起動時にすべてのハッシュを正しく設定することは非常に重要です
         /// なぜなら、Unity は以前にインポートされたプリセットに OnPostprocessAllAssets メソッドを適用しないためです。
         ///また、CustomDependencies はセッション間で保存されないため、毎回再ビルドする必要があるためです。
    /// </ サマリー>

        [InitializeOnLoadMethod]
        static void InitPresetDependencies()
        {
            // AssetDatabase.FindAssets は glob フィルターを使用して、プロジェクトのオブジェクトすべてまとめてインポートすることを回避します。
             //この glob 検索では、.preset ファイルのみが検索されます。
            var allPaths = AssetDatabase.FindAssets("glob:\"**.preset\"")
                .Select(AssetDatabase.GUIDToAssetPath)
                .OrderBy(a => a)
                .ToList();

            bool atLeastOnUpdate = false;
            string previousPath = string.Empty;
            Hash128 hash = new Hash128();
            for (var index = 0; index < allPaths.Count; index++)
            {
                var path = allPaths[index];
                var folder = Path.GetDirectoryName(path);
                if (folder != previousPath)
                {
                   //新しいフォルダが見つかったら、プリセット名とプリセットタイプを使用して新しいCustomDependencyを作成します。
                    if (previousPath != string.Empty)
                    {
                        AssetDatabase.RegisterCustomDependency($"PresetPostProcessor_{previousPath}", hash);
                        atLeastOnUpdate = true;
                    }

                    hash = new Hash128();
                    previousPath = folder;
                }

                //パスとプリセットタイプの両方を追加して、プリセットタイプが変更されるたびにアセットが再インポートされるようにします。
                hash.Append(path);
                hash.Append(AssetDatabase.LoadAssetAtPath<Preset>(path).GetTargetFullTypeName());
            }

            // 最後のパスを登録します
            if (previousPath != string.Empty)
            {
                AssetDatabase.RegisterCustomDependency($"PresetPostProcessor_{previousPath}", hash);
                atLeastOnUpdate = true;
            }

            // ここで更新された依存関係が少なくとも1つある場合にのみ、Refresh をトリガーします。
            if (atLeastOnUpdate)
                AssetDatabase.Refresh();
        }

        /// <サマリー>
         ///プロジェクトでアセットが変更されるたびに、OnAssetsModified メソッドが呼び出されます。
         ///このメソッドは、プリセットが追加、削除、または移動されたかどうかを判別します
         ///そして、変更されたフォルダーに関連する CustomDependency を更新します。
         /// </ サマリー>

        protected override void OnAssetsModified(string[] changedAssets, string[] addedAssets, string[] deletedAssets, AssetMoveInfo[] movedAssets)
        {
            HashSet<string> folders = new HashSet<string>();
            foreach (var asset in changedAssets)
            {
                // A Preset has been changed, so the dependency for this folder must be updated in case the Preset type has been changed.
                if (asset.EndsWith(".preset"))
                {
                    folders.Add(Path.GetDirectoryName(asset));
                }
            }

            foreach (var asset in addedAssets)
            {
               //プリセットが変更されたため、プリセットタイプが変更された場合は、このフォルダの依存関係を更新する必要があります。
                if (asset.EndsWith(".preset"))
                {
                    folders.Add(Path.GetDirectoryName(asset));
                }
            }

            foreach (var asset in deletedAssets)
            {
               //プリセットが削除されたため、このフォルダーの依存関係を更新する必要があります。
                if (asset.EndsWith(".preset"))
                {
                    folders.Add(Path.GetDirectoryName(asset));
                }
            }

            foreach (var movedAsset in movedAssets)
            {
                //プリセットが移動されたため、このフォルダーの依存関係を更新する必要があります。
                if (movedAsset.destinationAssetPath.EndsWith(".preset"))
                {
                    folders.Add(Path.GetDirectoryName(movedAsset.destinationAssetPath));
                }

                if (movedAsset.sourceAssetPath.EndsWith(".preset"))
                {
                    folders.Add(Path.GetDirectoryName(movedAsset.sourceAssetPath));
                }
            }

            // 理由なく依存関係の更新を追加しないでください。
            if (folders.Count != 0)
            {
                //依存関係は AssetPostprocessor 呼び出しの外で更新する必要があります。
                 //メソッドを次のエディター更新に登録します。
                EditorApplication.delayCall += () =>
                {
                    DelayedDependencyRegistration(folders);
                };
            }
        }

        /// <サマリー>
         ///このメソッドは、指定された各フォルダーパスのすべてのプリセットをロードします
         ///そして、現在そのフォルダにあるプリセットに基づいて CustomDependency ハッシュを更新します。
         /// </ サマリー>

        static void DelayedDependencyRegistration(HashSet<string> folders)
        {
            foreach (var folder in folders)
            {
                var presetPaths =
                    AssetDatabase.FindAssets("glob:\"**.preset\"", new[] { folder })
                        .Select(AssetDatabase.GUIDToAssetPath)
                        .Where(presetPath => Path.GetDirectoryName(presetPath) == folder)
                        .OrderBy(a => a);

                Hash128 hash = new Hash128();
                foreach (var presetPath in presetPaths)
                {
                    //パスとプリセットタイプの両方を追加して、プリセットタイプが変更されるたびにアセットが再インポートされるようにします。
                     hash.Append(presetPath);
                    hash.Append(AssetDatabase.LoadAssetAtPath<Preset>(presetPath).GetTargetFullTypeName());
                }

                AssetDatabase.RegisterCustomDependency($"PresetPostProcessor_{folder}", hash);
            }


             //手動で Refresh をトリガーします
             // AssetDatabase が、更新されたフォルダーハッシュの依存関係チェックをトリガーするようにします。

            AssetDatabase.Refresh();
        }
    }
}

2017–03–27 公開ページ 2018.1 の新機能NewIn20181

ストリーミングアセット
スクリプトによるソースアセットの変更