【Unity】インスペクターにプレハブのプレビュー画像を追加する

# はじめに
ScriptableObjectにプレハブのリストを追加していくと、一見しただけではわからないことがあったためプレハブのプレビュー画像をインスペクター上に追加する実装方法です。

# 実装イメージ
:::note warn
## 未実装
![スクリーンショット 2024-12-05 174018.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3847430/af596752-ecea-65d5-ca47-492c69fad177.png)
:::

:::note info
## 実装後
![スクリーンショット 2024-12-05 174440.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3847430/0fcfe24c-7102-27e5-149c-6796ec8fe13f.png)
:::

# 実装コード

```c++:PreviewDrawer.cs
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;

/// <summary>
/// プレビューのサイズ設定
/// </summary>
public class PreviewAttribute : PropertyAttribute
{
    // 定数 //

    static readonly int DefaultImageSize = 100; // プレビュー画像の初期サイズ

    // 変数 //
    
    // サイズが指定されなかった場合の初期値
    public float Height = DefaultImageSize;
}

/// <summary>
/// インスペクター上に画像を描画
/// </summary>
/// MEMO：シリアライズフィールドでインスペクター上にプレハブデータを追加した際、
///       プレハブのプレビュー画像を出力するようにできる
[CustomPropertyDrawer(typeof(PreviewAttribute))]
public class PreviewDrawer : PropertyDrawer
{
    // 定数 //

    static readonly int MarginRate = 2;                                         // インスペクター上に表示する際の余白倍率
    static readonly float Margin = EditorGUIUtility.standardVerticalSpacing;    // インスペクター上に表示する際の余白値

    /// <summary>
    /// GUIイベントに合わせたGUI描画
    /// </summary>
    /// <param name="position">描画座標、サイズ</param>
    /// <param name="property">描画対象のプロパティ</param>
    /// <param name="label">描画対象の情報</param>
    /// MEMO：プレハブ画像を追加するため、通常の描画をオーバーライド
    /// 実装参考URL：https://zenn.dev/gameshalico/articles/51217c728bfb67
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // 通常のプロパティを描画
        position.height = EditorGUI.GetPropertyHeight(property, label);
        EditorGUI.PropertyField(position, property, label);

        // サイズ情報の取得
        var previewAttribute = (PreviewAttribute)attribute;

        // プレビュー用テクスチャの取得
        var texture = AssetPreview.GetAssetPreview(property.objectReferenceValue);
        if (texture == null) return;

        // 幅を設定
        var width = previewAttribute.Height * texture.width / texture.height;

        // テクスチャを表示する位置を計算
        var imageRect = new Rect
        {
            // 右寄せにして画像を表示
            x = position.xMax - width - Margin,
            y = position.y + position.height + Margin,
            width = width,
            height = previewAttribute.Height
        };

        // 画像を表示
        GUI.DrawTexture(imageRect, texture);
    }

    /// <summary>
    /// GUIの高さの取得
    /// </summary>
    /// <param name="property">対象のシリアライズされたプロパティ</param>
    /// <param name="label">ラベル</param>
    /// <returns>ピクセル単位の高さ</returns>
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        // 高さの追加
        var previewAttribute = (PreviewAttribute)attribute;
        
        // インスペクターに表示する際の高さ＋余白
        return previewAttribute.Height + EditorGUI.GetPropertyHeight(property, label) + Margin * MarginRate;
    }
}
#endif

```

```c++:実装箇所
#if UNITY_EDITOR
    // HACK:[Preview(Height = 100)]の部分がリテラルでしか対応出来ていないため定数化出来ておりません。
    [Preview(Height = 100)]
#endif
    [SerializeField, Header("プレハブデータ")]
    GameObject sourcePrefab;    // ディスクのプレハブデータ
```

# 注意
:::note alert
エディター拡張部分は`#if`で囲むか、Asset > Editorフォルダ内に入れないと
ビルドエラーが発生します。
:::

# 参考
エディター拡張エラー↓
https://dokuro.moe/unity-its-not-bug-that-propertydrawer-not-found-when-build/

実装↓
https://zenn.dev/gameshalico/articles/51217c728bfb67