現在Editorクラスでプリセット周りの実装をしているのですが、古めのスクリプトが仕様の変更で動作しないため しらべている最中です。

Editorクラスの情報は大量に存在しているので需要があるかわかりませんが。せっかくなので今回調べた経過を記事にしておきます。

 

まずUnityのEditorクラスから格納される内部データがどのように格納されているのかとデータへのアクセスのための実装ポイント、を簡単に解説している記事がありました。

 

■Accessing Unity's saved palettes

Q: 1つのパレットを保存する場合、それはスクリプト可能なオブジェクトのように見えるに格納されることに気づいた のですが、そこに保存されている色にアクセスする方法はありますか? できれば本当に便利です!

Screen Shot 2018-01-24 at 5.04.44 pm-CiS9zWDpoW

Screen Shot 2018-01-24 at 5.04.50 pm-VCX4uZJpsD

 

1.クリックしたものの種類を把握するように設定することができます:

  1. public static class EditorCommands {

  2. [MenuItem("Commands/Get Type Of Selected")]

  3. public static void GetTypeOfSelected() {

  4. Debug.Log(Selection.activeObject?.GetType().Name);

  5. }

  6. }

2.これを使用すると、パレットのタイプがColorPresetLibraryであることがわかります。さて、それは私たちが協力できるタイプですか?あなたがそれを行うにはいくつかの方法がありますが、有能なコードエディタがあれば、その名前を検索して、コンパイルされていないバージョンの型を見つけることができます:

  1. namespace UnityEditor

  2. {

  3. internal class ColorPresetLibrary : PresetLibrary

  4.    ...

  5. }

3.まあ内部的なので、スクリプトからアクセスすることはできません。アセットをテキストエディタで開いてその外観を確認してみます。

  1. %YAML 1.1

  2. %TAG !u! tag:unity3d.com,2011:

  3. --- !u!114 &1

  4. MonoBehaviour:

  5.   m_ObjectHideFlags: 52

  6.   m_PrefabParentObject: {fileID: 0}

  7.   m_PrefabInternal: {fileID: 0}

  8.   m_GameObject: {fileID: 0}

  9.   m_Enabled: 1

  10.   m_EditorHideFlags: 1

  11.   m_Script: {fileID: 12323, guid: 0000000000000000e000000000000000, type: 0}

  12.   m_Name:

  13.   m_EditorClassIdentifier:

  14.   m_Presets:

  15. - m_Name:

  16.     m_Color: {r: 1, g: 1, b: 1, a: 1}

  17. - m_Name:

  18.     m_Color: {r: 0.9705882, g: 0.007136685, b: 0.007136685, a: 1}

  19. - m_Name:

  20.     m_Color: {r: 0.13559689, g: 0.4712593, b: 0.5588235, a: 1}

さて、これは簡単に操作できます! そのテキストファイルをつかんで、 "m_Color"で始まるすべての行を探し、色を解析することをお勧めします。それをヘルパーメソッドとして作成するのはかなり簡単です。

 

次にヘルパークラスの実装例を探してみました

ヘルパーメソッドの実装例

 

Unityでプリセット内部変数にアクセスで検索すると上位にくるサイトですが掲載から時間が経過しているためスクリプトは手直しを入れる必要があります。掲載されているHelper関数のスクリプトですが以下のようなメッセージが帰ると思います。

error CS0619: `UnityEngine.Types.GetType(string, string)' is obsolete: `This was an internal method which is no longer used'
unity5以降の仕様変更でこのやり方では内部データにアクセスできないためエラーが返ります。そこで以下のように。
  1. Unityちゃん2Dのインポートのエラー解決(error CS0619)[ver.2017.2.1f1] - Qiita
  2. UnityEngine.Typesが使えなくなりました - FreelyApps
1.のサイトリンクではこのような変更で、エラーの回避ができたということです。
Types.GetType("UnityEditor.AnimationClipEditor", "UnityEditor.dll");
  ? var baseType = Types.GetType("UnityEditor.AnimationClipEditor", "UnityEditor.dll");
  ○ System.Type baseType = System.Reflection.Assembly.Load("UnityEditor.dll").GetType(typeName);
 
 

2.のサイトでは

  •   UnityEngine.Types.GetType(className,"Assembly-CSharp");
  •  System.Reflection.Assembly.Load("Assembly-CSharp").GetType(className);

IDEのサジェストにしたがうとSystem.Reflectionは省略して 良いそうなので

  • Assembly.Load("Assembly-CSharp").GetType(typeName);

このように記述することでエラーは回避できるようです。  さらに以下のような記述があります。

  • System.Type.GetTypeというメソッドでもTypeを取得できるようでした。型の名前を引数にとり、型を返すメソッドです。実行中のアセンブリ(dllと考えていい)かMscorlib.dllに含まれる型であれば名前空間で修飾した型名で型が取れるようです。
  •   System.Type.GetType(className+ ",Assembly-CSharp");

 

 

 

さらに 検索してみたところ以下のようなスクリプトが発見できました、新しめの2018.2月の情報ですが、コメント部分が詳細なので参考にしてみてください。

ColorPresetLibraryCreator.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Reflection;
public static class ColorPresetLibraryCreator
{

/*

■1:

Unity creates new preset librarys internally through typed singleton instances of the PresetLibraryManager class.

The two key methods we need to access through reflection are:

public T CreateLibrary(ScriptableObjectSaveLoadHelper helper, string presetLibraryPathWithoutExtension) where T : ScriptableObject

public void SaveLibrary(ScriptableObjectSaveLoadHelper helper, T library, string presetLibraryPathWithoutExtension) where T : ScriptableObject

CreateLibrary does some file path checking before creating the library through the helper object

and registering it with the library cache. SaveLibrary does what it says through the helper object.

In between the two calls is when we can actually add presets to the library.

We could use the helper object directly to save the library but it's probably safer to let the Manager class do it.

*/

    private const string assemblyDef = "UnityEditor.{0},UnityEditor";

    public static void CreateNewLibraryThroughPresetLibraryManager(string name, List colors)
    {

   

//■2:

// The ScriptableSingleton class is public, but because PresetLibraryManager isn't

// we still need to make a generic type and then use reflection to get the static instance property.

// This is assuming that we need the singleton instance for library registration purposes -

// it might not be necessary.


        Type managerType = Type.GetType(string.Format(assemblyDef, "PresetLibraryManager"));
        Type singletonType = typeof(ScriptableSingleton<>).MakeGenericType(managerType);
        PropertyInfo instancePropertyInfo = singletonType.GetProperty("instance", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
        var managerInstance = instancePropertyInfo.GetValue(null, null);

// ■3:

// We create an instance of the save/load helper and then pass it and the path to the CreateLibrary method.

// "colors" is the file extension we want for the library asset without the '.'

 
        Type libraryType = Type.GetType(string.Format(assemblyDef, "ColorPresetLibrary"));
        Type helperType = Type.GetType(string.Format(assemblyDef, "ScriptableObjectSaveLoadHelper`1"))
                              .MakeGenericType(libraryType);
        MethodInfo createMethod = managerType.GetMethod("CreateLibrary", BindingFlags.Instance | BindingFlags.Public)
                                             .MakeGenericMethod(libraryType);
        var helper = Activator.CreateInstance(helperType, new object[] { "colors", SaveType.Text });
        var library = createMethod.Invoke(managerInstance, new object[] { helper, Path.Combine("Assets/Editor", name) });

//■4:

// We can't cast library to the desired type so we get the Add method through reflection

// and add the desired colours as presets through that.

// After that the library can be saved!


        if ((UnityEngine.Object)library != (UnityEngine.Object)null)
        {
            MethodInfo addPresetMethod = libraryType.GetMethod("Add");
            foreach (var color in colors)
            {
                addPresetMethod.Invoke(library, new object[] { color, color.ToString() });
            }

            MethodInfo saveMethod = managerType.GetMethod("SaveLibrary", BindingFlags.Instance | BindingFlags.Public)
                                             .MakeGenericMethod(libraryType);
            saveMethod.Invoke(managerInstance, new object[] { helper, library, Path.Combine("Assets/Editor", name) });
        }

■5:

// The library could be returned as an Object or ScriptableObject reference if that was useful

// I don't know of a way to cast it to the actual ColorPresetLibrary type.

   
    }

// Extension function because why not
    public static void CreateNewPresetLibrary(this List colors, string name)
    {
        CreateNewLibraryThroughPresetLibraryManager(name, colors);
    }
}

※誤訳があるかもしれませんのでスクリプト中のコメントで確認してください。

 1.  Unityは、PresetLibraryManagerクラスの型指定されたシングルトンインスタンスを通じて、内部的に新しいプリセットライブラリを作成します。リフレクションを通じてアクセスする必要がある2つの重要な方法は次のとおりです。

  • public T CreateLibrary(ScriptableObjectSaveLoadHelper helper, string presetLibraryPathWithoutExtension) where T : ScriptableObject
  • public void SaveLibrary(ScriptableObjectSaveLoadHelper helper, T library, string presetLibraryPathWithoutExtension) where T : ScriptableObject


  CreateLibraryは、ヘルパーオブジェクトを通してライブラリを作成する前に、いくつかのファイルパスのチェックを行いライブラリキャッシュに登録します。 SaveLibraryはヘルパーオブジェクトを通して何を言うのかを行います。
2つの呼び出しの間に、ライブラリーに実際にプリセットを追加することができます。
ヘルパーオブジェクトを直接使用してライブラリを保存することもできますが、Managerクラスで行うほうが安全でしょう。

2. ScriptableSingletonクラスはpublicですが、PresetLibraryManagerはpublicではないため ジェネリック型を作成し、リフレクションを使用して静的インスタンスプロパティを取得する必要があります。これは、ライブラリ登録の目的でシングルトンインスタンスが必要であると仮定していますが、必要ないのかもしれません。

3.  セーブ/ロードヘルパーのインスタンスを作成し、それをパスとCreateLibraryメソッドに渡します。"colors"は、ライブラリアセットに必要なファイル拡張子で '.'を必要としません。

4. ライブラリを目的の型にキャストできないため、リフレクションでAddメソッドを取得します プリセットとして必要な色を追加します。その後、ライブラリを保存することができます!
 
5.  有用であれば、ライブラリはObjectまたはScriptableObject参照として返すことができます。 ColorPresetLibraryタイプにキャストする方法はわかりません。
なぜ拡張機能なのでしょう?

 

この解説によればUnityではManagerクラスが用意されているので内部データへのアクセスはそちらを使用することで安全なアクセスができるようです。今回の場合PresetLibraryManagerのコードを参照することになります、

PresetLibraryManageにどのようなクラス定義がされているのか簡単に抜粋してみました

unity-decompiled/UnityEditor/UnityEditor/PresetLibraryManager.cs

 

  • public void GetAvailableLibraries(ScriptableObjectSaveLoadHelper helper, out List preferencesLibs, out List projectLibs) where T : ScriptableObject
  • private string GetLibaryNameFromPath(string filePath)
  • public T CreateLibrary(ScriptableObjectSaveLoadHelper helper, string presetLibraryPathWithoutExtension) where T : ScriptableObject
  • public T GetLibrary(ScriptableObjectSaveLoadHelper helper, string presetLibraryPathWithoutExtension) where T : ScriptableObject
  • public void UnloadAllLibrariesFor(ScriptableObjectSaveLoadHelper helper) where T : ScriptableObject
  • public void SaveLibrary(ScriptableObjectSaveLoadHelper helper, T library, string presetLibraryPathWithoutExtension) where T : ScriptableObject
  • private PresetLibraryManager.LibraryCache GetPresetLibraryCache(string identifier)
  • public List loadedLibraries
  • public List loadedLibraryIDs
  • public LibraryCache(string identifier)
  • public void UnloadScriptableObjects()

 

まだ全体を調べ終わっていないので実装までは時間がかかりそうですが、今回はここまで調べたということでメモ代わりに記事にしておきました。 参考になれば幸いです。 それではまた