Work in progress
The content of this page was not yet updated for Godot
4.4
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
Regardless of the approach we choose, we will need to create a new EditorNode3DGizmoPlugin. This will allow us to set a name for the new gizmo type and define other behaviors such as whether the gizmo can be hidden or not.
这是一个基本设置:
# 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)
简单的小工具只要继承 EditorNode3DGizmoPlugin 就足够了。如果你想存储各个小工具的一些数据,或者你要把 Godot 3.0 的小工具移植到 3.1+,你应该选择第二种方法。
简单方法
第一步,在我们的自定义小工具插件中,覆盖 _has_gizmo() 方法,以便当节点参数是目标类型时返回 true
。
# ...
func _has_gizmo(node):
return node is MyCustomNode3D
# ...
我们可以覆盖譬如 _redraw() 之类的方法,或所有与句柄相关的方法。
# ...
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 实现,可能是因为希望在各个小工具中存储一些状态,或者因为正在移植旧的小工具插件,而不想经历重写过程。
在这些情况下,我们需要做的就是在新小工具插件中覆盖 _create_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 中实现其余与句柄相关的回调,以获得正常工作的句柄。