【Unity】Gizmoの表示状態をスクリプトから制御する
シーン内のGameObjectの数が増えてくると、シーンに表示されているGizmoの数がとんでもないことになって、何がなんやらわからなくなってくることがあります。
それらをコンポーネントごとに表示、非表示を切り替えるには、GUI上でのみ設定可能でした。
(リフレクションを使えば、スクリプトからでも制御可能です。)
Unity2022になって、ついに公式でスクリプトから制御する方法が追加されました。
新しい方法を紹介するとともに、他の制御方法も併せて記載します。
GUIで制御する
Sceneビューの右上のアイコンから、Gizmoの表示状態をコンポーネントごとに制御するメニューを開くことができます。
スクリプトで制御する(リフレクション)
リフレクションを使えば、スクリプトからでも制御する方法が存在はします。
が、リフレクションは呼び出してるメソッド名などが変わったら使えなくなったりパフォーマンスの観点から、可能であればやりたくない方法です。
stackoverflow.com
スクリプトで制御する(公式)
Unity2022あたりから、GizmoUtility、GizmoInfoというクラスが登場し、Gizmoの表示状態を制御するAPIが正式に実装されました。
GizmoUtility - Unity スクリプトリファレンス
GizmoInfo - Unity スクリプトリファレンス
以下は、すべてのGizmoの表示非表示と、LightとCameraのみを非表示にするように変更するサンプルです。
youtu.be
これらは以下のスクリプトでできています。
gist.github.com
【Unity】EditorWindowを定義しているスクリプトをすぐに探せるようにする
はじめに
ゲームを開発していると、EditorWindowを継承して、そのゲーム専用のエディタウィンドウを作成することがよくあるかと思います。
開発が進むと、いろんな人がエディタウィンドウを新しく定義して、エディタウィンドウが増えていくこともあるかと思います。
そんな時、ふと「このEditorWindowの実装どうなってるんだろう」と思うことがあると思います。
が、EditorWindowを定義しているスクリプトファイルがどれになるのか、スムーズに探せないことがあります。
EditorWindowのタイトルが別に定義されていたりするとさらにわからなくなります。GUIの表示から処理を推測してスクリプトファイルを探す..なんてことを僕はしょっちゅうやっています。
解決策
あまり効率的ではないので、これを解決する方法を紹介します。
動画のようにメニューからスクリプトの場所を探すことができるようになります。

EditorWindowにIHasCustomMenuを実装する
IHasCustomMenu を実装すると、EditorWindow右上のボタンで出る一覧に自分のメニューを増やすことができます。
using UnityEngine; using UnityEditor; public class ExampleEditorWindow : EditorWindow, IHasCustomMenu { [MenuItem("Tools/EditorWIndow")] public static void Open() { EditorWindow.GetWindow<ExampleEditorWindow>("ExampleEditorWindow"); } /// <summary> /// メニューにアイテムをを追加する /// </summary> /// <param name="menu">メニュー</param> public void AddItemsToMenu(GenericMenu menu) { menu.AddItem(new GUIContent("Ping Script"), false, () => { var mono = MonoScript.FromScriptableObject(this); EditorGUIUtility.PingObject(mono); }); } }
GenericMenu.AddItem() を使うことで、簡単にメニューを追加することができます。
【Unity】GameObjectを配置せずに、オブジェクトを描画する【RenderMesh】
突然ですが、以下の画像を見てください。

シーンに赤い立方体が10個存在しているなんてことないシーンですが、ヒエラルキーには GameObject が3つしかありません。
どういうことでしょうか??
Graphics.RenderMesh
Unity で何かキャラクターなどの3Dモデルを描画しようと思うと、MeshRenderer や SkinnedMeshRenderer のついた GameObject をシーンに配置しないといけないと思っている方が多いかもしれません。
実は、GameObject を配置しなくてもシーン上に3Dモデルを描画することができます。
実は、 RenderMesh オブジェクトについている、RenderMesh コンポーネントが描画処理をしています。
public class RenderMesh : MonoBehaviour { [SerializeField] Mesh _mesh; [SerializeField] Material _material; [SerializeField, Range(0, 5)] float _interval = 3f; RenderParams _renderParams; void Start() { _renderParams = new RenderParams(_material); } void Update() { var matrix = new Matrix4x4( new Vector4(1, 0, 0, 0), new Vector4(0, 1, 0, 0), new Vector4(0, 0, 1, 0), new Vector4(0, 0, 0, 1)); var row0 = matrix.GetRow(0); for (int i = 0; i < 10; i++) { matrix.SetRow(0, row0); // 描画 Graphics.RenderMesh(_renderParams, _mesh, 0, matrix); // X座標を等間隔にずらして配置 var c = row0; c.w += _interval; row0 = c; } } }
Graphics.RenderMesh に、描画のパラメータ (RenderParams)とメッシュ、あとは座標を渡すことで、簡単にオブジェクトを描画することができます。
RenderParams
描画時に使うありとあらゆるデータを設定することができます。唯一必要なのはマテリアルくらいかなと思います。ほかにもどのカメラに映すのかなどの設定ができるみたいです。
詳細はリファレンス見たほうが早いかと思います。
docs.unity3d.com
メリット
スクリプトからものを描画できるメリットは、主に以下かと思います。
- GameObject を Instantiate する必要がないので、パフォーマンス的に早いし、メモリにも優しい
- GameObject をシーンに配置するわけではないので、シーンに差分が出ない
- 配置を数学的に制御できる
デメリット
GameObjectを使わないことによるデメリットは結構あります。
- シーン上で配置をいじることができないし、そもそも選択することができない
- スキンメッシュアニメーションさせることはできない
- コンポーネントをつけることができないので、例えば当たり判定などは自前で用意する必要がある
【Unity】Gizmoの表示非表示をスクリプトから操作する【Annotation】
Gizmo の表示非表示は、シーンビュー右上のアイコンからそれぞれ表示したいものにチェックを入れることで制御可能です。
いつからから検索もできるようもなり、さらに便利になりました。

が、これを1つずつ手作業でやるのは大変です。
スクリプトから制御できればいくらか作業も楽になるかもしれません。
ということで、今回はスクリプトからこの表示非表示を制御する方法です。
※ Unityのバージョンは 2021.3.3f1 を使用しています。
今回はリフレクションで Annotation や AnnotationUtility を持ってきて実装しています。
GizmoUtility
2022.1 からは GizmoUtility というのが実装されて、リフレクションを使わなくても同じことができるようになっているようです。
2022.1 以上を使う人はこちらの手法を使ったほうがより安全で効率も良いかと思います。
docs.unity3d.com
unity.com
【Unity】矩形の空間を定義、シーン上でいじれる処理を書いてみる【BoxBoundsHandle】
ゲームを作っていると、ゲーム空間の中にエリアを定義したくなることがあると思います。
今回はそれをシーン上で調整できるようにする BoxBoundsHandle を紹介します。
とりあえずシーンに矩形を描画してみる
単純にシーン上で範囲を表すために図形を描画したい場合、GizmosクラスのDraw〇〇というメソッドを使います。今回は矩形を描画したいので、DrawWireCube()を使いました。
public class BoxArea : MonoBehaviour { [SerializeField] private Bounds bounds; private void OnDrawGizmosSelected() { Gizmos.DrawWireCube(bounds.center, bounds.size); } }

これで範囲をシーン上でグラフィカルにみることができますが、矩形の調整が数値でしかできないです。
ピッタリ数値を合わすときなどは数値でできたほうが便利なことがありますが、シーン上で矩形の大きさを見ながら直接調節できたほうがやりやすい場合もあるかと思います。
そこで、シーン上から直接矩形の範囲を調節することができる方法を紹介します。
BoxBoundsHandle を使う
シーン上で矩形の大きさを調整できるBoxBoundsHandleというものがあります。
OnDrawGizmos() 上では使えないので、インスペクタをエディタ拡張する必要があります。
[CustomEditor(typeof(BoxArea))] public class BoxAreaInspector : Editor { BoxBoundsHandle handle; Bounds cacheBounds; private void OnEnable() { handle = new BoxBoundsHandle(); } private void OnSceneGUI() { serializedObject.Update(); var bounds = serializedObject.FindProperty("bounds"); handle.center = bounds.boundsValue.center; handle.size = bounds.boundsValue.size; using (var check = new EditorGUI.ChangeCheckScope()) { // 描画 handle.DrawHandle(); // 変更を適用する if (check.changed) { cacheBounds.center = handle.center; cacheBounds.size = handle.size; bounds.boundsValue = cacheBounds; } } serializedObject.ApplyModifiedProperties(); } }

これでシーン上からグラフィカルに矩形を調整できるようになりました。
が、実はまだ問題があります。
回転ができない
BoxBoundsHandleには回転の値がありません。
Handles.DrawingScope()を使う必要があります。
ここに行列を渡すことで、回転させることができます。
[CustomEditor(typeof(BoxArea))] public class BoxAreaInspector : Editor { BoxBoundsHandle handle; Bounds cacheBounds; private void OnEnable() { handle = new BoxBoundsHandle(); } private void OnSceneGUI() { var self = target as BoxArea; serializedObject.Update(); var bounds = serializedObject.FindProperty("bounds"); // GameObjectの空間で回転、拡縮、移動させる var matrix = Handles.matrix * self.transform.localToWorldMatrix; handle.center = bounds.boundsValue.center; handle.size = bounds.boundsValue.size; using (var check = new EditorGUI.ChangeCheckScope()) { // 描画する using (new Handles.DrawingScope(matrix)) { handle.DrawHandle(); } // 変更を適用する if (check.changed) { cacheBounds.center = handle.center; cacheBounds.size = handle.size; bounds.boundsValue = cacheBounds; } } serializedObject.ApplyModifiedProperties(); } }

最後に
BoxBoundsHandle以外にも、球を同じように制御できるSphereBoundsHandleなどもあるので、いろいろ探してみてください。
docs.unity3d.com
また、PrimitiveBoundsHandleを継承することで、自分だけの Handle を作ることもできそうです。
docs.unity3d.com
【Unity】インスペクタ拡張で元のスクリプトを表示する
何もインスペクタ拡張をしていない場合、コンポーネントにはそのスクリプトが ObjectField で表示されます。
これをクリックするとスクリプトの場所を教えてくれるため、意外と便利です。
public class Example : MonoBehaviour { [SerializeField] int example; }

が、インスペクタ拡張で特に何もしないでいると、この表示は消えてしまいます。

スクリプトは m_Script で宣言されているので、 SerializedProperty を取得すれば実現できます。
[CustomEditor(typeof(Example))] public class ExampleInspector : Editor { public override void OnInspectorGUI() { // Scriptを編集できない状態で表示する using (new EditorGUI.DisabledScope(true)) { EditorGUILayout.PropertyField(serializedObject.FindProperty("m_Script")); } EditorGUILayout.PropertyField(serializedObject.FindProperty("example")); } }
この処理は Unity の公式の GitHub と同じ処理をしています。
github.com
