Attention: Here be dragons

This is the latest (unstable) version of this documentation, which may document features not available in or compatible with released stable versions of Godot.

制作插件

关于插件

插件是用有用的工具来扩展编辑器的好方法。它可以完全用 GDScript 和标准场景制作,甚至无需重新加载编辑器。与模块不同,你不需要创建 C++ 代码,也不需要重新编译引擎。虽然这使得插件的功能没有那么强大,但你仍然可以用它们做很多事情。请注意,插件与你已经可以制作的任何场景相似,只不过它是用脚本创建的,用于添加编辑器功能。

本教程将指导你创建两个插件,以便你了解它们的工作原理并能够开发自己的插件。第一个是可以添加到项目中任何场景的自定义节点,另一个是添加到编辑器的自定义停靠面板。

创建插件

在开始之前,随意找个地方创建一个新的空项目。这将作为开发和测试插件的基础。

要让编辑器识别新插件,首先需要创建两个文件:一个是用于配置的 plugin.cfg,另一个是具有功能的工具脚本。插件有一个在项目文件夹内部的标准路径,如 addons/plugin_name。Godot 提供了一个对话框来生成这些文件并将它们放在正确的位置。

在主工具栏上,点击 Project (项目) 下拉菜单。然后点击 Project Settings... (项目设置...)。进入 Plugins (插件)选项卡,接着点击位于右上角的 Create New Plugin (创建新插件)按钮。

你会看到出现一个对话框,类似这样:

../../../_images/making_plugins-create_plugin_dialog.webp

每个字段中的占位符文本都描述了它如何影响插件创建的文件和配置文件的值。

要继续此示例,请使用以下值:

Plugin Name: My Custom Node
Subfolder: my_custom_node
Description: A custom node made to extend the Godot Engine.
Author: Your Name Here
Version: 1.0.0
Language: GDScript
Script Name: custom_node.gd

警告

在 C# 中,EditorPlugin 脚本需要编译,这要求构建项目。构建项目后,可以在项目设置插件选项卡中启用该插件。

你最终应该得到这样的目录结构:

../../../_images/making_plugins-my_custom_mode_folder.webp

plugin.cfg 是一个包含插件元数据的 INI 文件。其中的名称和描述有助于他人了解插件的用途。你的姓名能帮助你为自己的工作得到应有的认可。版本号可以让其他人判断自己使用的是否为过时的版本;如果你不确定如何设置版本号,请查看语义化版本。主脚本文件将指示 Godot 你的插件在激活后在编辑器中执行的操作。

脚本文件

创建插件后,对话框将自动为你打开 EditorPlugin 脚本。该脚本有两个你无法更改的要求:它必须是一个 @tool 脚本,否则它将无法在编辑器中正确加载,并且它必须继承自 EditorPlugin

警告

除了 EditorPlugin 脚本之外,插件使用的其他任何 GDScript同样必须是工具脚本。任何编辑器使用的不带 @tool 的 GDScript 都会像空文件一样!

处理资源的初始化和清理非常重要。一个好的做法是使用虚函数 _enter_tree() 来初始化插件,并使用 _exit_tree() 来清理插件。幸运的是,对话框会为你生成这些回调。你的脚本应该看起来像这样:

@tool
extends EditorPlugin


func _enter_tree():
    # Initialization of the plugin goes here.
    pass


func _exit_tree():
    # Clean-up of the plugin goes here.
    pass

这是创建新插件时可以使用的一个良好模板。

自定义节点

有时你希望在许多节点中实现某种行为,例如可以重复使用的自定义场景或控件。实例化在很多情况下都很有用,但有时会很麻烦,特别是在许多项目中使用它时。一个很好的解决方案是创建一个插件来添加一个具有自定义行为的节点。

在本教程中,我们将创建一个按钮,当点击时会打印一条消息。为此,我们需要一个从 Button 扩展的脚本。如果你愿意,它也可以扩展 BaseButton

# Optional, add to execute in the editor.
@tool

# Icons are optional.
# Alternatively, you may use the UID of the icon or the absolute path.
@icon("icon.svg")

# Automatically register the node in the Create New Node dialog
# and make it available for use with other scripts.
class_name MyButton
extends Button


func _enter_tree():
    pressed.connect(clicked)


func clicked():
    print("You clicked me!")

我们基础按钮的制作就到这里完成了。你可以把这个文件保存为 my_button.gd ,放在插件文件夹里。你或许还想准备一个 16×16 的图标,让它在场景树里显示出来。如果你手头没有现成的图标,可以直接从引擎里拿一个默认的,把它保存到你的 addons/my_custom_node 文件夹下,命名为 icon.svg ;或者直接调用默认的 Godot Logo(@icon("res://icon.svg"))。

小技巧

用作自定义节点图标的 SVG 图像应启用编辑器 > 依照编辑器比例缩放编辑器 > 依照编辑器主题转换颜色导入选项。如果图标设计使用的调色板与 Godot 本身的图标相同,则这允许图标遵循编辑器的比例和主题设置。

../../../_images/making_plugins-custom_node_icon.png

完成后,插件应该已经在项目设置的插件列表中可用,请按照检验结果中的说明激活它。

接下来可以尝试添加新节点:

../../../_images/making_plugins-custom_node_create.webp

当你添加节点时,你可以看到它已被附加了你创建的脚本。给这个按钮设置文本,保存并运行场景。当你点击按钮时,你可以在控制台中看到一些文字:

../../../_images/making_plugins-custom_node_console.webp

自定义停靠面板

有时,你需要扩展编辑器并添加始终可用的工具。一种简单的方法是使用插件添加一个新的停靠面板。停靠面板只是基于 Control 的场景,因此它们的创建方式与通常的 GUI 场景类似。

创建自定义停靠面板的方法与自定义节点一样。在 addons/my_custom_dock 文件夹中创建一个新的 plugin.cfg 文件,然后在其中添加以下内容:

[plugin]

name="My Custom Dock"
description="A custom dock made so I can learn how to make plugins."
author="Your Name Here"
version="1.0"
script="custom_dock.gd"

然后在同一文件夹中创建脚本 custom_dock.gd。向其中填写之前见过的模板是一个好的开始。

由于我们要添加新的自定义停靠面板,因此需要创建停靠面板的内容。这只不过是一个标准的 Godot 场景:只需在编辑器中创建一个新场景然后编辑它。

对于编辑器停靠面板,根节点必须Control 或其子类之一。在本教程中,你可以创建一个按钮。别忘了给你的按钮添加一些文本。

../../../_images/making_plugins-my_custom_dock_scene.webp

将此场景保存为 my_dock.tscn。现在,我们需要获取创建的场景,然后将其作为停靠面板添加到编辑器中。为此,你可以使用 EditorPlugin 类中的 add_dock() 函数。

你需要选择一个停靠位置并定义要添加的控件(也就是你刚刚创建的场景)。不要忘了在插件停用时移除停靠面板。脚本可以是这样的:

@tool
extends EditorPlugin


# A class member to hold the dock during the plugin life cycle.
var dock


func _enter_tree():
    # Initialization of the plugin goes here.
    # Load the dock scene and instantiate it.
    var dock_scene = preload("res://addons/my_custom_dock/my_dock.tscn").instantiate()

    # Create the dock and add the loaded scene to it.
    dock = EditorDock.new()
    dock.add_child(dock_scene)

    dock.title = "My Dock"

    # Note that LEFT_UL means the left of the editor, upper-left dock.
    dock.default_slot = DOCK_SLOT_LEFT_UL

    # Allow the dock to be on the left or right of the editor, and to be made floating.
    dock.available_layouts = EditorDock.DOCK_LAYOUT_VERTICAL | EditorDock.DOCK_LAYOUT_FLOATING

    add_dock(dock)


func _exit_tree():
    # Clean-up of the plugin goes here.
    # Remove the dock.
    remove_dock(dock)
    # Erase the control from the memory.
    dock.queue_free()

请注意,虽然停靠面板最初会出现在其指定的位置,但用户可以自由改变其位置,并保存所产生的布局。

检验结果

现在是时候检验你的工作成果了。打开项目设置并点击插件选项卡。你的插件应该是列表中唯一的插件。

../../../_images/making_plugins-project_settings.webp

你可以看到该插件未启用。点击启用复选框以激活该插件。在关闭设置窗口之前,该停靠面板应该已经可见。你现在应该有一个自定义的停靠面板了:

../../../_images/making_plugins-custom_dock.webp

在插件中注册自动加载/单例

编辑器插件可以在启用时自动注册自动加载。这同样也包括了在插件禁用时注销该自动加载。

这使得用户可以更快速地设置插件,因为即使你的编辑器插件需要使用自动加载,他们也不必手动去项目设置里添加自动加载。

在编辑器插件中使用以下代码注册单例:

@tool
extends EditorPlugin

# Replace this value with a PascalCase autoload name, as per the GDScript style guide.
const AUTOLOAD_NAME = "SomeAutoload"


func _enable_plugin():
    # The autoload can be a scene or script file.
    add_autoload_singleton(AUTOLOAD_NAME, "res://addons/my_addon/some_autoload.tscn")


func _disable_plugin():
    remove_autoload_singleton(AUTOLOAD_NAME)

使用子插件

通常一个插件会添加多个功能,例如自定义节点和面板。在这种情况下,为每个功能使用单独的插件脚本可能更简单。此时就可以使用子插件。

首先像普通插件一样创建所有插件和子插件:

../../../_images/sub_plugin_creation.webp

然后将子插件移动到主插件文件夹中:

../../../_images/sub_plugin_moved.webp

Godot 会在插件列表中隐藏子插件,这样用户就无法将其启用或禁用。而主插件脚本就应该像这样来启用和禁用子插件:

@tool
extends EditorPlugin

# The main plugin is located at res://addons/my_plugin/
const PLUGIN_NAME = "my_plugin"

func _enable_plugin():
    EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/node", true)
    EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/panel", true)

func _disable_plugin():
    EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/node", false)
    EditorInterface.set_plugin_enabled(PLUGIN_NAME + "/panel", false)

举一反三

现在你已经学会了如何制作基本插件,你可以通过多种方式扩展编辑器。使用 GDScript 就可以将许多功能添加到编辑器中;这是一种无需深入研究 C++ 模块也能创建专门编辑器的强大方法。

你可以制作自己的插件来帮助自己,或在资产库中分享它们,以便人们从你的工作中受益。