FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »エディタ拡張
このページの記事一覧

【Unity】【C#】インスペクタで入力不可(Disable)な属性を作る  


 インスペクタに表示するだけで、インスペクタからは入力はできないフィールドを作りたいときは良くある。デバッグ用に表示だけしたいときとかね。

 サードパーティのインスペクタの拡張アセットなどにはよく付いているのだが、インポートするまでもなく、簡単に済ませたいときなどに便利なので掲載しておこう。何でもかんでもアセット導入すると、ファイルが増えすぎてビルド時間も長くなるしね。自作なら色々応用効くのも魅力だ(笑)。

(※) Unity 2018.4.8f1 / Windows10(x64) で確認



 必要なスクリプトは PropertyAttribute を継承した属性用スクリプトと、PropertyDrawer を継承したエディタ(インスペクタ)用スクリプトになる。エディタ表示用スクリプトは「Editor」フォルダを作って、そこに置いておく必要がある。

PropertyAttribute
PropertyDrawer



●属性用スクリプト:DisableAttribute.cs
using UnityEngine;

public class DisableAttribute : PropertyAttribute
{
//※中身は空で良い
}

●エディタ表示用スクリプト:DisableDrawer.cs
using UnityEngine;
using UnityEditor;

[CustomPropertyDrawer(typeof(DisableAttribute))]
public class DisableDrawer : PropertyDrawer
{
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
return EditorGUI.GetPropertyHeight(property, label, true);
}

public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
GUI.enabled = false; //ここで入力不可にしている
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true; //元に戻しておく
}
}


 例として適当な GameObject にテスト用スクリプトをアタッチして、インスペクタの表示を見てみよう。



●テスト用スクリプト:DisableAttributeTest.cs(※名前は任意)
using UnityEngine;

public class DisableAttributeTest : MonoBehaviour
{

[Disable] public string str = "hoge"; //インスペクタからは入力できない
[SerializeField, Disable] int num = 1; //インスペクタからは入力できない

[HideInInspector] public float f = Mathf.PI; //※これは単にインスペクタに表示しない例
}

 今回作ったのは "[Disable]" という属性だ。以前のバージョンでは "[DisableAttribute]" と書く必要があったらしいが、現在の Unity バージョンでは "Attribute" は省略できる。

 "[HideInInspector]" は単に表示したくないときの属性で、元々 Unity にビルトインされている。他にも色々な属性があるので、少し古いが、以下に資料を載せておこう。

UnityのAttribute(属性)についてまとめてメモる。

 アセットによっては "[ReadOnly]" などになってるかもだが、C# Job System でも使われる属性なので、今後はコンフリクトするかも知れない。その場合は namespace を付けておくのも一つの手だ。もっと色々自作したいなら、以下も少し古いが、資料を載せておこう。

自分だけのPropertyDrawerを作ろう!






(関連記事)
【Unity】インスペクタの値を保持したまま変数をリネームする
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる
【Unity】【C#】UnityEvent, Action, delegate, interface でのコールバック実装方法とインスペクタでの登録
【Unity】【C#】インスペクタの表示項目を動的に変更する
【Unity】【C#】独自のギズモ(Gizmo)を表示する


関連記事
スポンサーサイト



category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  エディタ拡張 
tb: 0   cm: --

【Unity】インスペクタの値を保持したまま変数をリネームする  


 最近は色々なオープンソーズを利用しているのだが、時折アップデートしたらプロジェクトが破壊されることもある。その1つがインスペクタの参照だろうか?

 残念ながら、インスペクタに設定されていたフィールド名を変えると、参照が外れることはよくあることだ。これが大量にあったら・・・まぁ、まともに動かなくなることは想像できるだろう(笑)。

 これは FormerlySerializedAs というものを使えば、回避できるのだが、微妙に凡例が長かったり、必要なものが欠けてたりと、ちょっとわかりずらいと思ったので、もっと簡単な例をメモ代わりに書いておこうと思った。

FormerlySerializedAsAttribute(公式マニュアル)

(※) Unity 2018.4.6f1 / Windows10(x64) で確認



■簡単な手順

 例として、ボタンの参照しているフィールドを

[SerializeField] private Button hogeButton;
 ↓
[SerializeField] private Button _hogeButton;


のように変更したいとする。インスペクタの表示名は変わらないが(hogeButton, _hogeButton, m_hogeButton などはコード内では別物だが、インスペクタでの表示は同じになる)、普通にリネームすると、これまで設定していたボタンへの参照が外れる。これをそのまま保持してリネームしたいという感じだ。

1.「using UnityEngine.Serialization;」をファイルの先頭に追加する。

2.[FormerlySerializedAs] の属性を加え、現在のフィールド名(変更前の名前)を入れる。

using UnityEngine.Serialization;

public class Example : MonoBehaviour
{
[FormerlySerializedAs("hogeButton")]
[SerializeField] private Button hogeButton;
}


3.フィールド名をリネーム(リファクタリング)して、一旦、シーンを保存する。また、プレファブ化してるものは個々に保存[Override]しておかないと参照が外れることがある(※要エディタでプレイ前に保存)。

using UnityEngine.Serialization;

public class Example : MonoBehaviour
{
[FormerlySerializedAs("hogeButton")]
[SerializeField] private Button _hogeButton;
}


4.インスペクタで値が保持されているのを確認できたら、FormerlySerializedAs 属性や using は削除しても良い(※複数から参照しているときは注意)。

using UnityEngine.Serialization;

public class Example : MonoBehaviour
{
[SerializeField] private Button _hogeButton;
}


 まぁ、それでも大量にあったら大変だけどね。できればリリース後は、public なものや過去のリソースに影響を与える変更は、極力控えて貰いたいなぁ・・・(バグとかクリティカルなものならともかく・・・ただの命名規則の変更で無駄に時間とられるのは勘弁・・・)。|||orz


(参考)
FormerlySerializedAsAttribute(公式マニュアル)
PrefabやSceneのSerializeされた変数の値を保持したままリネームする
FormerlySerializedAs(テラシュールブログ)






(関連記事)
【Unity】【C#】インスペクタの表示項目を動的に変更する
【C#】【Unity】enum 型と string, int 型の相互変換など
【Unity】【C#】インスペクタでの UnityEvent のコールバック登録の有無を調べる


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityトラブルシューティング  Unityリファレンス  エディタ拡張 
tb: 0   cm: --

【Unity】【C#】インスペクタの表示項目を動的に変更する  


 インスペクタである値を設定すると、それに対応したパラメタ設定だけを表示したいときってあるよね。幸いにも「Editor」クラスを継承して「OnInspectorGUI()」 を override すれば、元のスクリプトを変更せずに表示を切り替えられるので、試しに設定項目の表示切り替えをやってみよう。

(※) Unity 5.4.3f1 / Windows10 で確認



 せっかくなので、用例は前回作った「独自のギズモを表示する」のスクリプトをそのまま使って、形状(Shape)を切り替えたときに「球」「箱」「線」に対応した設定パラメタだけを表示するようにしてみよう。各形状パラメタは「MySphere」「MyCube」「MyLine」クラスで保持されているが、実際に必要なのは「Shape」(形状)に対応するパラメタだけなので、1つだけ表示した方がインスペクタがスッキリする。以下は元の「独自ギズモを表示する」スクリプトとなる。

●球/箱/線状を切り替えられる独自ギズモを表示するスクリプト(→前回のと同じもの
using UnityEngine;
using System.Collections;

/** 独自 Gizmo (球,箱,線 状) */
public class MyGizmo : MonoBehaviour {

/** 形状を表す定数 */
[System.Serializable]
public enum Shape {
Sphere = 0,
Cube,
Line,
}

public bool visible = true; //可視状態
public Color color = Color.yellow; //色
public Shape shape = Shape.Sphere; //形状

public MySphere sphereParam = new MySphere(0, 0, 0, 1); //球状のパラメタ
public MyCube cubeParam = new MyCube(0, 0, 0, 1, 1, 1); //箱状のパラメタ
public MyLine lineParam = new MyLine(0, 0, 0, 1, 1, 1); //線状のパラメタ

void OnDrawGizmos() {
if (!visible) {
return;
}

Gizmos.color = color;

//Gizmo はワールド座標指定なので、相対座標指定の場合はマトリクス変換で移動する
Gizmos.matrix = Matrix4x4.TRS(this.transform.position, this.transform.rotation,
this.transform.localScale);
switch (shape) {
case Shape.Sphere:
Gizmos.DrawSphere(sphereParam.center, sphereParam.radius);
break;

case Shape.Cube:
Gizmos.DrawCube(cubeParam.center, cubeParam.size);
break;

case Shape.Line:
Gizmos.DrawLine(lineParam.from, lineParam.to);
break;
}
}
}

/** 球状パラメタ */
[System.Serializable]
public class MySphere
{
public Vector3 center = Vector3.zero;
public float radius = 1f;

public MySphere(float x, float y, float z, float radius) {
this.center = new Vector3(x, y, z);
this.radius = radius;
}
}

/** 箱状パラメタ */
[System.Serializable]
public class MyCube
{
public Vector3 center = Vector3.zero;
public Vector3 size = Vector3.one;

public MyCube(float x, float y, float z, float scaleX, float scaleY, float scaleZ) {
this.center = new Vector3(x, y, z);
this.size = new Vector3(scaleX, scaleY, scaleZ);
}
}

/** 線状パラメタ */
[System.Serializable]
public class MyLine
{
public Vector3 from = Vector3.zero;
public Vector3 to = Vector3.one;

public MyLine(float x1, float y1, float z1, float x2, float y2, float z2) {
this.from = new Vector3(x1, y1, z1);
this.to = new Vector3(x2, y2, z2);
}
}

 このスクリプトでは画像の一番上のように各形状のパラメタがインスペクタに全て表示される。このパラメタ部分を「Shape」(形状定数)を変更したら、それに対応するパラメタだけを表示するように、もう1つ「MyGizomoEditor」(名前は任意で良い)を作成しよう。完成したスクリプトは以下のようになる。なお、カスタムエディタ(インスペクタ)機能のスクリプトは「Editor」というフォルダを作って、そこに置く必要がある(※ルートでなくても良い)。


●MyGizmo の Shape によってインスペクタを動的に変更するスクリプト
using UnityEngine;
using System.Collections;
using UnityEditor;

[CustomEditor (typeof(MyGizmo))]
public class MyGizmoEditor : Editor {

public override void OnInspectorGUI() {
MyGizmo obj = target as MyGizmo;

obj.visible = EditorGUILayout.Toggle("Visible", obj.visible);
obj.color = EditorGUILayout.ColorField("Color", obj.color);

obj.shape = (MyGizmo.Shape)EditorGUILayout.EnumPopup("Shape", obj.shape);

EditorGUI.indentLevel++; //インデントを入れる

switch (obj.shape) {
case MyGizmo.Shape.Sphere:
obj.sphereParam.center = EditorGUILayout.Vector3Field("Center", obj.sphereParam.center);
obj.sphereParam.radius = EditorGUILayout.FloatField("Radius", obj.sphereParam.radius);
break;

case MyGizmo.Shape.Cube:
obj.cubeParam.center = EditorGUILayout.Vector3Field("Center", obj.cubeParam.center);
obj.cubeParam.size = EditorGUILayout.Vector3Field("Size", obj.cubeParam.size);
break;

case MyGizmo.Shape.Line:
obj.lineParam.from = EditorGUILayout.Vector3Field("From", obj.lineParam.from);
obj.lineParam.to = EditorGUILayout.Vector3Field("To", obj.lineParam.to);
break;
}

EditorGUI.indentLevel--; //インデントを戻す

//obj.iconImage = EditorGUILayout.TextField("Icon Image", obj.iconImage); //アイコン画像

EditorUtility.SetDirty(target);
}
}

 注意点としては、継承するのはいつもの「MonoBehaviour」ではなく「Editor」(要:using UnityEditor;)であることと、「[CustomEditor (typeof(MyGizmo))]」「MyGizmo obj = target as MyGizmo;」のようにカスタム対象となるスクリプト(ここでは「MyGizmo」)を指定することだ。

 あとは「OnInspectorGUI()」内で「EditorGUILayout」(または「EditorGUI」)を使って、すべての表示項目の入力フィールド(チェックボックスやスライダーなども同様)を表示し、戻値をセットしていくだけだ。クラスや構造体のメンバ(プロパティ)なども同様にすれば良い。アイコン画像についてはコメントアウトしてあるが、これは前回の「ギズモのアイコンを表示する」に対応する場合の例なので記事を参照して欲しい。なお、ここで表示しなかったものは全て非表示となる。つまりある値によって表示する項目を分岐すれば、表示切り替えが可能となるわけだ。

 他にも入力値制限や細かいカスタムインスペクタの使い方などは以下に資料を載せておこう。

(参考資料)
カスタムエディタ編 Inspectorが変わる!
インスペクタで設定できる値を動的に変更する【Unity】【エディタ拡張】





(関連記事)
【Unity】インスペクタの値を保持したまま変数をリネームする
【Unity】【C#】独自のギズモを表示する
【Unity】【C#】FPS(フレームレート)をリアルタイムに測定して表示するv2(4隅選択可能で、画面サイズの変更にも対応版)


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  エディタ拡張 
tb: 0   cm: --

【Unity】【C#】独自のギズモ(Gizmo)を表示する  


 特殊な当たり判定やオブジェクトに対しての当たり線などに、独自のギズモを利用すると見た目わかりやすい事がある。ギズモはあくまでエディタ上だけのものであり、実際のゲームでは表示されないので、開発を進める上では利用しない手はないだろう。ググッても思ったよりサンプルが少なかったので、簡単な例を書いてみることにした。アタッチするだけでいつでも使えるので非常に便利だ。

(※) Unity 5.4.3f1 / Windows10 で確認



反射レーザー(Wave51以降)の当たり判定は円形コライダを等間隔に置いて実装している(球状Gizmoで可視化)


Wave50のボスのミサイル発射位置と方向は同一線上に等間隔に配置した砲台を回転し翼に合わせている(線状Gizmoで可視化)


Unityチュートリアルの2Dシューティングにレーザーやらミサイルやらボス戦やらを付け加えてゲームとして楽しめるように超改造してみた



■球状 Gizmo
using UnityEngine;
using System.Collections;

/** 球状 Gizmo */
public class MyGizmoSphere : MonoBehaviour {

public bool visible = true; //可視状態
public Color color = Color.yellow; //色
public Vector3 center = Vector3.zero; //中心(相対座標)
public float radius = 1f; //半径

void OnDrawGizmos() {
if (!visible) {
return;
}

Gizmos.color = color;

//Gizmo はワールド座標指定なので、相対座標指定の場合はマトリクス変換で移動する
Gizmos.matrix = Matrix4x4.TRS(this.transform.position, this.transform.rotation,
this.transform.localScale);
Gizmos.DrawSphere(center, radius);
}
}


球状ギズモを縦長にしてみる

設定例

 使い方はギズモを付けたい GameObject にスクリプトを追加するだけだ。ギズモの形状やその他のメソッドなどはオンラインマニュアルに載っているので、色々試してみるといいだろう。

 注意点としては座標の指定などはワールド座標なので、そのままだと少し使いづらい点だ。階層化されているオブジェクトなどに設置するには相対座標の方がわかりやすいので、その場合は Gizmos.matrix(マトリクス変換)を使って移動すれば良い。Gizmos.DrawSphere() の中心座標(center)は this.transform.position でも良いが(この例が非常に多い)、マトリクス変換を使えばオブジェクトの回転やスケール、基準位置をずらすことなどにも対応させられるのでメリットが大きい。

 また、ギズモはエディタ上の機能なので、enabled のオン/オフで表示を切り替えられない(Start() や Update() を書くと、インスペクタにチェックボックスが表示される)。なので、visible (可視フラグ)を付けてある。一時的に消したいときに使うと良い。

 他の形状も設定パラメタが変わるだけなので、いくつか書いておこう。



■箱状 Gizmo
using UnityEngine;
using System.Collections;

/** 箱状 Gizmo */
public class MyGizmoCube : MonoBehaviour {

public bool visible = true; //可視状態
public Color color = Color.yellow; //色
public Vector3 center = Vector3.zero; //中心(相対座標)
public Vector3 size = Vector3.one; //x,y,z 方向の長さ

void OnDrawGizmos() {
if (!visible) {
return;
}

Gizmos.color = color;

//Gizmo はワールド座標指定なので、相対座標指定の場合はマトリクス変換で移動する
Gizmos.matrix = Matrix4x4.TRS(this.transform.position, this.transform.rotation,
this.transform.localScale);
Gizmos.DrawCube(center, size);
}
}


箱状ギズモを横長にし、45度回転してみる

設定例



■線状 Gizmo
using UnityEngine;
using System.Collections;

/** 線状 Gizmo */
public class MyGizmoLine : MonoBehaviour {

public bool visible = true; //可視状態
public Color color = Color.yellow; //色
public Vector3 from = Vector3.zero; //始点(相対座標)
public Vector3 to = Vector3.one; //終点(相対座標)

void OnDrawGizmos() {
if (!visible) {
return;
}

Gizmos.color = color;

//Gizmo はワールド座標指定なので、相対座標指定の場合はマトリクス変換で移動する
Gizmos.matrix = Matrix4x4.TRS(this.transform.position, this.transform.rotation,
this.transform.localScale);
Gizmos.DrawLine(from, to);
}
}


線状ギズモを中心を基準に45度回転してみる

設定例




■球/箱/線状のギズモ切り替え

 上記の//状のギズモの作り方がわかったら、形状を切り替えるようにしてみるのも良いだろう。今度はインスペクタで Shape を変更すると形状が変わるようにしてみた。各形状のパラメタは以下のように MySphere, MyCube, MyLine のように専用クラスでまとめるとスッキリする。また今回は、この形状パラメタのクラスは簡略のためコンストラクタを1つしか書いてないが、Vector3 でのコンストラクタも付け加えると便利かもしれない。

●球/箱/線状のギズモ切り替え版
using UnityEngine;
using System.Collections;

/** 独自 Gizmo (球,箱,線 状) */
public class MyGizmo : MonoBehaviour {

/** 形状を表す定数 */
[System.Serializable]
public enum Shape {
Sphere = 0,
Cube,
Line,
}

public bool visible = true; //可視状態
public Color color = Color.yellow; //色
public Shape shape = Shape.Sphere; //形状

public MySphere sphereParam = new MySphere(0, 0, 0, 1); //球状のパラメタ
public MyCube cubeParam = new MyCube(0, 0, 0, 1, 1, 1); //箱状のパラメタ
public MyLine lineParam = new MyLine(0, 0, 0, 1, 1, 1); //線状のパラメタ

void OnDrawGizmos() {
if (!visible) {
return;
}

Gizmos.color = color;

//Gizmo はワールド座標指定なので、相対座標指定の場合はマトリクス変換で移動する
Gizmos.matrix = Matrix4x4.TRS(this.transform.position, this.transform.rotation,
this.transform.localScale);
switch (shape) {
case Shape.Sphere:
Gizmos.DrawSphere(sphereParam.center, sphereParam.radius);
break;

case Shape.Cube:
Gizmos.DrawCube(cubeParam.center, cubeParam.size);
break;

case Shape.Line:
Gizmos.DrawLine(lineParam.from, lineParam.to);
break;
}
}
}

/** 球状パラメタ */
[System.Serializable]
public class MySphere
{
public Vector3 center = Vector3.zero;
public float radius = 1f;

public MySphere(float x, float y, float z, float radius) {
this.center = new Vector3(x, y, z);
this.radius = radius;
}
}

/** 箱状パラメタ */
[System.Serializable]
public class MyCube
{
public Vector3 center = Vector3.zero;
public Vector3 size = Vector3.one;

public MyCube(float x, float y, float z, float scaleX, float scaleY, float scaleZ) {
this.center = new Vector3(x, y, z);
this.size = new Vector3(scaleX, scaleY, scaleZ);
}
}

/** 線状パラメタ */
[System.Serializable]
public class MyLine
{
public Vector3 from = Vector3.zero;
public Vector3 to = Vector3.one;

public MyLine(float x1, float y1, float z1, float x2, float y2, float z2) {
this.from = new Vector3(x1, y1, z1);
this.to = new Vector3(x2, y2, z2);
}
}


Shpae で形状を変更できる

 各形状クラスに [System.Serializable] を書いておくとインスペクタで階層的に表示され設定できるようになる。ただ、この方法だと、実際に選択した形状について対応するパラメタは1つなので、少しインスペクタの表示に無駄があり紛らわしいのが難点だ(球状を選択して、箱状のパラメタに値を設定しても意味がない等)。その場合は「インスペクタの設定項目を動的に変更する」方法で表示を切り替えることもできるので、そちらの記事も参照して欲しい。

 他にも「WireSphere」「WireCube」「Mesh」「WireMesh」なども描けるので、付け加えるとかなり便利になる。やってみると良いだろう。




■ギズモのアイコンを表示する

 もう1つ、カメラや太陽(ライト)のようなアイコンを表示することも覚えておくと良いかもしれない。やり方は簡単で「Gizmos」フォルダを作成し、そこにアイコン画像をインポートして「Gizmos.DrawIcon()」を使用すれば良いだけだ。試しに上記の「MyGizmoCube」に付けてみよう。

●箱状のギズモにアイコン表示を加えたもの
using UnityEngine;
using System.Collections;

/** 箱状 Gizmo */
public class MyGizmoCube : MonoBehaviour {

public bool visible = true; //可視状態
public Color color = Color.yellow; //色
public Vector3 center = Vector3.zero; //中心(相対座標)
public Vector3 size = Vector3.one; //x,y,z 方向の長さ

public string iconImage = ""; //※使用するアイコン画像は Gizmos という名前のフォルダを作成し、そこに格納しておく。

void OnDrawGizmos() {
if (!visible) {
return;
}

if (iconImage != "") {
Gizmos.DrawIcon(transform.position, iconImage, true);
}

Gizmos.color = color;

//Gizmo はワールド座標指定なので、相対座標指定の場合はマトリクス変換で移動する
Gizmos.matrix = Matrix4x4.TRS(this.transform.position, this.transform.rotation,
this.transform.localScale);
Gizmos.DrawCube(center, size);
}
}


Gizmos フォルダに画像を置く

アイコンを表示してみる

名前を指定する

 なお、アイコンには回転などは効かないようだ(ただの存在位置表現のため)。大きさなどはカメラ等デフォルトのアイコンの表示スライダーと共有となる。


(関連記事)
【Unity】【C#】インスペクタの表示項目を動的に変更する
【Unity】【C#】FPS(フレームレート)をリアルタイムに測定して表示するv2(4隅選択可能で、画面サイズの変更にも対応版)
 Unityチュートリアルの2Dシューティングにレーザーやらミサイルやらボス戦やらを付け加えてゲームとして楽しめるように超改造してみた


<素材提供>
[グラフィック]
ハムコロ様
ゲーム開発日記 DVDM様
Merry Party様
ぴぽや様

※著作権は各著作者に帰属します。


関連記事

category: Unity

thread: ゲーム開発

janre: コンピュータ

tag: Unityリファレンス  エディタ拡張 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop