制作插件

关于插件

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

This tutorial will guide you through the creation of two plugins so you can understand how they work and be able to develop your own. The first is a custom node that you can add to any scene in the project, and the other is a custom dock added to the editor.

创建插件

在开始前, 先找个地方创建个空项目. 这个空项目是制作和测试我们插件的基础.

编辑器要识别一个新的插件, 首先需要创建两个文件: 一个是 plugin.cfg 用于配置和具有此功能的工具脚本. 插件在项目文件夹里面有一个标准路径, 比如 addons/plugin_name.Godot提供了一个属性框, 用于生成这些文件并将它们放在需要的位置.

In the main toolbar, click the Project dropdown. Then click Project Settings.... Go to the Plugins tab and then click on the Create New Plugin button in the top-right.

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

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

每个字段中文本属性都描述了它会影响到哪些配置文件的值.

如果要继续使用该例子,请使用下列的值:

GDScriptC#

  1. Plugin Name: My Custom Node
  2. Subfolder: my_custom_node
  3. Description: A custom node made to extend the Godot Engine.
  4. Author: Your Name Here
  5. Version: 1.0.0
  6. Language: GDScript
  7. Script Name: custom_node.gd
  8. Activate now: No
  1. Plugin Name: My Custom Node
  2. Subfolder: MyCustomNode
  3. Description: A custom node made to extend the Godot Engine.
  4. Author: Your Name Here
  5. Version: 1.0.0
  6. Language: C#
  7. Script Name: CustomNode.cs
  8. Activate now: No

警告

始终需要在 C# 中去掉对 立即激活? 选项的勾选,因为和其他 C# 脚本一样,EditorPlugin 脚本是需要编译的,要求构建整个项目。构建项目后,就可以在 项目设置插件 选项卡中启用该插件。

你最终的目录结构应该是这样显示的:

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

plugin.cfg is an INI file with metadata about your plugin. The name and description help people understand what it does. Your name helps you get properly credited for your work. The version number helps others know if they have an outdated version; if you are unsure on how to come up with the version number, check out Semantic Versioning. The main script file will instruct Godot what your plugin does in the editor once it is active.

脚本文件

Upon creation of the plugin, the dialog will automatically open the EditorPlugin script for you. The script has two requirements that you cannot change: it must be a @tool script, or else it will not load properly in the editor, and it must inherit from EditorPlugin.

警告

In addition to the EditorPlugin script, any other GDScript that your plugin uses must also be a tool. Any GDScript without @tool imported into the editor will act like an empty file!

It’s important to deal with initialization and clean-up of resources. A good practice is to use the virtual function _enter_tree() to initialize your plugin and _exit_tree() to clean it up. Thankfully, the dialog generates these callbacks for you. Your script should look something like this:

GDScriptC#

  1. @tool
  2. extends EditorPlugin
  3. func _enter_tree():
  4. # Initialization of the plugin goes here.
  5. pass
  6. func _exit_tree():
  7. # Clean-up of the plugin goes here.
  8. pass
  1. #if TOOLS
  2. using Godot;
  3. [Tool]
  4. public partial class CustomNode : EditorPlugin
  5. {
  6. public override void _EnterTree()
  7. {
  8. // Initialization of the plugin goes here.
  9. }
  10. public override void _ExitTree()
  11. {
  12. // Clean-up of the plugin goes here.
  13. }
  14. }
  15. #endif

这是创建新插件时使用的好模板.

自定义节点

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

警告

通过 EditorPlugin 添加的节点是“CustomType”(自定义类型)节点。虽然它们可以用于任何脚本语言,但功能比 Script 类系统少。如果你正在编写 GDScript 或 NativeScript,建议使用 Script 类代替。

To create a new node type, you can use the function add_custom_type() from the EditorPlugin class. This function can add new types to the editor (nodes or resources). However, before you can create the type, you need a script that will act as the logic for the type. While that script doesn’t have to use the @tool annotation, it can be added so the script runs in the editor.

For this tutorial, we’ll create a button that prints a message when clicked. For that, we’ll need a script that extends from Button. It could also extend BaseButton if you prefer:

GDScriptC#

  1. @tool
  2. extends Button
  3. func _enter_tree():
  4. pressed.connect(clicked)
  5. func clicked():
  6. print("You clicked me!")
  1. using Godot;
  2. [Tool]
  3. public partial class MyButton : Button
  4. {
  5. public override void _EnterTree()
  6. {
  7. Pressed += Clicked;
  8. }
  9. public void Clicked()
  10. {
  11. GD.Print("You clicked me!");
  12. }
  13. }

That’s it for our basic button. You can save this as my_button.gd inside the plugin folder. You’ll also need a 16×16 icon to show in the scene tree. If you don’t have one, you can grab the default one from the engine and save it in your addons/my_custom_node folder as icon.png, or use the default Godot logo (preload(“res://icon.svg”)).

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

现在,我们需要把它作为一个自定义类型添加,以便它显示在新建 Node 的对话框中。为此,将 custom_node.gd 脚本改为以下内容:

GDScriptC#

  1. @tool
  2. extends EditorPlugin
  3. func _enter_tree():
  4. # Initialization of the plugin goes here.
  5. # Add the new type with a name, a parent type, a script and an icon.
  6. add_custom_type("MyButton", "Button", preload("my_button.gd"), preload("icon.png"))
  7. func _exit_tree():
  8. # Clean-up of the plugin goes here.
  9. # Always remember to remove it from the engine when deactivated.
  10. remove_custom_type("MyButton")
  1. #if TOOLS
  2. using Godot;
  3. [Tool]
  4. public partial class CustomNode : EditorPlugin
  5. {
  6. public override void _EnterTree()
  7. {
  8. // Initialization of the plugin goes here.
  9. // Add the new type with a name, a parent type, a script and an icon.
  10. var script = GD.Load<Script>("res://addons/MyCustomNode/MyButton.cs");
  11. var texture = GD.Load<Texture2D>("res://addons/MyCustomNode/Icon.png");
  12. AddCustomType("MyButton", "Button", script, texture);
  13. }
  14. public override void _ExitTree()
  15. {
  16. // Clean-up of the plugin goes here.
  17. // Always remember to remove it from the engine when deactivated.
  18. RemoveCustomType("MyButton");
  19. }
  20. }
  21. #endif

完成后, 插件应该已经在 项目设置 的插件列表中可用, 因此请按照 Checking the results 中的说明激活它.

然后通过添加新节点来尝试:

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

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

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

自定义窗口

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

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

GDScriptC#

  1. [plugin]
  2. name="My Custom Dock"
  3. description="A custom dock made so I can learn how to make plugins."
  4. author="Your Name Here"
  5. version="1.0"
  6. script="custom_dock.gd"
  1. [plugin]
  2. name="My Custom Dock"
  3. description="A custom dock made so I can learn how to make plugins."
  4. author="Your Name Here"
  5. version="1.0"
  6. script="CustomDock.cs"

然后在同一文件夹中创建脚本 custom_dock.gd。填写之前见过的模板以获得良好的开端。

由于我们正在尝试添加新的自定义窗口, 因此我们需要创建窗口的内容. 这只不过是一个标准的Godot场景: 只需在编辑器中创建一个新场景然后编辑它.

对于编辑器停靠站, 根节点 必须是 Control 或其子类之一. 在本教程中, 你可以创建一个按钮. 根节点的名称也将是面板对话框中显示的名称, 因此请务必为其指定一个简短的描述性名称. 另外, 不要忘记在按钮上添加一些文字.

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

把这个场景保存为 my_dock.tscn . 现在, 我们需要抓取我们创建的场景, 然后在编辑器中把它添加为一个栏目. 为此, 你可以依赖 add_control_to_dock() 这个函数, 它来自 EditorPlugin 类.

你需要选择一个停靠位置并定义要添加的控件, 也就是你刚刚创建的场景. 不要忘了在插件停用时 remove the dock . 脚本可以是这样的:

GDScriptC#

  1. @tool
  2. extends EditorPlugin
  3. # A class member to hold the dock during the plugin life cycle.
  4. var dock
  5. func _enter_tree():
  6. # Initialization of the plugin goes here.
  7. # Load the dock scene and instantiate it.
  8. dock = preload("res://addons/my_custom_dock/my_dock.tscn").instantiate()
  9. # Add the loaded scene to the docks.
  10. add_control_to_dock(DOCK_SLOT_LEFT_UL, dock)
  11. # Note that LEFT_UL means the left of the editor, upper-left dock.
  12. func _exit_tree():
  13. # Clean-up of the plugin goes here.
  14. # Remove the dock.
  15. remove_control_from_docks(dock)
  16. # Erase the control from the memory.
  17. dock.free()
  1. #if TOOLS
  2. using Godot;
  3. [Tool]
  4. public partial class CustomDock : EditorPlugin
  5. {
  6. private Control _dock;
  7. public override void _EnterTree()
  8. {
  9. _dock = GD.Load<PackedScene>("res://addons/MyCustomDock/MyDock.tscn").Instantiate<Control>();
  10. AddControlToDock(DockSlot.LeftUl, _dock);
  11. }
  12. public override void _ExitTree()
  13. {
  14. // Clean-up of the plugin goes here.
  15. // Remove the dock.
  16. RemoveControlFromDocks(_dock);
  17. // Erase the control from the memory.
  18. _dock.Free();
  19. }
  20. }
  21. #endif

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

检查结果

It’s now time to check the results of your work. Open the Project Settings and click on the Plugins tab. Your plugin should be the only one on the list.

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

You can see the plugin is not enabled. Click the Enable checkbox to activate the plugin. The dock should become visible before you even close the settings window. You should now have a custom dock:

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

举一反三

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

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

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

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

这样用户就可以更快速地设置插件了,因为你的编辑器插件要求使用自动加载时,他们不必再手动去项目设置里添加自动加载了。

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

GDScriptC#

  1. @tool
  2. extends EditorPlugin
  3. # Replace this value with a PascalCase autoload name, as per the GDScript style guide.
  4. const AUTOLOAD_NAME = "SomeAutoload"
  5. func _enter_tree():
  6. # The autoload can be a scene or script file.
  7. add_autoload_singleton(AUTOLOAD_NAME, "res://addons/my_addon/some_autoload.tscn")
  8. func _exit_tree():
  9. remove_autoload_singleton(AUTOLOAD_NAME)
  1. #if TOOLS
  2. using Godot;
  3. [Tool]
  4. public partial class MyEditorPlugin : EditorPlugin
  5. {
  6. // Replace this value with a PascalCase autoload name.
  7. private const string AutoloadName = "SomeAutoload";
  8. public override void _EnterTree()
  9. {
  10. // The autoload can be a scene or script file.
  11. AddAutoloadSingleton(AutoloadName, "res://addons/MyAddon/SomeAutoload.tscn");
  12. }
  13. public override void _ExitTree()
  14. {
  15. RemoveAutoloadSingleton(AutoloadName);
  16. }
  17. }
  18. #endif

Previous Next


© 版权所有 2014-present Juan Linietsky, Ariel Manzur and the Godot community (CC BY 3.0). Revision b1c660f7.

Built with Sphinx using a theme provided by Read the Docs.