進捗
チュートリアルの実装三日目。ひとまず考案したシナリオについてはすべて実装し、見た目の調整が必要だったところも修正したのでチュートリアルの実装については作業完了。
カスタムプロパティドローワーによるインスペクタのデザイン変更
チュートリアル中は出現するタスクを固定化して進める仕様としたため、チュートリアル用のデータを用意してゲーム中に反映する仕組みを作成した。この際、データ入力はエディタのインスペクタから行えるようにしたが、エディタに表示されるデザインが非常に見づらく、入力するにも変更を加えるにも効率が悪かったので、カスタムエディタを使用し、インスペクタ上に表示されるプロパティの見せ方を変更した。
以下のサイトを参考に実装。
今回エディタの変更を行ったプロパティ(クラス)は次のコード。内容としてはTaskTypesとScrewTypesが列挙型のプロパティで、インスペクタ上ではポップアップに列挙値を表示して選択する。今回はマネージャクラスがこのクラスのオブジェクトをリスト化して保持しており、100個程度のデータを設定する必要がある。
    [System.Serializable]
    public class ScheduleScrewItem
    {
        public TaskTypes TaskType;
        public ScrewTypes ScrewType;
        public int ScrewNum;
    }
デフォルトの状態だとインスペクタ上には次のように表示される。

見ての通りプロパティ一つに対して一行分の描画範囲を使っているので、高さが余計にあるのと、折り畳みで表示されている場合は一つ一つ展開して内容を見ることになるので非常に使いづらい。
ここで、インスペクタからの編集が楽になる等に次のカスタムプロパティドローワーを実装。
    [CustomPropertyDrawer(typeof(ScheduleScrewItem))]
    public class ScheduleScrewItemInspector : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var taskTypeProperty = property.FindPropertyRelative("TaskType");
            var screwTypeProperty = property.FindPropertyRelative("ScrewType");
            var screwNumProperty = property.FindPropertyRelative("ScrewNum");
            var previousTaskTypeIndex = taskTypeProperty.enumValueIndex;
            var previousScrewTypeIndex = screwTypeProperty.enumValueIndex;
            var previousScrewNum = screwNumProperty.intValue;
            // コントロールを追加
            var taskTypesArray = System.Enum.GetNames(typeof(TaskTypes));
            var screwTypesArray = System.Enum.GetNames(typeof(ScrewTypes));
            var margin = 3f; // コントロールを横に並べるのでマージンを取る
            var widthAndMargin = position.width / 4;
            var width = widthAndMargin - margin;
            // 実際にコントロールを追加する処理
            EditorGUI.LabelField(new Rect(position.x, position.y, width, position.height), label.text);
            var selectedTaskTypeIndex = EditorGUI.Popup(new Rect(position.x + (margin / 2) + widthAndMargin, position.y, width, position.height), previousTaskTypeIndex, taskTypesArray);
            var selectedScrewTypeIndex = EditorGUI.Popup(new Rect(position.x + (margin / 2) + widthAndMargin * 2, position.y, width, position.height), previousScrewTypeIndex, screwTypesArray);
            var screwNum = EditorGUI.IntField(new Rect(position.x + (margin / 2) + widthAndMargin * 3, position.y, width, position.height), previousScrewNum);
            // 値を変えていれば、キーを取得してシリアライズ情報を更新
            if (previousTaskTypeIndex != selectedTaskTypeIndex)
            {
                taskTypeProperty.enumValueIndex = selectedTaskTypeIndex;
                // 修正があったらシリアライズしてあるオブジェクトに変更を反映
                taskTypeProperty.serializedObject.ApplyModifiedProperties(); 
            }
            if (previousScrewTypeIndex != selectedScrewTypeIndex)
            {
                screwTypeProperty.enumValueIndex = selectedScrewTypeIndex;
                // 修正があったらシリアライズしてあるオブジェクトに変更を反映
                screwTypeProperty.serializedObject.ApplyModifiedProperties(); 
            }
            if (previousScrewNum != screwNum)
            {
                screwNumProperty.intValue = screwNum;
                // 修正があったらシリアライズしてあるオブジェクトに変更を反映
                screwNumProperty.serializedObject.ApplyModifiedProperties();
            }
        }
    }
注意点としてはEditorGUILayoutが使用できないため、各プロパティを表示する座標とサイズについては自前で計算して設定する必要がある。そのため、コントロールを追加しているところでゴリゴリ座標計算してあるあたりが汚いが、とりあえず動けばOKの精神で進めている。
上記のコードを反映した結果が次の画像。

いちいちプロパティを展開しなくても一覧でプロパティの内容を確認・修正できるため非常に設定しやすくなった。設定するデータが多かったり、調整の頻度が高いプロパティはこのような方法でインスペクタ上のデザイン変更を行うと開発効率が向上してよいと思う。
今回は1行にまとめるという趣旨だったので特にやらなかったが、デザインを2行分の高さを使用するような見た目にする場合はカスタムプロパティードローワーに次のような処理を加える必要があるとのこと。
        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            return base.GetPropertyHeight(property, label) + EditorGUIUtility.singleLineHeight;
        }
試しに上記のクラスに追加すると次のような見た目になる。

残作業
- 難易度選択微修正
 - 音声をつける
 - 広告の実装
 - プライバシーポリシー作成
 - プロモーションの作成
 - リリース
 
チュートリアルの作成が終わったので次は音声を付ける作業に入ろうと思うが、チュートリアルを作っている最中に難易度ごとのクリア条件等がどこにも明示されていないことに気が付いたので、その辺の修正作業を先にやろうと思う。


