Aizu-Progressive xr Lab blog

会津大学のVR部であるA-PxLの部員が持ち回りで投稿していくブログです。部員がそれぞれVRに関する出来事やVRにちなんだことについて学んだことを書いていきます。

連絡はサークルTwitterのDMへお願いします。
「面白法人カヤックVR分室」としても活動しています。詳細はこちら

Unityのエディタ拡張で快適に開発を進めよう

皆さんこんにちは。A-PxLゲーム部所属の森口です。
もう3月ですね新生活が迫っている方もいるのではないのでしょうか。ついこの間始まったと思っていた私の大学生活も最終学年に突入しようとしています。
今日はタイトルにもある通りUnityのエディタ拡張に触れてコンテンツの開発をより良いものにしていこうという記事です。

エディタ拡張とは

「そもそもエディタ拡張ってなんだよ?」とか「名前は知ってる」という方向けに簡単にエディタ拡張について説明していきたいと思います。
Unityエディタ上にはアニメーションを作成するウインドウやプロジェクトの設定をするウインドウ、はたまたオブジェクトの情報が乗っているインスペクターなどが多く存在します。
Unityはとても良く作られているので基本的にこれらのものに対して不便に感じることは少ないと思います。しかし例えばゲームで敵のレベルやその数などを再生して確認→不満だったらスクリプトをいじって修正→再生して確認→不満d(ry みたいにするのは少し面倒ですよね。他にも再生しないでエディタ上で動作を確認して見たいと思うこともあると思います。そんなときに使えるのがエディタ拡張(インスペクタ拡張)です。
具体的にどんなことができるかというとUnityで標準に用意されているウィンドウの自分好みのものを作成したり、インスペクターの表示を編集しやすいようにいじることができます

f:id:aizu-vr:20200305004936p:plain
自作したウインドウ
f:id:aizu-vr:20200305004942p:plain
自作したインスペクター

エディタ拡張を始める前に

Editorという名前のフォルダをAssetフォルダ内に作ってください。そのフォルダに拡張のためのスクリプトを配置しないと正しく反映されません。また、このフォルダはAsset以下であればどのサブフォルダの下でも良いそうなのでAsset/UI/Editorみたいな感じでも大丈夫です。

ウインドウを作る

まずは1枚目の画像のようなウインドウを作っていきます。ウインドウはUnityEditor.EditorWindowを継承して作ります。

using UnityEngine;
using UnityEditor;


public class MyEditor : EditorWIndow{
    private string textField;
    private string textArea;
    
    
    [MenuItem("Window/MyEditor")]
    static void Open(){
        EditorWindow.GetWindow<MyEditor>("MyEditorTitle");
    }

    void OnGUI(){
        EditorGUILayout.LabelField("A-PxLで始めるエディタ拡張");
        //ボタン
        if(GUILayout.Button("自爆ボタン")) Debug.Log("自爆しました");
        //テキスト(1行)
        GUILayout.Label("TextField");
        textField = GUILayout.TextField(textField);
        //テキスト(複数行)
        GUILayout.Label("TextArea");
        textArea = GUILayout.TextArea(textArea);
    }
} 

こんな感じのコードを書くと以下のようなウィンドウになります。

[MenuItem("Window/MyEditor")]

この部分でUnityエディタ上部のメニューバーにあるWindowをクリックしたときに出てくるプルダウンにMyEditorという項目を追加しておりそこをクリックするとその下に記述してある関数が呼ばれます。

static void Open(){
    EditorWindow.GetWindow<MyEditor>("MyEditorTitle");
}

GetWindowの引数として与えている文字列はウインドウのタイトルになります。

void OnGUI(){
    EditorGUILayout.LabelField("A-PxLで始めるエディタ拡張");
    //ボタン
    if(GUILayout.Button("自爆ボタン")) Debug.Log("自爆しました");
    //テキスト(1行)
    GUILayout.Label("TextField");
    textField = GUILayout.TextField(textField);
    //テキスト(複数行)
    GUILayout.Label("TextArea");
    textArea = GUILayout.TextArea(textArea);
}

ウインドウに配置したいものをここへ記述します。機能はコメントしてあるとおりです。 エディタに配置できるものはたくさんあるので色々調べてみてください!

インスペクタ拡張

今度はインスペクタをカスタマイズします。インスペクタをカスタマイズするとはそのまんまの意味で、スクリプトなどをアタッチするとインスペクタに表示される部分をデフォルトの表示からカスタマイズすることができます。(上の”自作したインスペクタ”画像参照)

拡張するスクリプトを用意

まずはインスペクタを拡張したいスクリプトを用意します。流れとして他のスクリプトから変数をいじることになるのでプロパティを用意するなどしてアクセスできるようにしておいてください。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MyInspector : MonoBehaviour {
    [SerializeField] private int m_hp;
    [SerializeField] private int m_attack;
    [SerializeField] private float m_speed;
    [SerializeField] private Text m_playerName;
    [SerializeField] private Text m_introduction;
    
    public int Hp { get => m_hp; set => m_hp = value; }
    public int Attack { get => m_attack; set => m_attack = value; }
    public float Speed { get => m_speed; set => m_speed = value; }
    public Text PlayerName { get => m_playerName; set => m_playerName = value; }
    public Text Introduction { get => m_introduction; set => m_introduction = value; }
    
}

インスペクタを編集するスクリプトを用意

次に上のスクリプトのインスペクタを編集するスクリプトを用意します。
ウインドウのとき同様にEditorフォルダに配置してください。
こちらのスクリプトはUnityEditor.Editorを継承させてください

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;

[CustomEditor(typeof(MyInspector), true)]
[CanEditMultipleObjects]
public class MyInspectorEditor : Editor {
    private SerializedProperty hp;
    private SerializedProperty attack;


    private float attackMin;
    private Text name;
    private Text intro;
    
    private void OnEnable() {
        hp = serializedObject.FindProperty("m_hp");
        attack = serializedObject.FindProperty("m_attack");
    }

    public override void OnInspectorGUI() {
        //シリアライズオブジェクトの更新
        serializedObject.Update();
        
        //targetで拡張先クラスのデータを取得
        MyInspector myIns = target as MyInspector;
        
        //スライダーで体力と攻撃力を表示できるように
        EditorGUILayout.IntSlider(hp, 0, 100);
        EditorGUILayout.IntSlider(attack, 0, 100);
        
        //シリアライズオブジェクトの変更を更新
        serializedObject.ApplyModifiedProperties();
        
        //ここからtargetを使った方
        //速さもスライダーで
        myIns.Speed = EditorGUILayout.Slider("速さ", myIns.Speed, 0f, 100f);

        name = myIns.PlayerName;
        intro = myIns.Introduction;
        
        //プレイヤー名
        name.text = EditorGUILayout.TextField("プレイヤー名", name.text);
        name.color = EditorGUILayout.ColorField("文字色", name.color);
        name.fontSize = EditorGUILayout.IntField("フォントサイズ", name.fontSize);
        
        //自己紹介
        EditorGUILayout.LabelField("自己紹介");
        intro.text = EditorGUILayout.TextArea(intro.text, GUILayout.Height(100));
        intro.color = EditorGUILayout.ColorField("文字色", intro.color);
        intro.fontSize = EditorGUILayout.IntField("フォントサイズ", intro.fontSize);
        
        //エディットモードでリアルタイムに反映が見れるように
        EditorUtility.SetDirty(target);
        
    }
}

全体はこんな感じです。
配置してあるものの説明はコメントにしてあるとおりなのでそれ以外の部分の説明だけしたいと思います。

中身に入る前に

[CustomEditor(typeof(MyInspector), true)]
[CanEditMultipleObjects]

クラス名の上に書いてあるこれらですが、CustomEditor(typeof(className))の方を記述することで指定したスクリプトのインスペクタを拡張する事ができます。第2引数は継承したクラスにも反映させるか否かです。
もう一つの方はオブジェクトを複数個生成したときにそれぞれで編集できるようにするものになります。

また、ウインドウのときはOnGUI()に配置などを記述したのに対しインスペクタ拡張の場合はOnInspectorGUI()に記述するようにしてください。

データの取得方法

インスペクタ拡張をした際のデータの取得方法は2つありSerializedObjectを使う方法とtargetをキャストして使う方法です。

SerializedObjectを使う場合はSerializedProperty型の変数を用意しそれに取得したい変数を割り当ててやるみたいな感じになります。上のスクリプトではOnEnableのところで割当を行っています。また、以下のように記述してやることでSerializedObjectを更新します。データの変更前後でそれぞれ行います。

serializedObject.Update();
//諸々の処理
serializedObject.ApplyModifiedProperties();

targetを使う場合は他のスクリプト内でインスタンスを取得してプロパティやなどをいじるのと同様に記述すればいいだけなので個人的にはこっちのほうがわかりやすくて好きです。

MyInspector myIns = target as MyInspector;

ボタンやパラメータの配置などは概ねウインドウと書き方は一緒なので配置できるものなどいろいろ調べてみるとおもしろいかもですね。

出来上がったもの

できたものがこちらになります。比較用に拡張しなかったときのものもおいておきます。

f:id:aizu-vr:20200307152136p:plain
インスペクタ拡張前
f:id:aizu-vr:20200307152159p:plain
インスペクタ拡張後
また、テキスト部分がリアルタイムに変更されるよーってやつを動画にしたのでそれもどうぞ

youtu.be

話が長い。3行でまとめて

ではこの記事のまとめです。

  • エディタ拡張は自分の好みにUnityエディタの表示をいじれる機能

  • エディタ拡張には大きく分けてウインドウを生成するものとインスペクタの拡張の2種類がある。

  • 中身はどこに何を配置するか、どのデータを扱うかくらいなのでそこまで難しく考えなくてOK!

こんな感じでしょうか。あくまで紹介記事程度に収める気持ちで書いたので配置できるものや詳しい使い方などは以下のリンクが参考になるかなと思います。
この記事を見ていただいたみなさんがUnityで快適に開発を進めてくれればと思います。

参考記事

docs.unity3d.com

caitsithware.com

xr-hub.com

gametukurikata.com

会津大学VR部の部員が持ち回りで投稿していくブログです。特にテーマに縛りを設けずに書いていきます!