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小工具外掛

前言

3D 小工具外掛程式是由編輯器和自訂外掛程式來定義附加到任何型別的 Node3D 節點的小工具。

本教學將向您展示定義您自己的自訂小工具的兩種主要方法。第一種方法對於簡單的小工具來說效果很好,並且在你的外掛程式結構中較少的混亂,而第二種方法將讓你儲存每個小工具的一些資料。

備註

本教學假設您已經知道如何製作通用外掛程式。如有疑問,請參閱 製作外掛程式 頁面。

EditorNode3DGizmoPlugin

不管我們選擇什麼方式,都需要建立一個新的 EditorSpatialGizmoPlugin。我們可以為新建的小工具型別設定名稱並定義其他行為,比如是否可以隱藏。

這是一個基本設定:

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

簡單的小工具只要繼承 EditorSpatialGizmoPlugin 就足夠了。如果你想儲存每個小工具的一些資料,或者你要把 Godot 3.0 的小工具移植到 3.1+,你應該選擇第二種方法。

簡單方法

The first step is to, in our custom gizmo plugin, override the _has_gizmo() method so that it returns true when the node parameter is of our target type.

# ...


func _has_gizmo(node):
    return node is MyCustomNode3D


# ...

Then we can override methods like _redraw() or all the handle related ones.

# ...


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

注意,我們只是在重繪方法中新增了一些控制碼,但是我們仍然需要在 EditorSpatialGizmoPlugin 中實作其他與控制碼相關的回呼函式,以獲得正確的工作控制碼。

替代方法

在某些情況下,我們希望提供自己的 EditorSpatialGizmo 實作,也許是因為我們想要在每個小工具中儲存一些狀態,或者正在移植一個舊的小工具外掛程式,而不想經歷重寫過程。

In these cases all we need to do is, in our new gizmo plugin, override _create_gizmo(), so it returns our custom gizmo implementation for the Node3D nodes we want to target.

# 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

這樣,所有的小工具邏輯和繪圖方法都可以在一個新的類中實作,這個類擴充自 EditorSpatialGizmo,像這樣:

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

請注意,我們剛剛在重繪方法中新增了一些控制碼,但我們仍然需要在 EditorSpatialGizmo 中實作其餘的與控制碼相關的回呼函式,以獲得正確工作的控制碼。