1makqq
Last Updated: February 12, 2017
·
339
· ProGM
Avatar progm

Add a draggable point to scene for your Vector3 serialized fields

I was always wondering if it's possible to show a draggable point in the scene to position

For instance, assuming I have a script that should spawn a certain prefab in the scene with 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 was to make easier to move the SpawnPosition point inside the Scene. My first approach was to use the OnGizmosSelected method, and show a colored sphere in the scene editor, to indicate the point position. However it was not so comfortable to edit.

Inspecting this great plugin, I discovered the great Unity Editor's Handles API

So I tried to define a custom editor script to show an Handle on generic points, so I can reuse it in many script easily.

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 an 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 to CustomEditor. This says to unity to instantiate this Editor script to all classes that inherits 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 type DraggableType defined in the current object property.
  • I use serializedObject.ApplyModifiedProperties (); to apply the movement done by the handle. It also creates an history entry, so you can use ctrl+z to rollback the movement

I think that the result is very cool!

Here's an example:
Demo

And here's the complete code example:
https://gist.github.com/ProGM/226204b2a7f99998d84d755ffa1fb39a

I'm using this script for my videogame. Check it out!
Hope you can find it useful!