Show a draggable point into the scene linked to a Vector3 field using the Handle API in Unity Editor
I’m always searching for new ways to improve the editing within the editor in Unity. One common issue in my projects is: making it easier to define the coordinates of a Serialized field that represents a point in the scene (i.e. a serialized Vector3)
For instance, assuming I have a script that spawns a certain prefab in the scene in a certain position:
using UnityEngine;
using System.Collections;public class ExampleBehavior : MonoBehaviour {
public Vector3 SpawnPosition;
public GameObject SpawnableObject; public void Spawn() {
Instantiate(SpawnableObject, SpawnPosition, Quaternion.identity);
}
}
My goal is to make easier to move the SpawnPosition
point inside the Scene.
My first approach was to use the OnGizmosSelected
method, showing a colored sphere in the scene editor, to indicate the point position, then change manually the Vector3 values until I achieve the correct position. However, it wasn’t very comfortable to edit.
Inspecting this great plugin sources, I discovered the Unity Editor’s Handles API, a set of methods to spawn interactable objects within the scene, like points, resizable shapes and more.
So I tried to define a custom editor script to show a Handle
on generic points, so I can reuse it in many scripts easily.
Here’s how I did it:
First of all, I defined a new PropertyAttribute
, so I can use it to enable this behavior on the editor:
public class DraggablePoint : PropertyAttribute {}
Then I defined a custom editor script, for all MonoBehaviors
, that iterates every property in search of a DraggablePoint
, showing a handle for it:
[CustomEditor(typeof(MonoBehaviour), true)]
public class DraggablePointDrawer : Editor { readonly GUIStyle style = new GUIStyle(); void OnEnable(){
style.fontStyle = FontStyle.Bold;
style.normal.textColor = Color.white;
} public void OnSceneGUI () {
var property = serializedObject.GetIterator ();
while (property.Next (true)) {
if (property.propertyType == SerializedPropertyType.Vector3) {
var field = serializedObject.targetObject.GetType ().GetField (property.name);
if (field == null) {
continue;
}
var draggablePoints = field.GetCustomAttributes (typeof(DraggablePoint), false);
if (draggablePoints.Length > 0) {
Handles.Label(property.vector3Value, property.name);
property.vector3Value = Handles.PositionHandle (property.vector3Value, Quaternion.identity);
serializedObject.ApplyModifiedProperties ();
}
}
}
}
}
Note that:
- I’m passing
true
as a second parameter toCustomEditor
. This says to unity to instantiate this Editor script to all classes that inherit from MonoBehavior (every unity script component). - I’m using
serializedObject.GetIterator ()
to iterate on every property of the script. - I use Reflection to check for any
PropertyAttribute
of typeDraggableType
defined in the current object property. - I use
serializedObject.ApplyModifiedProperties ();
to apply the movement done by the handle. It also creates a history entry, so you can usectrl+z
to rollback the movement
I think that the result is very cool!
Here’s an example:
And here’s the complete code example:
https://gist.github.com/ProGM/226204b2a7f99998d84d755ffa1fb39a
I’m using this script for my videogames. Check it out!
Hope you can find it useful!