Work in progress
The content of this page was not yet updated for Godot
4.6
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-гизмо
Введение
Плагины 3D-гизмо используются редактором и пользовательскими плагинами для определения гизмо, прикрепленных к любому типу узла Node3D.
В этом руководстве показаны два основных подхода к созданию собственных гизмо. Первый вариант хорошо подходит для простых гизмо и не загромождает структуру плагина, а второй позволяет хранить некоторые данные для каждого гизмо.
Примечание
В этом руководстве предполагается, что вы уже умеете создавать универсальные плагины. Если у вас возникли сомнения, обратитесь к странице Создание плагинов.
EditorNode3DGizmoPlugin
Независимо от выбранного подхода, нам потребуется создать новый EditorNode3DGizmoPlugin. Это позволит нам задать имя для нового типа гизмо и определить другие параметры, например, можно ли скрыть гизмо.
Вот базовая настройка:
# 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)
For simple gizmos, inheriting EditorNode3DGizmoPlugin is enough. If you want to store some per-gizmo data, you should go with the second approach.
Простой подход
Первым шагом в нашем пользовательском плагине gizmo является переопределение метода _has_gizmo() так, чтобы он возвращал true, когда параметр узла имеет наш целевой тип.
# ...
func _has_gizmo(node):
return node is MyCustomNode3D
# ...
Затем мы можем переопределить такие методы, как _redraw() или все методы, связанные с дескриптором (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), [])
# ...
Обратите внимание, что мы создали материал в методе _init и извлекли его в методе _redraw с помощью метода get_material(). Этот метод извлекает один из вариантов материала в зависимости от состояния гизмо (выбран и/или доступен для редактирования).
Таким образом, финальный плагин будет выглядеть примерно так:
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(), ...).
Обратите внимание, что мы только что добавили несколько дескрипторов в метод _redraw, но нам все еще необходимо реализовать остальные обратные вызовы, связанные с дескрипторами, в EditorNode3DGizmoPlugin, чтобы получить правильно работающие дескрипторы.
Альтернативный подход
В некоторых случаях мы хотим предоставить собственную реализацию EditorNode3DGizmo, возможно, потому, что мы хотим хранить некоторое состояние в каждом гизмо или потому, что мы портируем старый плагин гизмо и не хотим проходить через процесс переписывания.
В этих случаях все, что нам нужно сделать, это в нашем новом плагине gizmo переопределить _create_gizmo(), чтобы он возвращал нашу пользовательскую реализацию gizmo для узлов Node3D, на которые мы хотим ориентироваться.
# 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
Таким образом, всю логику гизмо и методы рисования можно реализовать в новом классе, расширяющем EditorNode3DGizmo, например так:
# 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(), ...).
Обратите внимание, что мы только что добавили несколько дескрипторов в метод _redraw, но нам все еще необходимо реализовать остальные обратные вызовы, связанные с дескрипторами, в EditorNode3DGizmo, чтобы получить правильно работающие дескрипторы.