Skip to content

Instantly share code, notes, and snippets.

@ProGM
Last active April 25, 2023 07:45
Show Gist options
  • Save ProGM/226204b2a7f99998d84d755ffa1fb39a to your computer and use it in GitHub Desktop.
Save ProGM/226204b2a7f99998d84d755ffa1fb39a to your computer and use it in GitHub Desktop.
A Unity Editor extension to generate draggable points for vector3 serialized attributes.
using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public class DraggablePoint : PropertyAttribute {}
#if UNITY_EDITOR
[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 ();
}
}
}
}
}
#endif
using UnityEngine;
using System.Collections;
public class ExampleBehavior : MonoBehaviour {
[DraggablePoint] public Vector3 SpawnPosition;
public GameObject SpawnableObject;
public void Spawn() {
Instantiate(SpawnableObject, SpawnPosition, Quaternion.identity);
}
}
@iuridosanjos
Copy link

How can you do the same but for Arrays or Lists of Vector3? Thank you.

@GaanDjin
Copy link

Here's a quick modification I made up to handle arrays. It gets a bit busy with lots of elements but it does what I need.
Thanks for the original @ProGM !

It's only lightly tested (aka one object with a few points in an array)

using System;
using UnityEngine;
using System.Reflection;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class DraggablePoint : PropertyAttribute { }


#if UNITY_EDITOR
[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()
    {
        SerializedProperty property = serializedObject.GetIterator();
        while (property.Next(true))
        {
            if (property.propertyType == SerializedPropertyType.Vector3)
            {
                handleVectorProperty(property);
            }
            else if (property.isArray)
            {
                for (int x = 0; x < property.arraySize; x++)
                {
                    SerializedProperty element = property.GetArrayElementAtIndex(x);
                    if (element.propertyType != SerializedPropertyType.Vector3)
                    {
                        //Break early if we're not an array of Vector3
                        break;
                    }
                    handleVectorPropertyInArray(element, property, x);
                }
            }
        }
    }

    void handleVectorProperty(SerializedProperty property)
    {
        FieldInfo field = serializedObject.targetObject.GetType().GetField(property.name);
        if (field == null)
        {
            return;
        }
        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();
        }
    }

    void handleVectorPropertyInArray(SerializedProperty property, SerializedProperty parent, int index)
    {
        FieldInfo parentfield = serializedObject.targetObject.GetType().GetField(parent.name);
        if (parentfield == null) //Check parent is not null
        {
            return;
        }
        var draggablePoints = parentfield.GetCustomAttributes(typeof(DraggablePoint), false);
        if (draggablePoints.Length > 0)
        {
            Handles.Label(property.vector3Value, parent.name + "[" + index + "]"); //Print the Array field name and its index
            property.vector3Value = Handles.PositionHandle(property.vector3Value, Quaternion.identity);
            serializedObject.ApplyModifiedProperties();
        }
    }
}
#endif

@GaanDjin
Copy link

How can you do the same but for Arrays or Lists of Vector3? Thank you.

Sorry accidentally didn't comment to you directly but have a look at my comment:
https://gist.github.com/ProGM/226204b2a7f99998d84d755ffa1fb39a#gistcomment-3266712

@GaanDjin
Copy link

Soooo back again.... Made another version that puts handles relative to the parent. So if you move the game object around the handles will move relative to the parent rather than being in world space.


using System;
using UnityEngine;
using System.Reflection;
#if UNITY_EDITOR
using UnityEditor;
#endif

///Handles relative to game object
public class DraggablePointRelative : PropertyAttribute { }


#if UNITY_EDITOR
[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()
    {
        SerializedProperty property = serializedObject.GetIterator();
        while (property.Next(true))
        {
            if (property.propertyType == SerializedPropertyType.Vector3)
            {
                handleVectorProperty(property);
            }
            else if (property.isArray)
            {
                for (int x = 0; x < property.arraySize; x++)
                {
                    SerializedProperty element = property.GetArrayElementAtIndex(x);
                    if (element.propertyType != SerializedPropertyType.Vector3)
                    {
                        //Break early if we're not an array of Vector3
                        break;
                    }
                    handleVectorPropertyInArray(element, property, x);
                }
            }
        }
    }

    void handleVectorProperty(SerializedProperty property)
    {
        FieldInfo field = serializedObject.targetObject.GetType().GetField(property.name);
        if (field == null)
        {
            return;
        }
        var draggablePoints = field.GetCustomAttributes(typeof(DraggablePoint), false);
        if (draggablePoints.Length > 0)
        {
            Handles.Label(property.vector3Value + ((MonoBehaviour)target).transform.position, property.name);
            property.vector3Value = Handles.PositionHandle(property.vector3Value + ((MonoBehaviour)target).transform.position, Quaternion.identity) - ((MonoBehaviour)target).transform.position;
            serializedObject.ApplyModifiedProperties();
        }
    }

    void handleVectorPropertyInArray(SerializedProperty property, SerializedProperty parent, int index)
    {
        FieldInfo parentfield = serializedObject.targetObject.GetType().GetField(parent.name);
        if (parentfield == null)
        {
            return;
        }
        var draggablePoints = parentfield.GetCustomAttributes(typeof(DraggablePoint), false);
        if (draggablePoints.Length > 0)
        {
            Handles.Label(property.vector3Value + ((MonoBehaviour)target).transform.position, parent.name + "[" + index + "]");
            property.vector3Value = Handles.PositionHandle(property.vector3Value + ((MonoBehaviour)target).transform.position, Quaternion.identity) - ((MonoBehaviour)target).transform.position;
            serializedObject.ApplyModifiedProperties();
        }
    }
}
#endif

@DrRek
Copy link

DrRek commented Jun 13, 2020

public class DraggablePoint : PropertyAttribute {} should not be defined in a script inside the Editor folder but somewhere inside a Script or Assets folder. Otherwise you'll be forced to place all the script using this inside the Editor folder

@CellCyphon
Copy link

I realise I'm replying a year and a half later, but I'm trying to adapt this to be able to edit Vector3 arrays in scriptable objects. I'm using SOs to generate spawn points for a mobile game and being able to edit them quickly in the scene view would be useful, rather than doing so in the inspector.

I'm assuming I need to use the OnOpenAsset to access assets as opposed to game objects but I keep hitting brick walls. Anybody managed to do this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment