【Unity】デフォルトのカスタムエディタを実装可能なisFallbackフラグの紹介【CustomEditor】

https://twitter.com/su10_dev/status/1578248125319241729?s=20&t=8JbJdPbMQJGo5VS7UrI4Fw

# はじめに
UI Toolkit（UIElements）でUI作ってたら `CustomEditor` 属性に便利なフラグを見つけたので紹介します。

# 環境
* Unity2021.3.0f1

# 背景と問題点
## 背景
「IMGUIはもう嫌だ！俺はUI ToolkitでUIを作るんだ！」となってカスタム `PropertyDrawer` を書いたとします。

試しに以下のようなコンポーネントと、UI Toolkit（というか `VisualElement`）を使って描画する `PropertyDrawer` を実装してみます。

```SampleComponent.cs
using System;
using UnityEngine;

public class SampleComponent : MonoBehaviour
{
    public SampleClassWithIMGUI sampleClassWithIMGUI;
    public SampleClassWithUIToolkit sampleClassWithUIToolkit;
}

[Serializable]
public class SampleClassWithIMGUI
{
    public bool isUIToolkit = false;
}

[Serializable]
public class SampleClassWithUIToolkit
{
    public bool isUIToolkit = true;
}
```

```SampleClassWithUIToolkitPropertyDrawer.cs
#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

[CustomPropertyDrawer(typeof(SampleClassWithUIToolkit))]
public class SampleClassWithUIToolkitPropertyDrawer : PropertyDrawer
{
    public override VisualElement CreatePropertyGUI(SerializedProperty property)
    {
        return new PropertyField(property)
        {
            // 色を赤くしてみる
            style = { color = new StyleColor(Color.red) },
        };
    }
}
#endif
```

上記の `SampleComponent` を適当な `GameObject` につけると、Inspectorはこうなります。


![SampleScene_-_UI_Toolkit_Test_-_Windows__Mac__Linux_-_Unity_2021_3_0f1__Metal_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27863/0d76e8b8-1af7-879a-eb7f-eeb87cff8360.png)


IMGUIで描画されるクラス `SampleClassWithIMGUI` は通常通り描画されてますが、UI Toolkitで描画したい `SampleClassWithUIToolkit` クラスは謎の "No GUI Implemented" というメッセージが出るだけで描画されてません。

これは[フォーラムの投稿](https://forum.unity.com/threads/property-drawers.595369/#post-4076821)によると

> That is, you need to make sure to override CreateInspectorGUI() on the Editor class for your object and then populate manually with PropertyFields. This is a temporary situation until we switch the default inspectors of all objects to UIElements (right now default inspectors still use IMGUI).

> つまり、オブジェクトのEditorクラスでCreateInspectorGUI()をオーバーライドし、PropertyFieldsを手動で入力する必要があります。これは、すべてのオブジェクトのデフォルトインスペクタをUIElementsに切り替えるまでの一時的な状況です（現在、デフォルトインスペクタはまだIMGUIを使用しています）。

（DeepL翻訳）

だからだそうで、 `SampleComponent` のカスタム `Editor` を実装すればOKです。

```SampleComponentEditor.cs
// 参考:
// https://issuetracker.unity3d.com/issues/propertydrawer-dot-createpropertygui-will-not-get-called-when-using-a-custompropertydrawer-with-a-generic-struct

#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine.UIElements;

[CustomEditor(typeof(SampleComponent))]
public class SampleComponentEditor : Editor
{
    public override VisualElement CreateInspectorGUI()
    {
        var container = new VisualElement();

        // IMGUI同様のInspectorを実装
        InspectorElement.FillDefaultInspector(container, serializedObject, this);

        return container;
    }
}
#endif
```


![SampleScene_-_UI_Toolkit_Test_-_Windows__Mac__Linux_-_Unity_2021_3_0f1__Metal_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/27863/682f7666-2f4b-b731-7d89-b7c212be2c80.png)



これでUI Toolkitでも描画されるようになりました。めでたしめでたし。

## 問題点
すでにお気づきかと思われますが、これだと

**「UI Toolkitでカスタムプロパティドロワーを実装したら、その対象クラスを使用するクラスは必ずカスタムエディタを書かないといけない」**

ということになってしまいます（正気か！？）。

この問題の解決方法として、先に上げた `SampleComponentEditor` のような汎用クラスを作って「これを継承すればカスタム `Editor` を数行で実装できる」というのを見たのですが、もっとスマートな方法があったので紹介します。

# `CustomEditor` の `isFallback` フラグ
解決方法は以下のカスタム `Editor` を一つ追加するだけです。

`SampleComponentEditor` みたいな個別のカスタム `Editor` は必要ありません。

```DefaultEditor.cs
// 参考URL:
// https://forum.unity.com/threads/property-drawers.595369/#post-5118800

#if UNITY_EDITOR
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;

[CustomEditor(typeof(Object), true, isFallback = true)]
public class DefaultEditor : Editor
{
    public override VisualElement CreateInspectorGUI()
    {
        var container = new VisualElement();

        // IMGUI同様のInspectorを実装
        InspectorElement.FillDefaultInspector(container, serializedObject, this);

        return container;
    }
}
#endif
```

`CustomEditor` の `isFallback` というフラグを `true` にすると

　　「他に適用されるカスタム `Editor` が存在しない場合のデフォルト `Editor`」

として使用されます。

以下は [ドキュメント](https://docs.unity3d.com/2021.3/Documentation/ScriptReference/CustomEditor-isFallback.html) からの引用です。

> `CustomEditor.isFallback`
>
> public bool isFallback;
>
> **Description**
> If true, match this editor only if all non-fallback editors do not match. Defaults to false.
> 
> Unity does a two-pass match to hook up editors with inspected types. First the non-fallback editors are tested, and if none match, then the fallbacks are tested for a match. Setting this flag lets you set up a default editor for a given type, while still permitting another editor type to override it.

> trueの場合、フォールバックしないエディタがすべてマッチしない場合にのみ、このエディタをマッチさせます。デフォルトはfalseです。
>
> Unityは、検査されたタイプのエディタをフックするために、2パスマッチを行います。まず、フォールバックでないエディタがテストされ、マッチするものがなければ、フォールバックがマッチするかどうかテストされます。このフラグを設定することで、特定のタイプのデフォルトエディタを設定することができ、同時に他のエディタタイプによるオーバーライドを許可することができます。

（DeepL翻訳）

これググっても日本語の情報が出てこないのですが、ドキュメントをさかのぼっていくと少なくとも[Unity2017.1の時点で存在](https://docs.unity3d.com/2017.1/Documentation/ScriptReference/CustomEditor-isFallback.html)してたみたいです。

`isFallback` フラグが `true` のものが複数ある場合はどうなるのか気になりますが、未検証です。知ってる人いたら教えて下さい。

# 最後に
というわけでUI Toolkit使用時の注意点と `CustomEditor` の `isFallback` フラグの紹介でした。

~~ちなみに今回起きた問題は[Unity2022.1で対応済み](https://unity3d.com/jp/unity/whats-new/2022.1.0#:~:text=UI%20Toolkit%3A%20Added%20UI%20Toolkit%20implementation%20for%20property%20drawers.)なので、最新のUnityを使用している場合は気にする必要がありません。~~ Unity2022.1.0f1(Apple Silicon版)で検証しましたが直ってませんでした。

あと実は紹介した `DefaultEditor` の実装にも問題があり、一番上に描画されるスクリプトの `PropertyField` をクリックしてもスクリプトファイルがpingされない問題があるのですが、それについてはまた別の記事で~~書こうと思います~~書きました。

* [【Unity】UI Toolkitを使ったカスタムエディタでスクリプトファイルのpingが動くようにする【エディタ拡張】](https://qiita.com/su10/items/0ead51b5155d3f56850a)