Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

Plugins de gizmo 3D

Introdução

Plugins de gizmo 3D são usados pelo editor e por plugins personalizados para definir os gizmos anexados a qualquer tipo de nó Node3D.

Este tutorial mostra as duas abordagens principais para definir seus próprios gizmos personalizados. A primeira opção funciona bem para gizmos simples e cria menos desordem na estrutura do seu plugin, e a segunda permitirá que você armazene alguns dados por gizmo.

Nota

Este tutorial pressupõe que você já sabe como fazer plugins genéricos. Em caso de dúvida, consulte a página Criando plugins.

O EditorNode3DGizmoPlugin

Independentemente da abordagem que escolhermos, precisaremos criar um novo EditorNode3DGizmoPlugin. Isso nos permitirá definir um nome para o novo tipo de gizmo e definir outros comportamentos, como se o gizmo pode ser ocultado ou não.

Esta seria uma configuração básica:

# my_custom_gizmo_plugin.gd
extends EditorNode3DGizmoPlugin


func _get_gizmo_name():
    return "CustomNode"
# MyCustomEditorPlugin.gd
@tool
extends EditorPlugin


const MyCustomGizmoPlugin = preload("res://addons/my-addon/my_custom_gizmo_plugin.gd")

var gizmo_plugin = MyCustomGizmoPlugin.new()


func _enter_tree():
    add_node_3d_gizmo_plugin(gizmo_plugin)


func _exit_tree():
    remove_node_3d_gizmo_plugin(gizmo_plugin)

Para gizmos simples, herdar de EditorNode3DGizmoPlugin é suficiente. Se você quiser armazenar alguns dados por gizmo, deve seguir a segunda abordagem.

Abordagem simples

O primeiro passo é, em nosso plugin de gizmo personalizado, sobrescrever o método _has_gizmo() para que ele retorne true quando o parâmetro do nó for do nosso tipo alvo.

# ...


func _has_gizmo(node):
    return node is MyCustomNode3D


# ...

Então, podemos sobrescrever métodos como _redraw() ou todos os relacionados a manipulação (handle).

# ...


func _init():
    create_material("main", Color(1, 0, 0))
    create_handle_material("handles")


func _redraw(gizmo):
    gizmo.clear()

    var node3d = gizmo.get_node_3d()

    var lines = PackedVector3Array()

    lines.push_back(Vector3(0, 1, 0))
    lines.push_back(Vector3(0, node3d.my_custom_value, 0))

    var handles = PackedVector3Array()

    handles.push_back(Vector3(0, 1, 0))
    handles.push_back(Vector3(0, node3d.my_custom_value, 0))

    gizmo.add_lines(lines, get_material("main", gizmo), false)
    gizmo.add_handles(handles, get_material("handles", gizmo), [])


# ...

Note that we created a material in the _init method, and retrieved it in the _redraw method using get_material(). This method retrieves one of the material's variants depending on the state of the gizmo (selected and/or editable).

Portanto, o plug-in final seria mais ou menos assim:

extends EditorNode3DGizmoPlugin


const MyCustomNode3D = preload("res://addons/my-addon/my_custom_node_3d.gd")


func _init():
    create_material("main", Color(1,0,0))
    create_handle_material("handles")


func _has_gizmo(node):
    return node is MyCustomNode3D


func _redraw(gizmo):
    gizmo.clear()

    var node3d = gizmo.get_node_3d()

    var lines = PackedVector3Array()

    lines.push_back(Vector3(0, 1, 0))
    lines.push_back(Vector3(0, node3d.my_custom_value, 0))

    var handles = PackedVector3Array()

    handles.push_back(Vector3(0, 1, 0))
    handles.push_back(Vector3(0, node3d.my_custom_value, 0))

    gizmo.add_lines(lines, get_material("main", gizmo), false)
    gizmo.add_handles(handles, get_material("handles", gizmo), [])


# You should implement the rest of handle-related callbacks
# (_get_handle_name(), _get_handle_value(), _commit_handle(), ...).

Note that we just added some handles in the _redraw method, but we still need to implement the rest of handle-related callbacks in EditorNode3DGizmoPlugin to get properly working handles.

Abordagem alternativa

Em alguns casos, queremos fornecer nossa própria implementação de EditorNode3DGizmo, talvez porque queremos armazenar algum estado em cada gizmo(aparelho/bugiganga/engenhoca) ou porque estamos portando um plugin antigo de gizmo e não queremos passar pelo processo de reescrita.

Nesses casos, tudo o que precisamos fazer é, em nosso novo plugin de gizmo, sobrescrever o método _create_gizmo(), de modo que ele retorne nossa implementação de gizmo personalizada para os nós Node3D que queremos visar.

# my_custom_gizmo_plugin.gd
extends EditorNode3DGizmoPlugin


const MyCustomNode3D = preload("res://addons/my-addon/my_custom_node_3d.gd")
const MyCustomGizmo = preload("res://addons/my-addon/my_custom_gizmo.gd")


func _init():
    create_material("main", Color(1, 0, 0))
    create_handle_material("handles")


func _create_gizmo(node):
    if node is MyCustomNode3D:
        return MyCustomGizmo.new()
    else:
        return null

Dessa forma, toda a lógica do gizmo e os métodos de desenho podem ser implementados em uma nova classe que estende EditorNode3Gizmo, assim:

# my_custom_gizmo.gd
extends EditorNode3DGizmo


# You can store data in the gizmo itself (more useful when working with handles).
var gizmo_size = 3.0


func _redraw():
    clear()

    var node3d = get_node_3d()

    var lines = PackedVector3Array()

    lines.push_back(Vector3(0, 1, 0))
    lines.push_back(Vector3(gizmo_size, node3d.my_custom_value, 0))

    var handles = PackedVector3Array()

    handles.push_back(Vector3(0, 1, 0))
    handles.push_back(Vector3(gizmo_size, node3d.my_custom_value, 0))

    var material = get_plugin().get_material("main", self)
    add_lines(lines, material, false)

    var handles_material = get_plugin().get_material("handles", self)
    add_handles(handles, handles_material, [])


# You should implement the rest of handle-related callbacks
# (_get_handle_name(), _get_handle_value(), _commit_handle(), ...).

Note that we just added some handles in the _redraw method, but we still need to implement the rest of handle-related callbacks in EditorNode3DGizmo to get properly working handles.