インスペクターでインターフェースを割り当てる方法

1. はじめに

Unityでは、通常インターフェースや抽象クラスは直接シリアライズできませんが、[SerializeReference] 属性を使用することで、これらをシリアライズ対象にし、柔軟なポリモーフィズムを実現できます。
本資料では、ハイスコア保存処理を例に、インスペクター上でインターフェースの実装クラスを動的に選択・割り当てる方法と、HiScoreManager の利用方法について解説します。


2. システム構成概要

  1. インターフェースと実装クラスの作成
    • ハイスコア保存用インターフェース IHighScoreSaver を定義
    • 実装クラスとして、例:FileHighScoreSaver(ファイルに保存)と PlayerPrefsHighScoreSaver(PlayerPrefs に保存)を作成
  2. HiScoreManager の作成
    • [SerializeReference] 属性を利用して、インターフェース型のフィールドをシリアライズ
    • ハイスコア保存処理を行うための SaveScore() メソッドを実装
  3. カスタムインスペクターの実装
    • Editorフォルダ内にカスタムエディターを作成し、インスペクター上で Saver の追加・削除を可能にするUIを提供
  4. HiScoreManager の使い方
    • シーンに配置してインスペクターから Saver の実装を割り当て、実行時にハイスコア保存処理を確認

3. ステップバイステップ解説

3.1 インターフェースと実装クラスの作成

まず、ハイスコア保存用のインターフェース IHighScoreSaver を定義し、その実装クラスを作成します。各実装クラスは [Serializable] 属性を付与する必要があります。

// IHighScoreSaver.cs
using System;

public interface IHighScoreSaver
{
    void SaveHighScore(int score);
}
// FileHighScoreSaver.cs
using System;
using UnityEngine;

[Serializable]
public class FileHighScoreSaver : IHighScoreSaver
{
    public void SaveHighScore(int score)
    {
        Debug.Log("Score saved to file: " + score);
    }
}
// PlayerPrefsHighScoreSaver.cs
using System;
using UnityEngine;

[Serializable]
public class PlayerPrefsHighScoreSaver : IHighScoreSaver
{
    public void SaveHighScore(int score)
    {
        Debug.Log("Score saved to PlayerPrefs: " + score);
    }
}

3.2 HiScoreManager の作成

次に、ハイスコア保存機能を利用する HiScoreManager クラスを作成します。
ここでは、[SerializeReference] 属性を利用して、インターフェース型の highScoreSaver フィールドをシリアライズ対象にします。

// HiScoreManager.cs
using UnityEngine;

public class HiScoreManager : MonoBehaviour
{
    [SerializeReference]
    public IHighScoreSaver highScoreSaver;

    public int score = 100;

    // ハイスコア保存を実行するメソッド
    public void SaveScore()
    {
        if (highScoreSaver != null)
        {
            highScoreSaver.SaveHighScore(score);
        }
        else
        {
            Debug.LogWarning("Saver が設定されていません。");
        }
    }
}

3.3 カスタムインスペクターの実装

次に、HiScoreManager のインスペクター上で Saver の追加と削除を行えるように、カスタムエディターを実装します。
以下のコードでは、GenericMenu を使用して、利用可能な Saver クラスの一覧を動的に表示し、選択されたクラスのインスタンスを作成して highScoreSaver フィールドに割り当てます。

// HiScoreManagerEditor.cs (Editorフォルダ内に配置)
using UnityEngine;
using UnityEditor;
using System;
using System.Linq;

[CustomEditor(typeof(HiScoreManager))]
public class HiScoreManagerEditor : Editor
{
    public override void OnInspectorGUI()
    {
        // デフォルトのインスペクター表示
        DrawDefaultInspector();

        HiScoreManager manager = (HiScoreManager)target;

        EditorGUILayout.Space();
        EditorGUILayout.LabelField("HighScore Saver", EditorStyles.boldLabel);

        // Saver が未設定の場合
        if (manager.highScoreSaver == null)
        {
            EditorGUILayout.HelpBox("Saver が未設定です。", MessageType.Info);
            if (GUILayout.Button("Add Saver"))
            {
                // GenericMenu を利用して利用可能な Saver クラス一覧を表示
                GenericMenu menu = new GenericMenu();

                // AppDomainから IHighScoreSaver を実装した具象クラスを全て取得
                var saverTypes = AppDomain.CurrentDomain.GetAssemblies()
                                    .SelectMany(assembly => assembly.GetTypes())
                                    .Where(type => typeof(IHighScoreSaver).IsAssignableFrom(type) && !type.IsInterface && !type.IsAbstract)
                                    .ToArray();

                foreach (var type in saverTypes)
                {
                    menu.AddItem(new GUIContent(type.Name), false, () =>
                    {
                        // 選択された型のインスタンスを生成してフィールドに割り当てる
                        manager.highScoreSaver = (IHighScoreSaver)Activator.CreateInstance(type);
                        EditorUtility.SetDirty(manager);
                    });
                }
                menu.ShowAsContext();
            }
        }
        else
        {
            // Saver が設定されている場合、現在の Saver の型名を表示し削除ボタンを提供
            EditorGUILayout.LabelField("Current Saver:", manager.highScoreSaver.GetType().Name);
            if (GUILayout.Button("Remove Saver"))
            {
                manager.highScoreSaver = null;
                EditorUtility.SetDirty(manager);
            }
        }
    }
}

4. HiScoreManager の使い方

4.1 スクリプトの配置

  • Runtime用スクリプト
    IHighScoreSaver.csFileHighScoreSaver.csPlayerPrefsHighScoreSaver.csHiScoreManager.cs を Assets/Scripts/ フォルダなどに配置します。
  • エディター用スクリプト
    HiScoreManagerEditor.cs を Assets/Editor/ フォルダに配置してください。

4.2 シーンへの配置とインスペクターでの操作

  1. シーンにオブジェクトを配置
    シーン内に空の GameObject を作成し、そこに HiScoreManager コンポーネントを追加します。
  2. Saver の割り当て
    • HiScoreManager のインスペクターを開くと、「HighScore Saver」セクションが表示されます。
    • 「Add Saver」ボタンをクリックすると、利用可能な Saver クラスの一覧がポップアップ表示されます。
    • 任意の Saver(例:FileHighScoreSaver や PlayerPrefsHighScoreSaver)を選択すると、選択された実装が highScoreSaver に割り当てられ、型名が表示されます。
    • 必要に応じて「Remove Saver」ボタンで割り当てを解除できます。

4.3 実行時の動作確認

  1. ゲーム実行
    ゲーム実行中、またはエディタ上で HiScoreManager の SaveScore() メソッドを呼び出す(例えばボタンを配置してクリックイベントから呼び出す)ことで、設定された Saver によるハイスコア保存処理が実行されます。
  2. コンソールの確認
    • FileHighScoreSaver を選択した場合:「Score saved to file: 100」というログが表示されます。
    • PlayerPrefsHighScoreSaver を選択した場合:「Score saved to PlayerPrefs: 100」というログが表示されます。
    • Saver が未設定の場合は、警告メッセージが表示されます。

5. まとめ

本資料では、[SerializeReference] 属性を活用し、インスペクター上でインターフェースの実装を動的に選択・割り当てる方法と、HiScoreManager の使い方について解説しました。

  • インターフェースもシリアライズ可能に: [SerializeReference] により、インターフェースや抽象クラスをシリアライズ
  • 動的な実装クラスの取得: リフレクションを用いて利用可能な具象クラスを自動的にリストアップ
  • ユーザーインターフェースの提供: カスタムインスペクターで直感的に Saver の追加・削除を実装
  • HiScoreManager の利用方法: シーンへの配置から実行時の保存処理まで、具体的な操作手順を解説

この手法を活用することで、プラグイン形式の拡張や柔軟なシステム設計が実現でき、開発の幅が広がります。

6. UML図

1. クラス図

以下のクラス図は、IHighScoreSaver インターフェースとその実装クラス(FileHighScoreSaverPlayerPrefsHighScoreSaver)、および HiScoreManager とカスタムインスペクター HiScoreManagerEditor の関係を表しています。

2. シーケンス図

以下のシーケンス図は、カスタムインスペクターで「Add Saver」ボタンをクリックしてSaverを追加する流れを示しています。

3. アクティビティ図

以下のアクティビティ図は、カスタムインスペクター内でSaverの追加・削除処理がどのように実行されるかの流れを示しています。

これらの図を通じて、インターフェースのシリアライズやカスタムインスペクターでの動的な実装選択の仕組みが視覚的に理解できると思います。各図は、実際のコードと連動した処理の流れやクラス間の関係性を学習するのに役立ちます。