シリアライズとプロパティの描画

シリアライズとプロパティの描画


翻訳元のページ
快く翻訳と転載の許可を頂いたJasper Flick氏に感謝します。thank you!



<カスタムデータ>
シリアライズクラスとプロパティ描画のイントロダクション

このUNITY C#チュートリアルではシンプルなデーター構造と、その要求に応えるプロパティ描画を作ります。ここで学べることは

  • シリアライズクラスの利用法
  • カスタムプロパティ描画の作成
  • シリアライズプロパティの利用
  • GUIエディタのUnityの即応モードの利用(アンドゥやリドゥ、その他、通常unityエディタで要求される編集のサポート)

UnityC#スクリプトによる、あまり知られていないUNITYエディタの利用方法をあなたは知ることになります。
もしあなたがこれをコンプリートすれば他のチュートリアルも容易に理解する事が出来るでしょう。
学習する際はUnity version 4.3以上をご利用ください。

画像を表示

頂点カラー


あなたは必要であればUNITYにバラエティに富んだ独自のデータ形式、それら包括的なカスタムデータを独自に作成しコンポーネントとして組み込み利用する事ができます。
しかし、必要にせまられる度に小さなカスタムセットのデータをその都度作成して幾多の場所に作るべきではありません。
その代わりに、このチュートリアルで私達はデータをあたかもビルトインデータのように振舞わせるカプセル化されたシンプルなクラスデータを作成します。

今回のチュートリアルで私達は位置と色どちらも備えた頂点カラーのデーター構造を作り利用できるようにします(所謂、頂点カラーポリゴンのデータ)
新しい空のプロジェクトを作成し必要な変数を持った「ColorPoint」と名づけたC#スクリプトをプロジェクトに加えましょう。
(これはMonoBehaviourクラスを継承していない純粋なクラスです)

using UnityEngine;

public class ColorPoint {

  public Color color;
   public Vector3 position;
}

同時に私たちは新しいデータ形式をテストする為の「ColorPointTester」という名前の新しいスクリプトを作成します。
私達は単頂点と配列頂点を単ベクターと配列ベクターに照合する必要があります。
これから空のゲームオブジェクトと幾つかのコンポーネントを作成します。

using UnityEngine;

public class ColorPointTester : MonoBehaviour {
  
   public ColorPoint point;
   
   public ColorPoint[] points;
   
   public Vector3 vector;
   
   public Vector3[] vectors;
}

画像を表示

私達は新しく作成した型をインスペクタの中で見つけることは出来ません(この場合ColorPoint型のクラス変数)。
なぜならそれは今のところコンテンツとしてセーブされていないからです。
この問題を解決する為にSystem.Serializable属性を私達のクラスに追加します。これをすることによってpublicフィールドのクラスやデーターストリームは
シリアライズ化されエディタ内で代入が許可されるようになります。

using UnityEngine;
using System;

[Serializable]
public class ColorPoint {
  
   public Color color;
   public Vector3 position;
}

私達のデーターがインスペクタに今表示されるようになりました。私達は自由に編集しセーブできます。
また、このテストオブジェクトをプロジェクトビューにドラッグしてプレハブ化して、インスタンスとしてシーンに変数の変更を反映させる事が出来ます。

画像を表示 画像を表示  

画像を表示

インスペクタの見た目があまり良い状態ではないですね。
これを解決するには充分、熟考、設計した上でベクターとカラーの表示する情報を一行のラインに納める必要がありそうです・・・

プロパティを描画する


不幸にも、頂点と位置情報はそれに見合う複数のラインと幅広いインスペクタを必要としています。もう少しスペースをうまく利用できないでしょうか。
UnityEditor.CustomPropertyDrawer属性 と UnityEditor.PropertyDrawer を一緒に利用して拡張クラスに関連付けすれば私達が必要とする描画を型に対して行う事が出来ます。まずエディタクラスを利用する為の新しいEditorフォルダを作成します。その中に「ColorPointDrawer」という名前のクラスを作成しましょう。

関連資料:

using UnityEditor;
using UnityEngine;

[CustomPropertyDrawer(typeof(ColorPoint))]
public class ColorPointDrawer : PropertyDrawer {
}

画像を表示

この時点でインスペクタには有益な何かは何も表示されません。しかし、私達はpropertyDrawerの既定のOnGUIメソッドをオーバライドで変更して私達の望むバージョンにします。
このOnGUIメソッドは3つのパラメータを持ちます。最初にRect。これは私達のプロパティを描画する窓枠エリアです。2番目にプロパティそれ自身であるSerializedProperty。
3番目にGUIContent。これは私達が利用するプロパティのラベル定義です。

それでは GUIEditor.PrefixLabelメソッドで描画ラベルを利用し GUIEditor.PropertyField メソッドを利用して位置を指定してみましょう。
(tips:このコードの入力する際にインテリセンスを利用すると便利です。例えば「ov」と入力して続く入力をインテリセンスに任せて「override」と入力後、スペースキーを押してオーバライドする関数候補を表示させOnGUIを選択すると一気に入力できます)

関連資料

  public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
       EditorGUI.PrefixLabel(position, label);
       EditorGUI.PropertyField(position, property.FindPropertyRelative("position"));
   }

画像を表示
PropertyDrawerによりラベルが重複して表示されている様子

頂点カラーのラベルと頂点位置のラベルがオーバーラップ表示されています。この頂点位置ラベルをGUIContent.none.をオーバーライドして取り除きます。


  public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
       EditorGUI.PrefixLabel(position, label);
       EditorGUI.PropertyField(position, property.FindPropertyRelative("position"), GUIContent.none);
   }

画像を表示
ひとつのラベルにした状態。まだ重複している

位置情報の矩形がそのままなので頂点位置情報のラベルが重複した状態になります。
PrefixLabelメソッドの返り値rectを代用して矩形のスペースを調節し、この不具合を回避します。

public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
      Rect contentPosition = EditorGUI.PrefixLabel(position, label);
       EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("position"), GUIContent.none);
   }

画像を表示
重複表示がなくなった状態。間違った位置が正された

見た目が良くなりました。しかしベクターの位置情報の配列要素が右に片寄っています。なぜなら PropertyFieldメソッドのアジャストがエディタのインデントレベルに合わせられているからです。インデントレベルはint型のEditorGUI.indentLevelを経由してセットします。自動的なインデントを除去して私達はシンプルなゼロをセットします。

  public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
       Rect contentPosition = EditorGUI.PrefixLabel(position, label);
       EditorGUI.indentLevel = 0;
       EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("position"), GUIContent.none);
   }

画像を表示


プレフィックスを確定させる


インスタンスのプレハブの値を変更するとプレフィクスラベルがボールド体(太字)になり知らせてくれます。
(通常ここでプレフィクスラベルの上で右クリックするとプレハブの元の値に戻すなどのアクションメニューが表示されます)
このアクションが出来ない状態です。私達が頂点カラーに対して出来る事は直接の編集だけで簡単に配列要素を消したり複製する事は出来ません。

アクションの動作を解決するためにプロパティがスタートの時どんな状態だったか終了状態の時どんな状態だったかエディタに聞く必要があります。
それには EditorGUI.BeginPropertyメソッドで新しいラベルとスタート時のプロパティのシグナルと EditorGUI.EndProperty メソッドのシグナルを利用します。
私達はコンテキストメニューを経由したサービスを期待しても良い筈です。

public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
      label = EditorGUI.BeginProperty(position, label, property);
       Rect contentPosition = EditorGUI.PrefixLabel(position, label);
       EditorGUI.indentLevel = 0;
       EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("position"), GUIContent.none);
       EditorGUI.EndProperty();
   }

画像を表示
データに対するアクション機能をサポートしました

カラーを加える


ではカラープロパティを描画出来るようにしましょう。位置情報を縮小してスペースを確保したのち行の間に追加します。
表示は位置情報とカラーを加えた4要素になります。位置情報を75%の水平スペースに指定します。カラーは自動的に25%になります。
カラーラベルは小さく表示された状態になり利用できるようになります。

  public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
       label = EditorGUI.BeginProperty(position, label, property);
       Rect contentPosition = EditorGUI.PrefixLabel(position, label);
       contentPosition.width *= 0.75f;
       EditorGUI.indentLevel = 0;
       EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("position"), GUIContent.none);
       contentPosition.x += contentPosition.width;
       contentPosition.width /= 3f;
       EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("color"), new GUIContent("C"));
       EditorGUI.EndProperty();
   }

画像を表示
カラーが一緒に表示された。でもあまり良くないね

私たちが使うにはこのラベルはちょっと短かすぎます。このままではカラーデーターをこのスペースでは押せないとクレームが付きます。
ラベルの幅が固定化されているので内容に対し沿っていないようです。あなたはEditorGUIUtility.labelWidthを利用してラベルの幅を調節する必要があります。
幅を14ピクセルにしてみましょう。

関連資料

public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
      label = EditorGUI.BeginProperty(position, label, property);
       Rect contentPosition = EditorGUI.PrefixLabel(position, label);
       contentPosition.width *= 0.75f;
       EditorGUI.indentLevel = 0;
       EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("position"), GUIContent.none);
       contentPosition.x += contentPosition.width;
       contentPosition.width /= 3f;
       EditorGUIUtility.labelWidth = 14f;
       EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("color"), new GUIContent("C"));
       EditorGUI.EndProperty();
   }

画像を表示
ラベルのサイズが適正になりました

行に求められるもの


既定の位置情報は1行ですこれをインスペクタ内で2行に出来ないでしょうか。もちろん出来ます。
GetPropertyHeightメソッドをオーバライドすれば縦のスペースを私たちの必要と思う値に書き換える事が出来ます。
既定で1行は16ピクセルです。2行目は18ピクセル足された位置になります。従って16ピクセルの2行の間のマージンは2ピクセルになります。
Screen.width を利用してインスペクタパネルの幅を比較し私たちが利用しやすい値を返しましょう。
このコードではインスペクタの幅が333ピクセル以下になると複数行の扱いに切り替わるようにしています。

  public override float GetPropertyHeight (SerializedProperty property, GUIContent label) {
       return Screen.width < 333 ? (16f + 18f) : 16f;
   }

画像を表示

これでインスペクタ内が窮屈になると私たちの望む縦幅になります。しかし、まだ充分ではありません。
完璧に動かす為にはあと4行のコードの世話が必要です。
1行目は縦の矩形が2行になったか確かめています。
2行目で私たちが必要とする16ピクセルの高さにカラープロパティ含めた1行を収めています。
3行目ではプロパティラベルが行からはみ出して改行されるようにしています。
4行目ではEditorGUI.IndentedRectメソッドを利用して1行増やしたインデントレベルの位置情報を適用しています。

public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
      label = EditorGUI.BeginProperty(position, label, property);
       Rect contentPosition = EditorGUI.PrefixLabel(position, label);

      if (position.height > 16f) {
           position.height = 16f;
           EditorGUI.indentLevel += 1;
           contentPosition = EditorGUI.IndentedRect(position);
           contentPosition.y += 18f;
       }

      contentPosition.width *= 0.75f;
       EditorGUI.indentLevel = 0;
       EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("position"), GUIContent.none);
       contentPosition.x += contentPosition.width;
       contentPosition.width /= 3f;
       EditorGUIUtility.labelWidth = 14f;
       EditorGUI.PropertyField(contentPosition, property.FindPropertyRelative("color"), new GUIContent("C"));
       EditorGUI.EndProperty();
   }

画像を表示

これで私たちは良好なエディタ上で直接編集できる頂点カラー情報を持ったカスタムクラスを作成する事が出来ました。
これはアンドゥやリドゥ、プレハブやマルチオブジェクト等のunityで通常要求される編集が全てサポートされています。
インスペクタには使いやすいように一行で表示され他のプロジェクトでも再利用可能なものです。

メニュー




  • 最終更新:2014-06-13 10:42:55

このWIKIを編集するにはパスワード入力が必要です

認証パスワード