mcpField
A
addon-development

addon-development

github.com/jame581开发889165 次安装4 天前更新
代码仓库

关于 addon-development

面向 Godot 4.3+ 的编辑器插件开发技能,涵盖 GDScript 和 C#。内容包括插件结构、编辑器扩展、生命周期管理与测试。

主要功能

  1. 01插件结构与配置
  2. 02编辑器脚本和生命周期
  3. 03自定义节点、检查器与停靠面板
  4. 04资源编辑器与 3D 操纵器
  5. 05插件测试与重载指南

适用场景

  1. 01向编辑器添加自定义节点类型
  2. 02为特定属性创建检查器控件
  3. 03构建项目工具停靠面板
  4. 04添加可交互的 3D 编辑手柄

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jame581/godotprompter --skill addon-development

安装后在 Claude Code 中输入/addon-development确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
dock-panels.md
gizmos-deep-dive.md
inspector-plugins.md
testing-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在“添加节点”对话框中显示的名称
baseGodot 基类的字符串名称
scriptGDScript / C# 脚本资源
iconTexture2D,通常是 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"

字段参考:

键类型说明
nameStringProject Settings → Plugins 中显示的名称
descriptionStringPlugins 面板中显示的简短说明
authorString作者姓名或组织名称
versionString语义化版本字符串(例如 "1.2.0")
scriptString主 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 中

评论

支持 Markdown