Spatial 小工具插件

前言

Spatial 小工具插件是由编辑器和自定义插件来定义附加到任何类型的 Spatial 节点的小工具。

本教程将向您展示定义您自己的自定义小工具的两种主要方法。第一种方法对于简单的小工具来说效果很好,并且在你的插件结构中较少的混乱,而第二种方法将让你存储每个小工具的一些数据。

备注

本教程假设您已经知道如何制作通用插件。如有疑问,请参阅 制作插件 页面。

EditorSpatialGizmoPlugin

不管我们选择什么方式,都需要创建一个新的 EditorSpatialGizmoPlugin。我们可以为新建的小工具类型设置名称并定义其他行为,比如是否可以隐藏。

这是一个基本设置:

# 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)

简单的小工具只要继承 EditorSpatialGizmoPlugin 就足够了。如果你想存储每个小工具的一些数据,或者你要把 Godot 3.0 的小工具移植到 3.1+,你应该选择第二种方法。

简单方法

第一步,在我们的自定义小工具插件中,重写 has_gizmo() 方法,当空间参数为目标类型时,它将返回 true

# ...


func has_gizmo(spatial):
    return spatial is MyCustomSpatial


# ...

我们可以重写譬如 redraw() 的方法,或所有与句柄相关的方法。

# ...


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


# ...

请注意,我们在 _init 方法中创建了一个材质,并在 redraw 方法中使用 get_material() 取得它。该方法根据小工具的状态(选择中和/或可编辑)来获取材质的变体。

您最后的场景应该是这样的:

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

注意,我们只是在重绘方法中添加了一些句柄,但是我们仍然需要在 EditorSpatialGizmoPlugin 中实现其他与句柄相关的回调,以获得正确的工作句柄。

替代方法

在某些情况下,我们希望提供自己的 EditorSpatialGizmo 实现,也许是因为我们想要在每个小工具中存储一些状态,或者正在移植一个旧的小工具插件,而不想经历重写过程。

在这些情况下,我们需要做的就是在我们的新小工具插件中覆盖 create_gizmo(),让它返回我们想要实现的目标 Spatial 节点的自定义小工具。

# 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

这样,所有的小工具逻辑和绘图方法都可以在一个新的类中实现,这个类扩展自 EditorSpatialGizmo,像这样:

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

请注意,我们刚刚在重绘方法中添加了一些句柄,但我们仍然需要在 EditorSpatialGizmo 中实现其余的与句柄相关的回调,以获得正确工作的句柄。