~2 мин чтения

@tool и EditorPlugin — расширение редактора

Tool-скрипты, custom inspectors, EditorPlugin, добавление узлов и docks к редактору Godot.

Godot полностью написан на собственном языке для UI — это значит, что сам редактор может быть расширен GDScript-кодом. Никаких C++-плагинов, никаких пересборок.

Два уровня расширения:

  1. @tool-скрипты — обычные узлы с поведением в редакторе. Например, скрипт, который автоматически расставляет деревья по сплайну.
  2. EditorPlugin — настоящий плагин: добавляет узлы, кастомные docks, инспекторы, gizmos.

@tool — скрипт работает в редакторе

Обычный GDScript исполняется только в Play Mode. Добавьте @tool в первой строке — и скрипт будет работать прямо в Scene View.

@tool
extends Node3D

@export var radius: float = 5.0:
    set(value):
        radius = value
        _rebuild_circle()  # перерисовываем при изменении

@export var count: int = 8:
    set(value):
        count = value
        _rebuild_circle()

@export var rebuild: bool = false:
    set(value):
        if value: _rebuild_circle()
        rebuild = false

func _rebuild_circle() -> void:
    # Чистим старых детей
    for child in get_children():
        child.queue_free()

    # Расставляем сферы по кругу
    for i in count:
        var angle := TAU * i / count
        var sphere := MeshInstance3D.new()
        sphere.mesh = SphereMesh.new()
        sphere.position = Vector3(cos(angle), 0, sin(angle)) * radius
        add_child(sphere)
        sphere.owner = get_tree().edited_scene_root  # чтобы дети сохранились в .tscn

Меняете radius в Inspector — круг сразу перерисовывается в Scene View. Сохраняете сцену — дочерние сферы остаются.

Веб

Storybook + Design Tokens: интерактивные превью компонентов с live-параметрами в редакторе. Tool-скрипты — это “Storybook прямо в финальном продукте”.

Unity
Tool-скрипты — самая частая причина зависшего редактора

Если поставите while true: или тяжёлый цикл в @tool-скрипте без условия выхода — зависнет редактор. Перед сохранением проверяйте: ничего бесконечного, нет блокирующих операций. Если всё сломалось — запускайте Godot из терминала с флагом --no-window для удаления скрипта.

owner = edited_scene_root — обязательно

Если вы создаёте детей в @tool-скрипте, обязательно проставляйте им owner:

var instance := preload("res://prefab.tscn").instantiate()
add_child(instance)
instance.owner = get_tree().edited_scene_root

Без owner — дети существуют в Scene Tree во время редактирования, но не сохраняются в .tscn. Сохранение и открытие сцены сделает их невидимыми.

EditorPlugin — настоящий плагин

Полноценный плагин — это EditorPlugin-узел, который Godot загружает при старте редактора. Может:

  • Добавить новый тип узла (add_custom_type).
  • Добавить dock в боковую панель (add_control_to_dock).
  • Добавить пункт в меню Project/Scene (add_tool_menu_item).
  • Зарегистрировать gizmo для узла (add_node_3d_gizmo_plugin).
  • Перехватить save/load сцены.

Структура плагина

addons/
└── my_plugin/
    ├── plugin.cfg           ← манифест
    ├── plugin.gd            ← главный EditorPlugin
    ├── my_node.gd           ← новый тип узла
    └── icon.svg

plugin.cfg:

[plugin]

name="MyAwesomePlugin"
description="Adds X to the editor"
author="You"
version="1.0"
script="plugin.gd"

plugin.gd:

@tool
extends EditorPlugin

func _enter_tree() -> void:
    # Регистрируем новый тип узла "TerrainPainter"
    add_custom_type(
        "TerrainPainter",
        "Node3D",
        preload("res://addons/my_plugin/my_node.gd"),
        preload("res://addons/my_plugin/icon.svg")
    )

func _exit_tree() -> void:
    remove_custom_type("TerrainPainter")

После сохранения: Project → Project Settings → Plugins → активируйте свой. В меню Add Node появится “TerrainPainter”.

Dock-панель

@tool
extends EditorPlugin

var dock: Control

func _enter_tree() -> void:
    dock = preload("res://addons/my_plugin/dock.tscn").instantiate()
    add_control_to_dock(EditorPlugin.DOCK_SLOT_LEFT_UR, dock)

func _exit_tree() -> void:
    remove_control_from_docks(dock)
    dock.queue_free()

dock.tscn — обычная сцена с Control-узлом. Появится в редакторе как настоящая dock-панель, перетаскиваемая.

Inspector plugin — custom property edits

Для нестандартных property-эдиторов (например, color-palette picker вместо обычного Color):

extends EditorInspectorPlugin

func _can_handle(object: Object) -> bool:
    return object is MyCustomResource

func _parse_property(object, type, name, hint, hint_string, usage, wide):
    if name == "palette_color":
        add_property_editor(name, MyCustomPaletteEditor.new())
        return true
    return false

Gizmos — 3D-визуализация в редакторе

Хотите видеть в 3D viewport круг радиуса агрессии вашего врага? Зарегистрируйте Node3DGizmoPlugin:

class_name EnemyGizmo extends EditorNode3DGizmoPlugin

func _has_gizmo(node):
    return node is Enemy  # ваш custom class

func _redraw(gizmo):
    var node = gizmo.get_node_3d() as Enemy
    gizmo.clear()
    # Рисуем sphere wireframe радиуса agro_radius
    var lines := PackedVector3Array()
    var segments := 32
    for i in segments:
        var a1 := TAU * i / segments
        var a2 := TAU * (i + 1) / segments
        lines.append(Vector3(cos(a1), 0, sin(a1)) * node.agro_radius)
        lines.append(Vector3(cos(a2), 0, sin(a2)) * node.agro_radius)
    gizmo.add_lines(lines, get_material("main", gizmo))

В редакторе вокруг каждого Enemy будет нарисован круг агро — мгновенная визуальная подсказка.

Когда писать плагин vs использовать @tool

  • @tool — для одной сцены: процедурная расстановка, генератор, превью.
  • EditorPlugin — для переиспользуемой фичи: кастомные узлы, нестандартные инспекторы, dock’и.

Большинство Asset Library-плагинов (Phantom Camera, Dialogic, Terrain3D) — это EditorPlugin. Они активируются через Project Settings → Plugins.

Сравнение с Unity

Unity-аналог:

  • [ExecuteAlways] атрибут MonoBehaviour ↔ Godot @tool.
  • OnDrawGizmos() / OnDrawGizmosSelected() ↔ Godot EditorNode3DGizmoPlugin.
  • Custom Editor ([CustomEditor(typeof(...))]) ↔ Godot EditorInspectorPlugin.
  • EditorWindow ↔ Godot dock с Control.
  • MenuItem ↔ Godot add_tool_menu_item.

Концепции эквивалентны, имена разные.