[Editor do Unity] Mostra um ponto arrastável na cena vinculado a um campo Vector2 / Vector3

Sempre me perguntei se é possível mostrar um ponto arrastável na cena para posicionar

Por exemplo, supondo que eu tenha um script que deve gerar um certo pré-fabricado na cena com uma determinada posição:

using UnityEngine;
using System.Collections;

public class ExampleBehavior : MonoBehaviour {
public Vector3 SpawnPosition;
public GameObject SpawnableObject;

public void Spawn() {
Instantiate(SpawnableObject, SpawnPosition, Quaternion.identity);
}
}

Meu objetivo era tornar mais fácil mover o SpawnPositionponto dentro da cena. Minha primeira abordagem foi usar o OnGizmosSelectedmétodo e mostrar uma esfera colorida no editor de cena para indicar a posição do ponto. No entanto, não foi tão confortável editar.

Inspecionando este ótimo plugin , descobri a excelente API Handles do Editor Unity

Portanto, tentei definir um script de editor personalizado para mostrar um Handleponto genérico, para poder reutilizá-lo em muitos scripts facilmente.

Em primeiro lugar, defini um novo PropertyAttribute, para que possa usá-lo para habilitar esse comportamento no editor:

public class DraggablePoint : PropertyAttribute {}

Em seguida, defini um script de editor personalizado, para todos MonoBehaviors, que itera cada propriedade em busca de um DraggablePoint, mostrando um identificador para ele:

[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 ();
}
}
}
}
}

Observe que:

  • Estou passando truecomo segundo parâmetro para CustomEditor. Isso diz à unidade para instanciar este script do Editor para todas as classes que herdam de MonoBehavior (cada componente do script de unidade).
  • Estou usando para iterar em cada propriedade do script.serializedObject.GetIterator ()
  • Eu uso o Reflection para verificar se há algum PropertyAttributetipo DraggableTypedefinido na propriedade do objeto atual.
  • Eu uso serializedObject.ApplyModifiedProperties ();para aplicar o movimento feito pela alça. Ele também cria uma entrada de histórico, para que você possa usar ctrl+zpara reverter o movimento

Eu acho que o resultado é muito legal!

Aqui está um exemplo:
Demo

E aqui está o exemplo de código completo:
https://gist.github.com/ProGM/226204b2a7f99998d84d755ffa1fb39a

Estou usando este script para meu videogame. Confira!
Espero que você possa achar isso útil!