カスタムエディタで画面にメッシュを表示して、シーンビューのハンドルを使って位置や回転を変更できるようにしてみました。その値をスクリプトに設定して、アイテムをインスタンス化するときに使ってみます。
位置と回転を設定
まず、Cubeオブジェクトにスクリプトをつけて、位置と回転の値をインスペクタで設定できるようにし、スタート時にトランスフォームがその位置と回転になるようにしました。
using UnityEngine;
namespace Test1
{
public class DisplayMesh : MonoBehaviour
{
public Vector3 location;
public Quaternion rotation = Quaternion.identity;
// Start is called before the first frame update
void Start()
{
transform.position = location;
transform.rotation = rotation;
}
// Update is called once per frame
void Update()
{
}
}
}
カスタムエディタを作る
そして、このスクリプトのインスペクタやシーンビューでの表示をカスタマイズするための、カスタムエディタを作ります。
using UnityEngine;
using UnityEditor;
namespace Test1
{
[CustomEditor(typeof(DisplayMesh))]
[CanEditMultipleObjects]
public class DisplayMeshEditor : Editor
{
SerializedProperty location;
SerializedProperty rotation;
Mesh mesh; // 表示するメッシュ
Material material; // マテリアル
Vector3 pos;
Quaternion rot;
// トグルの状態
bool showBtn = false;
void OnEnable()
{
// SerializedPropertyを取得
location = serializedObject.FindProperty("location");
rotation = serializedObject.FindProperty("rotation");
// プロパティの値を変数に入れる
pos = location.vector3Value;
rot = rotation.quaternionValue;
// メッシュを取得
mesh = (target as DisplayMesh).GetComponent<MeshFilter>().sharedMesh;
// マテリアルを取得
material = (target as DisplayMesh).GetComponent<MeshRenderer>().sharedMaterial;
}
public override void OnInspectorGUI()
{
// トグルを表示
showBtn = EditorGUILayout.Toggle("メッシュを表示", showBtn);
serializedObject.Update();
// 位置を表示
EditorGUILayout.PropertyField(location);
// 回転を表示
EditorGUILayout.PropertyField(rotation);
serializedObject.ApplyModifiedProperties();
}
void OnSceneGUI()
{
// トグルがオンのとき
if (showBtn)
{
// ターゲットのスクリプト
var t = (target as DisplayMesh);
EditorGUI.BeginChangeCheck();
// 移動ツールのとき
if (Tools.current == Tool.Move)
{
pos = Handles.PositionHandle(t.location, t.rotation);
}
// 回転ツールのとき
else if (Tools.current == Tool.Rotate)
{
rot = Handles.RotationHandle(t.rotation, t.location);
}
// ハンドルに変更があるとき
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Move point");
// スクリプトの変数にセット
t.location = pos;
t.rotation = rot;
}
// メッシュを描画
Graphics.DrawMesh(mesh, t.location, t.rotation, material, 0);
}
}
}
}
EditorフォルダにC#スクリプトを作り、クラスにCustomEditor属性を付けて、Editorクラスを継承させます。CustomEditor属性で対象の型名を知らせます。
[CustomEditor(typeof(DisplayMesh))]
[CanEditMultipleObjects]
public class DisplayMeshEditor : Editor
オブジェクトがロードされたときに、そのクラスのプロパティ名からSerializedPropertyを取得し、その値を変数に入れておきます。また、メッシュフィルターとメッシュレンダラーコンポーネントからメッシュとマテリアルを取得しておきます。
SerializedProperty location;
SerializedProperty rotation;
Mesh mesh; // 表示するメッシュ
Material material; // マテリアル
Vector3 pos;
Quaternion rot;
// トグルの状態
bool showBtn = false;
void OnEnable()
{
// SerializedPropertyを取得
location = serializedObject.FindProperty("location");
rotation = serializedObject.FindProperty("rotation");
// プロパティの値を変数に入れる
pos = location.vector3Value;
rot = rotation.quaternionValue;
// メッシュを取得
mesh = (target as DisplayMesh).GetComponent<MeshFilter>().sharedMesh;
// マテリアルを取得
material = (target as DisplayMesh).GetComponent<MeshRenderer>().sharedMaterial;
}
インスペクタでは、まずトグルを表示します。このトグルがオンのときはメッシュが表示されるようにしました。そして、位置と回転のプロパティを入力欄で変更できるようにしています。
public override void OnInspectorGUI()
{
// トグルを表示
showBtn = EditorGUILayout.Toggle("メッシュを表示", showBtn);
serializedObject.Update();
// 位置を表示
EditorGUILayout.PropertyField(location);
// 回転を表示
EditorGUILayout.PropertyField(rotation);
serializedObject.ApplyModifiedProperties();
}
これでインスペクタの表示が変わりました。
シーンビューでの制御はOnSceneGUIメソッドに書きます。
void OnSceneGUI()
{
// トグルがオンのとき
if (showBtn)
{
// ターゲットのスクリプト
var t = (target as DisplayMesh);
EditorGUI.BeginChangeCheck();
// 移動ツールのとき
if (Tools.current == Tool.Move)
{
pos = Handles.PositionHandle(t.location, t.rotation);
}
// 回転ツールのとき
else if (Tools.current == Tool.Rotate)
{
rot = Handles.RotationHandle(t.rotation, t.location);
}
// ハンドルに変更があるとき
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Move point");
// スクリプトの変数にセット
t.location = pos;
t.rotation = rot;
}
// メッシュを描画
Graphics.DrawMesh(mesh, t.location, t.rotation, material, 0);
}
}
インスペクタのトグルによってbool型の変数の値を切り替えて、それがオンのときはハンドルとメッシュを表示します。ツールの種類はTools.currentの値で判別できます。
// ターゲットのスクリプト
var t = (target as DisplayMesh);
EditorGUI.BeginChangeCheck();
// 移動ツールのとき
if (Tools.current == Tool.Move)
{
pos = Handles.PositionHandle(t.location, t.rotation);
}
// 回転ツールのとき
else if (Tools.current == Tool.Rotate)
{
rot = Handles.RotationHandle(t.rotation, t.location);
}
まず、ターゲットのCubeに付けたスクリプトを取得して、そのフィールドの値を直接使っています。ハンドルの引数にそれらを渡しています。
変更を知りたいGUI要素をEditorGUI.BeginChangeCheckメソッドとEditorGUI.EndChangeCheckメソッドで囲っています。変更があればEditorGUI.EndChangeCheckメソッドがtrueになります。
// ハンドルに変更があるとき
if (EditorGUI.EndChangeCheck())
{
Undo.RecordObject(target, "Move point");
// スクリプトの変数にセット
t.location = pos;
t.rotation = rot;
}
このときに、Undo.RecordObjectメソッドを使って変更点を記録し、Ctrl + Zで操作をもとに戻せるようにしています。
呼び出された後の変更点を記録するので、ハンドルの位置や回転はいったん他の変数に入れておいて、このメソッド呼び出しの後にスクリプトにセットしています。
そして、その位置と回転の値を使ってメッシュを描画します。このときにメッシュとマテリアルも引数に渡します。
// メッシュを描画
Graphics.DrawMesh(mesh, t.location, t.rotation, material, 0);
ハンドルを使って値を設定
このCubeをシーンに配置しました。
インスペクタのチェックボックスをオンにすると、もう一つ同じメッシュが表示されました。
このメッシュを移動回転させると、インスペクタの値も変わります。