0からスタート!ゲーム開発ブログ

ゲーム開発に関する様々な記事を更新します

【Unity初心者向け】簡単にできる!SerializeFieldを使ったInspector拡張

1. はじめに

こんにちは、azarashin です。

ゲーム中で各種キャラクターを扱う場合、そのキャラクターのパラメータを設定する必要があります。

また、ゲームバランスを調整するためにゲームのルールに付随するパラメータを設定することもあります。 このようなパラメータ設定を行うにあたり、 C# のようなプログラミング言語を用いた場合にはパラメータを記述した外部ファイルを設け、 そのファイルからパラメータを読み込むといった処理を行うことがあります。

ソースコードにパラメータをベタ書きすることもできなくはありませんが、 プログラムの処理部分とパラメータなどのデータ部分は分離して管理する方が 品質面でも開発の運用面でも好ましいでしょう。)

しかしながら、特にキャラクターの場合はパラメータとキャラのモデリングデータや材質などがセットになっていることが多く、自力でこれらのセットを外部ファイルを用いて管理しようとすると手間がかかります。 UnityとC#を用いると、SerializeField という記述を活用することで オブジェクトに付与されたコンポーネントにパラメータを記述することができるようになります。

本記事ではこのSerializeField を用いてInspector 欄を拡張する方法について紹介していきます。

2. 単純型を扱う(整数値)

2.1. 項目の定義

まず空のGameObject を作成します。 そして新しいC#スクリプトファイルを作成し、作成したGameObject にコンポーネントとして割り当てます。

using UnityEngine;

public class InspectorSample : MonoBehaviour
{

}

この状態ではまだパラメータの設定をすることができず、InspectorSample のコンポーネントには何も項目が表示されていません。 これにパラメータを付与してエディタでその値を変更できるようにします。 メンバ変数_hp を追加し、このメンバ変数の宣言の手前に[SerializeField]を追加します。

using UnityEngine;

public class InspectorSample : MonoBehaviour
{
    [SerializeField]
    int _hp = 100; 

}

Inspector は以下のようになり、コンポーネント内の項目hpを設定できるようになっています。 この項目はGameObjectごとに異なる値を設定できるようになっています。

hp欄を追加

2.2. 単純型の範囲を制限する

パラメータ(単純型)に設定可能な範囲制限をつけるには[SerializeField] の次の行に[Range(xxx,yyy)]を記述します。xxx と yyy にはそれぞれ設定範囲の最小値と最大値を記述します。

using UnityEngine;

public class InspectorSample : MonoBehaviour
{
    [SerializeField]
    [Range(1, 65535)]
    int _hp = 100; 

}

これによりInspector のパラメータ設定部分がスライドバーになり、設定値に範囲制限をつけることができるようになりました。

範囲制限を付ける

3. その他の単純型

パラメータには前述した整数型(int)だけでなく、実数型(float)や文字列型(string), 2値型(bool)も設定することができます。2値型を適用するとチェックボックスが表示されるようになります。

using UnityEngine;

public class InspectorSample : MonoBehaviour
{
    [SerializeField]
    string _name; // 文字列型


    [SerializeField]
    [Range(1, 65535)]
    int _hp = 100; // 整数型

    [SerializeField]
    bool _invisible; // 2値型

    [SerializeField]
    [Range(1.0f, 64.0f)]
    float _speed = 2.0f; // 実数型

    [SerializeField]
    string _description;
}

様々な属性を記述

4. 見た目をきれいにする

4.1. タイトルをつける

Header を記述することで、Inspector 内のパラメータにタイトルを付与することができます。

    [Header("基本情報")]
    [SerializeField]
    string _name;

詳細は下記をご参照下さい。

docs.unity3d.com

4.2. 余白を加える

Space を記述することで、Inspector 内のパラメータ設定領域の手前に余白を追加することができるようになります。Space に指定する数値の単位はピクセルです。

    [Space(20)]
    int _hp = 100;

詳細は下記をご参照下さい。

docs.unity3d.com

4.3. テキストエリアを複数行にする

文字列をパラメータとして扱う場合、キャラクタ名のように1行で問題ない場合もあればキャラクタの詳細説明のように複数行にわたる文字列を扱いたい場合もあります。後者の場合は複数行に渡ることを明示する必要があります。 このような場合にはTextAreaAttribute を使用します。

    [SerializeField]
    [Header("詳細情報")]
    [TextAreaAttribute]
    string _description;

詳細は下記をご参照下さい。

docs.unity3d.com

4.4. 見た目をきれいにした結果

以上の内容を適用して見た目をキレイにすると下記のようになります。

using UnityEngine;

public class InspectorSample : MonoBehaviour
{
    [Header("キャラクターのステータス")]
    [Header("基本情報")]
    [SerializeField]
    string _name;

    [Header("詳細情報")]
    [TextArea]
    [SerializeField]
    string _description;

    [Space(20)]
    [Header("パラメータ群")]
    [Header("キャラクターの体力")]
    [SerializeField]
    [Range(1, 65535)]
    int _hp = 100;

    [Header("無敵かどうか")]
    [SerializeField]
    bool _invisible;

    [Header("移動速度")]
    [SerializeField]
    [Range(1.0f, 64.0f)]
    float _speed = 2.0f; 

}

見た目をきれいにした

5. 複数の単純型をまとめた構造体を扱う

今までの例だと予め定義された型しか扱うことができないのですが、 やり方を工夫することで複雑な構造を扱うこともできるようになります。

5.1. データに構造を持たせる

データに構造を持たせるためには、構造を持った独自のclass を定義します(今回の例ではTalkData)。 これに加え、クラス宣言の手前に[Serializable]を付与します。

using System;
using UnityEngine;

[Serializable]
public class TalkData
{
    public float Probability = 100.0f;
    public string TalkMessage = ""; 
}

最後に、上記class (TalkData)型のメンバ変数をInspectorSample で宣言すれば、構造を持ったパラメータをInspector にて入力することができるようになります。 このような構造を持つパラメータは配列として扱うことでさらに複雑なパラメータとして表現できるようになります。

    [Header("セリフ候補一覧")]
    [SerializeField]
    TalkData[] _talkDataList; 

上記を踏まえてInspectorSample を以下のように変更してみました。

public class InspectorSample : MonoBehaviour
{
    [Header("キャラクターのステータス")]
    [Header("基本情報")]
    [SerializeField]
    string _name;

    [Header("詳細情報")]
    [TextArea]
    [SerializeField]
    string _description;

    [Header("セリフ候補一覧")]
    [SerializeField]
    TalkData[] _talkDataList; 

    // ...

}

Inspector 欄の見た目が下記のようになりました。

複雑な構造を扱う

上記の例だとセリフとその発生確率をペアにしたものをリスト化することができましたので、 確率計算により異なるセリフを発生させることができるようになります。

6. 終わりに

本記事でご紹介したSerializeField を活用することで、Inspector 欄でパラメータを指定することができるようになりました。 この機能は特にPrefab と一緒に使うと便利です。 例えば様々な敵をPrefab として作成しておき、 それぞれのPrefab に今回のInspectorSample のようなクラスをコンポーネントとして付与すると、 一つのPrefab で敵の見た目(モデリングデータ・マテリアル)とパラメータとをまとめて管理することができるようになります。

SerializeField を用いたInspector の拡張機能は非常に応用範囲が広いため、 是非積極的に活用してみましょう。