Gadget spatial plugins

Introduction

Les plugins de gizmo spatial sont utilisés par l'éditeur et les plugins personnalisés pour définir les gizmos attachés à tout type de nœud spatial.

Ce tutoriel vous montrera les deux principales approches pour définir vos propres gizmos personnalisés. La première option fonctionne bien pour les gizmos simples et crée moins d'encombrement dans la structure de votre plugin, tandis que la seconde vous permettra de stocker des données par gizmo.

Note

Ce tutoriel suppose que vous savez déjà comment créer des plugins génériques. En cas de doute, reportez-vous à la page  Création de plugins.

L'EditorSpatialGizmoPlugin

Quelle que soit l'approche que nous choisissons, nous devrons créer un nouveau EditorSpatialGizmoPlugin. Cela nous permettra de définir un nom pour le nouveau type de gizmo et de définir d'autres comportements, par exemple si le gizmo peut être masqué ou non.

Ce sera une configuration de base :

# MyCustomGizmoPlugin.gd
extends EditorSpatialGizmoPlugin


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


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

var gizmo_plugin = MyCustomGizmoPlugin.new()


func _enter_tree():
    add_spatial_gizmo_plugin(gizmo_plugin)


func _exit_tree():
    remove_spatial_gizmo_plugin(gizmo_plugin)

Pour les gizmos simples, hériter simplement de EditorSpatialGizmoPlugin suffit. Si vous souhaitez stocker des données par gizmo ou si vous portez un gizmo Godot 3.0 vers 3.1+, vous devriez opter pour la deuxième approche.

Approche simple

La première étape consiste, dans notre plugin de gizmo personnalisé, à remplacer la méthode has_gizmo() afin qu'elle renvoie true lorsque le paramètre spatial est de notre type cible.

# ...


func has_gizmo(spatial):
    return spatial is MyCustomSpatial


# ...

Ensuite, nous pouvons remplacer des méthodes comme redraw() ou toutes les poignées associées.

# ...


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


func redraw(gizmo):
    gizmo.clear()

    var spatial = gizmo.get_spatial_node()

    var lines = PoolVector3Array()

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

    var handles = PoolVector3Array()

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

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


# ...

Notez que nous avons créé un matériau dans la méthode _init, et l'avons récupéré dans la méthode redraw en utilisant get_material(). Cette méthode récupère une des variantes du matériau en fonction de l'état du gizmo (sélectionné et/ou modifiable).

Le plugin final ressemblerait donc un peu à ceci :

extends EditorSpatialGizmoPlugin


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


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


func has_gizmo(spatial):
    return spatial is MyCustomSpatial


func redraw(gizmo):
    gizmo.clear()

    var spatial = gizmo.get_spatial_node()

    var lines = PoolVector3Array()

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

    var handles = PoolVector3Array()

    handles.push_back(Vector3(0, 1, 0))
    handles.push_back(Vector3(0, spatial.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()...).

Notez que nous venons d'ajouter quelques poignées dans la méthode de rafraîchissement, mais nous devons toujours implémenter le reste des rappels liés aux poignées dans EditorSpatialGizmoPlugin pour obtenir des poignées fonctionnant correctement.

Approche alternative

Dans certains cas, nous voulons fournir notre propre implémentation de EditorSpatialGizmo, peut-être parce que nous voulons avoir un état stocké dans chaque gizmo ou parce que nous portons un ancien plugin de gizmo et nous ne voulons le réécrire.

Dans ces cas, tout ce que nous devons faire est, dans notre nouveau plugin gizmo, remplacer create_gizmo(), afin qu'il renvoie notre implémentation de gizmo personnalisée pour les nœuds spatiaux que nous voulons cibler.

# MyCustomGizmoPlugin.gd
extends EditorSpatialGizmoPlugin


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


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


func create_gizmo(spatial):
    if spatial is MyCustomSpatial:
        return MyCustomGizmo.new()
    else:
        return null

De cette façon, toutes les méthodes de logique et de dessin du gizmo peuvent être implémentées dans une nouvelle classe étendant EditorSpatialGizmo, comme ça :

# MyCustomGizmo.gd
extends EditorSpatialGizmo


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


func redraw():
    clear()

    var spatial = get_spatial_node()

    var lines = PoolVector3Array()

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

    var handles = PoolVector3Array()

    handles.push_back(Vector3(0, 1, 0))
    handles.push_back(Vector3(gizmo_size, spatial.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()...).

Notez que nous venons d'ajouter quelques poignées dans la méthode redraw, mais nous devons toujours implémenter le reste des rappels liés aux poignées dans EditorSpatialGizmo pour obtenir des poignées fonctionnant correctement.