关于 addon-development
面向 Godot 4.3+ 的编辑器插件开发技能,涵盖 GDScript 和 C#。内容包括插件结构、编辑器扩展、生命周期管理与测试。
主要功能
- 01插件结构与配置
- 02编辑器脚本和生命周期
- 03自定义节点、检查器与停靠面板
- 04资源编辑器与 3D 操纵器
- 05插件测试与重载指南
适用场景
- 01向编辑器添加自定义节点类型
- 02为特定属性创建检查器控件
- 03构建项目工具停靠面板
- 04添加可交互的 3D 编辑手柄
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill addon-development安装后在 Claude Code 中输入/addon-development确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/dock-panels.mdgizmos-deep-dive.mdinspector-plugins.mdtesting-plugins.md详细介绍
在 Godot 4.3+ 中开发插件
编辑器插件可扩展 Godot 编辑器本身:自定义节点类型、检查器面板、停靠小部件、3D 操纵器和工具栏按钮。所有示例均面向 Godot 4.3+,不使用已弃用的 API。
**相关技能:**自定义 Resource 编辑器可参考 resource-pattern,编辑器面板 UI 可参考 godot-ui,C# 插件开发可参考 csharp-godot。
1. 插件结构
每个插件都位于项目根目录下的 addons/ 中。Godot 会扫描 plugin.cfg 文件以发现插件。
res://
└── addons/
└── my_plugin/
├── plugin.cfg # required — plugin metadata
├── plugin.gd # main EditorPlugin script (named in plugin.cfg)
├── my_inspector.gd # optional — EditorInspectorPlugin
├── my_dock.tscn # optional — dock panel scene
└── icons/
└── my_node.svg # optional — custom node icons
plugin.cfg 是纯 INI 文件。Godot 扫描 addons/ 时会读取它。script 键必须指向相对于插件文件夹的主插件脚本。
启用插件:Project → Project Settings → Plugins → 勾选插件名称旁边的复选框。
2. @tool 注解
@tool 可让 GDScript(或其 C# 等效代码)在编辑器进程和运行时中执行。没有它,脚本只会在游戏运行时执行。
GDScript
@tool
extends Sprite2D
# Engine.is_editor_hint() is true when running inside the editor,
# false during a running game. Use it to guard editor-only logic.
func _process(delta: float) -> void:
if Engine.is_editor_hint():
# This block runs in the editor viewport — safe to call editor APIs.
update_configuration_warnings()
else:
# Normal game logic here.
pass
# _get_configuration_warnings() returns an array of strings shown as
# yellow warning icons on the node in the Scene panel.
func _get_configuration_warnings() -> PackedStringArray:
var warnings := PackedStringArray()
if texture == null:
warnings.append("Texture is not set. Assign a Texture2D in the Inspector.")
return warnings
C#
using Godot;
[Tool]
public partial class MyToolSprite : Sprite2D
{
public override void _Process(double delta)
{
if (Engine.IsEditorHint())
{
// Editor-only logic. EditorInterface and other GodotSharpEditor types
// exist only in the editor build — wrap them in #if TOOLS.
UpdateConfigurationWarnings();
}
else
{
// Normal game logic.
}
}
public override string[] _GetConfigurationWarnings()
{
if (Texture == null)
return new[] { "Texture is not set. Assign a Texture2D in the Inspector." };
return System.Array.Empty<string>();
}
}
TOOLS仅在编辑器构建中定义,因此#if TOOLS会从导出的游戏中移除类:将其用于仅限编辑器的类(EditorPlugin、检查器插件),不要用于也会在游戏中运行的[Tool]节点。除非导出预设的筛选器将其排除,否则导出会像处理其他脚本一样包含@tool脚本。
关键规则:
- 在每个需要访问编辑器的脚本顶部添加
@tool/[Tool]。 - 始终使用
Engine.is_editor_hint()保护仅限运行时的代码,以免场景尚未完全加载、处理过程就已开始时导致编辑器崩溃。 - 每当属性变化可能影响警告状态时,都要调用
update_configuration_warnings()。
3. EditorPlugin 基类
主插件脚本继承自 EditorPlugin。启用插件时,Godot 会调用 _enter_tree();禁用插件或关闭项目时,则会调用 _exit_tree()。在 _enter_tree() 中添加的所有内容,都必须在 _exit_tree() 中移除。
GDScript
# plugin.gd
@tool
extends EditorPlugin
func _enter_tree() -> void:
# Register a custom node type. The editor shows MyNode in the
# "Add Node" dialog under the chosen base class, with a custom icon.
add_custom_type(
"MyNode", # name shown in editor
"Node2D", # base class to extend
preload("res://addons/my_plugin/my_node.gd"),
preload("res://addons/my_plugin/icons/my_node.svg")
)
# Add a menu item under Project > Tools.
add_tool_menu_item("My Plugin Action", _on_tool_menu_item)
func _exit_tree() -> void:
remove_custom_type("MyNode")
remove_tool_menu_item("My Plugin Action")
func _on_tool_menu_item() -> void:
print("My Plugin Action triggered")
C#
// Plugin.cs
#if TOOLS
using Godot;
[Tool]
public partial class MyPlugin : EditorPlugin
{
public override void _EnterTree()
{
AddCustomType(
"MyNode",
"Node2D",
GD.Load<Script>("res://addons/my_plugin/MyNode.cs"),
GD.Load<Texture2D>("res://addons/my_plugin/icons/my_node.svg")
);
AddToolMenuItem("My Plugin Action", new Callable(this, MethodName.OnToolMenuAction));
}
public override void _ExitTree()
{
RemoveCustomType("MyNode");
RemoveToolMenuItem("My Plugin Action");
}
private void OnToolMenuAction()
{
GD.Print("My Plugin Action triggered");
}
}
#endif
add_custom_type 参数:
| 参数 | 说明 |
|---|---|
name | 在“添加节点”对话框中显示的名称 |
base | Godot 基类的字符串名称 |
script | GDScript / C# 脚本资源 |
icon | Texture2D,通常是 16×16 的 SVG |
add_tool_menu_item 会在顶部菜单栏的 Project → Tools 下添加一个条目。传入一个不接收任何参数的 Callable。
未保存状态与脚本编辑器控制(Godot 4.7+)
Godot 4.7 新增了适用于构建/导出工具的文件管理 API,可在执行操作前检查是否有未保存的工作,或刷新由外部工具修改的脚本。
func _run_pre_build_check() -> void:
var unsaved_scenes := EditorInterface.get_unsaved_scenes() # PackedStringArray of scene paths
var script_editor := EditorInterface.get_script_editor()
var unsaved_files := script_editor.get_unsaved_files() # PackedStringArray of script paths
if not unsaved_scenes.is_empty() or not unsaved_files.is_empty():
push_warning("Unsaved work detected — save before building.")
script_editor.save_all_scripts() # saves every open script
script_editor.reload_open_files() # re-read files changed outside the editor
# Closes the tab, discarding unsaved changes; OK or ERR_FILE_NOT_FOUND.
var err := script_editor.close_file("res://addons/my_plugin/generated.gd")
#if TOOLS
private void RunPreBuildCheck()
{
string[] unsavedScenes = EditorInterface.Singleton.GetUnsavedScenes();
var scriptEditor = EditorInterface.Singleton.GetScriptEditor();
string[] unsavedFiles = scriptEditor.GetUnsavedFiles();
if (unsavedScenes.Length > 0 || unsavedFiles.Length > 0)
GD.PushWarning("Unsaved work detected — save before building.");
scriptEditor.SaveAllScripts();
scriptEditor.ReloadOpenFiles();
Error err = scriptEditor.CloseFile("res://addons/my_plugin/Generated.cs");
}
#endif
4. 自定义检查器插件
如果要为特定类型的导出属性创建自定义小部件,请从主 EditorPlugin 注册一个 EditorInspectorPlugin。检查器插件通过重写 _can_handle 来选择处理对象,并通过 _parse_property(或 _parse_begin)注入自定义小部件。搭配 EditorProperty 子类即可实现实际 UI。
完整的 GDScript 和 C# 脚手架(自定义检查器 + EditorProperty + 注册样板代码)请参阅 references/inspector-plugins.md。
**Godot 4.7+:**静态方法
EditorInspector.create_default_inspector(filter_line_edit: LineEdit = null)会返回一个检查器,其配置与编辑器的 Inspector 停靠面板相同,可直接嵌入插件 UI;传入LineEdit可实时筛选属性(参见 references/inspector-plugins.md)。EditorContextMenuPlugin的ContextMenuSlot还新增了CONTEXT_SLOT_INSPECTOR_PROPERTY,因此上下文菜单插件可以扩展检查器属性的右键菜单:_popup_menu()接收[object ID, property name],选项回调则直接接收EditorProperty。
5. 自定义停靠面板
在 EditorPlugin._enter_tree 中调用 add_control_to_dock(slot, control),即可向编辑器添加自定义停靠面板。在 _exit_tree 中释放该控件。适用于项目级工具界面(关卡浏览器、资源摘要、构建仪表板)。
完整的 GDScript 和 C# 停靠面板框架,请参阅 references/dock-panels.md。
6. 自定义资源编辑器
EditorResourcePicker 可通过工具提示和基类筛选器,将属性限定为特定的 Resource 子类。EditorResourcePreviewGenerator 可为 FileSystem 停靠面板和 Inspector 中的资源提供自定义缩略图。
完整的 GDScript 和 C#
EditorResourcePicker与EditorResourcePreviewGenerator框架,请参阅 references/inspector-plugins.md。
7. Gizmo
EditorNode3DGizmoPlugin 可为编辑器中的 3D 节点添加可视化控件——线框形状、可拖动控件、旋转环。实现 _init(材质)、_get_gizmo_name、_has_gizmo、_redraw(绘制线条/控件),以及用于交互式编辑的 _get_handle_value / _set_handle / _commit_handle。
完整的 GDScript 和 C# Gizmo 插件(包含用于控件提交的撤销/重做连接),请参阅 references/gizmos-deep-dive.md。
**Godot 4.7+:**重写
_can_commit_handle_on_click() -> bool(未重写时返回false),即可在最终控件位置与初始位置相同时提交控件操作,也就是在直接单击时提交。
8. 测试插件
在 Project Settings → Plugins 中关闭再开启插件,即可重新加载插件;保存 @tool 脚本会自动热重载,但新增类注册和停靠面板变更需要完整的重载流程。print() / push_warning() / push_error() 的输出会显示在 Output 面板中。C# 插件必须先重新编译——“Unable to load addon script from path … This might be due to a code error in that script”通常意味着程序集构建失败,因此应先检查 MSBuild 面板。
重新加载方法(GDScript + C# PluginReloader)、控制台启动标志和生命周期问题表(孤立的停靠面板、过时的自定义类型、重复注册的 Inspector 插件):references/testing-plugins.md
9. plugin.cfg 格式
plugin.cfg 是一个纯 INI 文件,位于插件文件夹的根目录。[plugin] 部分中的五个字段均为必填项:缺少任何一个字段的 plugin.cfg 都不会显示在 Plugins 列表中。
[plugin]
name="My Plugin"
description="Adds MyNode, a custom inspector, and a dock panel to the editor."
author="Your Name"
version="1.0.0"
script="plugin.gd"
字段参考:
| 键 | 类型 | 说明 |
|---|---|---|
name | String | Project Settings → Plugins 中显示的名称 |
description | String | Plugins 面板中显示的简短说明 |
author | String | 作者姓名或组织名称 |
version | String | 语义化版本字符串(例如 "1.2.0") |
script | String | 主 EditorPlugin 脚本的路径,相对于插件文件夹 |
Godot 4.x 的
plugin.cfg中没有其他标准键。依赖管理由外部处理(例如通过 Asset Library 或手动安装说明)。
10. 检查清单
-
addons/<plugin_name>/plugin.cfg存在,且包含name、description、author、version、script - 主脚本继承
EditorPlugin,并使用@tool(GDScript)或在#if TOOLS内使用[Tool]修饰 - 在
_enter_tree()中注册的所有内容,都在_exit_tree()中取消注册 - 自定义节点类型使用
add_custom_type/remove_custom_type,并配有对应的 SVG 图标 -
@tool脚本使用Engine.is_editor_hint()保护仅限编辑器的代码 - 节点配置错误时,
_get_configuration_warnings()返回非空数组 - 检视器插件实现
_can_handle,以避免处理不适用的类型 -
_parse_property仅对需要自定义编辑器的属性返回true - 停靠面板场景设置了
Custom Minimum Size,确保面板在默认停靠宽度下可用 - 在
_exit_tree()中使用queue_free()释放停靠面板的Control - 通过
EditorInterface.get_resource_previewer()添加和移除EditorResourcePreviewGenerator - Gizmo 插件使用传入的 EditorPlugin 的
get_undo_redo()实现_commit_handle,确保句柄拖动操作可撤销 - 每次结构变更后,通过完整的禁用/启用流程测试插件
- 在所有
_enter_tree设置流程中使用push_error(),而非静默失败 - 将 C# 插件脚本包裹在
#if TOOLS/#endif中