Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

3D-Gizmo-Plugins

Einführung

3D-Gizmo-Plugins werden vom Editor und von benutzerdefinierten Plugins verwendet, um die Gizmos zu definieren, die mit jeder Art von Node3D-Node verbunden sind.

Dieses Tutorial zeigt die beiden Hauptansätze für die Definition Ihrer eigenen benutzerdefinierten Gizmos. Die erste Option eignet sich gut für einfache Gizmos und schafft weniger Unordnung in Ihrer Plugin-Struktur, und die zweite ermöglicht es Ihnen, einige Daten pro Gizmo zu speichern.

Bemerkung

Dieses Tutorial setzt voraus, dass Sie bereits wissen, wie man generische Plugins erstellt. Im Zweifelsfall lesen Sie bitte die Seite Erstellen von Plugins.

Das EditorNode3DGizmoPlugin

Unabhängig davon, welchen Ansatz wir wählen, müssen wir ein neues EditorNode3DGizmoPlugin erstellen. Damit können wir einen Namen für den neuen Gizmo-Typ festlegen und andere Verhaltensweisen definieren, z. B. ob das Gizmo ausgeblendet werden kann oder nicht.

Dies wäre ein einfaches Setup:

# my_custom_gizmo_plugin.gd
extends EditorNode3DGizmoPlugin


func get_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)

Für einfache Gizmos reicht es aus, EditorNode3DGizmoPlugin zu erben. Wenn Sie einige Daten pro Gizmo speichern wollen oder ein Godot 3.0-Gizmo auf 3.1+ portieren, sollten Sie den zweiten Ansatz wählen.

Ein einfacher Ansatz

Der erste Schritt besteht darin, in unserem benutzerdefinierten Gizmo-Plugin die Methode _has_gizmo() zu überschreiben, so dass sie true zurückgibt, wenn der Node-Parameter unserem Zieltyp entspricht.

# ...


func _has_gizmo(node):
    return node is MyCustomNode3D


# ...

Dann können wir Methoden wie _redraw() oder alle Handle-bezogenen Methoden außer Kraft setzen.

# ...


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), [])


# ...

Beachten Sie, dass wir in der Methode _init ein Material erstellt und es in der Methode _redraw mit get_material() abgerufen haben. Diese Methode ruft je nach Zustand des Gizmo (ausgewählt und/oder bearbeitbar) eine der Materialvarianten ab.

Das fertige Plugin sollte ungefähr so aussehen:

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(), ...).

Beachten Sie, dass wir nur einige Handles in der _redraw Methode hinzugefügt haben, aber wir müssen noch den Rest der Handle-bezogenen Callbacks in EditorNode3DGizmoPlugin implementieren, um richtig funktionierende Handles zu bekommen.

Ein alternativer Ansatz

In einigen Fällen wollen wir unsere eigene Implementierung von EditorNode3DGizmo bereitstellen, vielleicht weil wir einen gewissen Zustand in jedem Gizmo gespeichert haben wollen oder weil wir ein altes Gizmo-Plugin portieren und nicht durch den Neuschreibungsprozess gehen wollen.

In diesen Fällen müssen wir in unserem neuen Gizmo-Plugin nur _create_gizmo() überschreiben, damit es unsere benutzerdefinierte Gizmo-Implementierung für die Node3D-Node zurückgibt, auf die wir abzielen wollen.

# 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

Auf diese Weise kann die gesamte Gizmo-Logik und die Zeichenmethoden in einer neuen Klasse implementiert werden, von der die Klasse EditorNode3DGizmo erweitert wird, etwa so:

# 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(), ...).

Beachten Sie, dass wir gerade einige Handles in der _redraw Methode hinzugefügt haben, aber wir müssen noch den Rest der Handle-bezogenen Callbacks in EditorNode3DGizmo implementieren, um richtig funktionierende Handles zu bekommen.