Voltar para o Blog
Quest Log

Como criar um plugin para o editor da Godot 4 (@tool e EditorPlugin)

Editor da Godot 4 com um dock customizado e um botão de ferramenta adicionados por um plugin

Aprenda a criar um plugin godot para o editor: scripts @tool, EditorPlugin com dock e botão, plugin.cfg e cuidados com código que roda no editor.

Se você já fez alguns jogos na Godot e começou a repetir a mesma tarefa manual dez vezes por semana (renomear nodes, gerar colisão, posicionar spawns na mão), está na hora de parar de sofrer e escrever um plugin godot para o editor. A engine foi construída em cima da própria API que você usa, então estender o editor não é um hack: é o caminho oficial, feito em GDScript, com as mesmas classes que você já conhece. Neste tutorial eu mostro quando vale a pena, como montar a estrutura de plugin.cfg, como usar @tool no código de jogo e como criar um EditorPlugin com dock e botão sem transformar o editor num campo minado.

A ideia central é simples: existem dois mecanismos diferentes e é fácil confundir. O @tool faz um script de node rodar dentro do editor. O EditorPlugin estende o editor em si, adicionando painéis e ferramentas globais. Você vai usar um, o outro, ou os dois juntos, dependendo do que quer automatizar.

Quando vale a pena criar um plugin

Nem toda automação precisa virar plugin. Antes de abrir o plugin.cfg, faça a pergunta honesta: isso é recorrente e visual? Se é uma tarefa que você faz uma vez por projeto, um script de EditorScript rodado com Arquivo > Executar Script resolve. Plugin vale quando o ganho é contínuo.

Os casos clássicos onde compensa:

  • Docks customizados. Um painel lateral para gerenciar dados do jogo, disparar builds, ou listar todos os inimigos da fase.
  • Botões de ferramenta na toolbar. Ações rápidas como "gerar navmesh" ou "snap de todos os tiles selecionados".
  • Gizmos e desenho no viewport. Visualizar raio de ataque, área de patrulha ou waypoints direto na cena, enquanto edita.
  • Importadores customizados. Transformar um .csv ou .json externo em Resource nativo da Godot no momento da importação.
  • Inspector plugins. Trocar o widget padrão de uma propriedade por um controle mais útil (um seletor de cor, um mapa, um botão).

Se o seu caso é nenhum desses e você só quer que um node reaja enquanto edita a cena, você provavelmente nem precisa de um plugin completo. Um @tool no script daquele node basta. Comece pelo menor mecanismo que resolve.

@tool: rodando código de jogo dentro do editor

Por padrão, o código de um script só executa quando o jogo roda. O editor apenas lê a estrutura da classe (as variáveis @export, os sinais) mas nunca chama _ready nem _process. A anotação @tool no topo do arquivo muda isso: o script passa a executar também dentro do editor.

Aqui está um node que desenha uma prévia do raio de detecção de um inimigo direto no viewport, atualizando enquanto você arrasta o valor no Inspector:

@tool
class_name DetectionArea
extends Node2D

@export var raio: float = 120.0:
    set(valor):
        raio = valor
        queue_redraw()

@export var cor: Color = Color(1.0, 0.3, 0.3, 0.4):
    set(valor):
        cor = valor
        queue_redraw()

func _draw() -> void:
    draw_circle(Vector2.ZERO, raio, cor)
    draw_arc(Vector2.ZERO, raio, 0.0, TAU, 64, Color.WHITE, 2.0)

Com o @tool presente, mudar raio no Inspector chama o setter, que dispara queue_redraw(), e o círculo se ajusta na cena na hora. Sem @tool, esse _draw só apareceria com o jogo rodando, o que é inútil para posicionar coisas no nível.

O poder do @tool vem com uma armadilha grande, e é a mais comum de todas.

Engine.is_editor_hint(): a trava de segurança obrigatória

Um script @tool roda nos dois mundos: editor e jogo. Isso significa que se o seu _ready instancia um projétil, toca um som, ou acessa um autoload que só existe em runtime, o editor vai tentar fazer isso enquanto você edita, e provavelmente quebrar ou poluir a cena com nodes fantasmas.

A defesa é Engine.is_editor_hint(), que retorna true quando o código está executando dentro do editor. Use como guarda no início de qualquer função que só faz sentido no jogo:

@tool
extends Node2D

@export var vida: int = 100

func _ready() -> void:
    if Engine.is_editor_hint():
        return
    # Daqui pra baixo, só roda no jogo de verdade.
    _conectar_sinais()
    _iniciar_timers()

func _process(delta: float) -> void:
    if Engine.is_editor_hint():
        # No editor, talvez só atualizar a aparência.
        queue_redraw()
        return
    _processar_input(delta)
    _mover(delta)

A regra prática que evita a maioria dos bugs: no editor, um script @tool só deveria atualizar aparência (desenhar, reposicionar helpers, validar exports). Lógica de gameplay fica atrás da guarda. Quando bater dúvida se algo pode rodar no editor, assuma que não pode e proteja.

Próximo nível
Quer aprender isso na prática?

No CursoGame.Dev você sai dos tutoriais soltos e constrói jogos publicáveis, com trilha progressiva, quests práticas e feedback real.

Conhecer a plataforma
+500 alunos4.9/5Garantia 7 dias

plugin.cfg: a estrutura mínima de um EditorPlugin

Quando você precisa de algo global no editor (um dock, um botão na toolbar, um importador), o @tool num node não basta. Aí entra o EditorPlugin. Todo plugin vive numa pasta dentro de res://addons/, e o editor só reconhece a pasta se ela tiver um arquivo plugin.cfg.

A estrutura fica assim:

res://addons/meu_plugin/
├── plugin.cfg
├── plugin.gd
└── dock.tscn

O plugin.cfg é um arquivo de texto simples com uma seção [plugin]:

[plugin]

name="Meu Plugin"
description="Ferramentas internas do projeto."
author="Juan Felipe"
version="1.0.0"
script="plugin.gd"

O campo script aponta para o arquivo que estende EditorPlugin, relativo à pasta do plugin. Depois de criar isso, vá em Projeto > Configurações do Projeto > Plugins e ative o plugin na lista. Se ele não aparecer ali, o problema é quase sempre o plugin.cfg no lugar errado ou com o campo script apontando para um arquivo inexistente.

EditorPlugin com dock e botão

O script principal estende EditorPlugin e trabalha com dois métodos de ciclo de vida: _enter_tree() (chamado quando o plugin é ativado) e _exit_tree() (chamado quando é desativado). A regra de ouro aqui é simétrica: tudo que você adiciona no _enter_tree você tem que remover no _exit_tree. Se esquecer, o editor acumula docks duplicados e vaza memória cada vez que você recarrega o plugin.

Repare que este arquivo não precisa de @tool: um EditorPlugin já roda no contexto do editor por definição.

extends EditorPlugin

var _dock: Control
var _botao: Button

func _enter_tree() -> void:
    # Instancia um dock a partir de uma cena e o encaixa na lateral.
    _dock = preload("res://addons/meu_plugin/dock.tscn").instantiate()
    add_control_to_dock(DOCK_SLOT_LEFT_UR, _dock)

    # Adiciona um botão na toolbar do editor.
    _botao = Button.new()
    _botao.text = "Gerar Spawns"
    _botao.pressed.connect(_ao_clicar_gerar)
    add_control_to_container(CONTAINER_TOOLBAR, _botao)

func _exit_tree() -> void:
    # Desfaz tudo, na ordem inversa, para não deixar lixo no editor.
    if _botao:
        remove_control_from_container(CONTAINER_TOOLBAR, _botao)
        _botao.queue_free()

    if _dock:
        remove_control_from_docks(_dock)
        _dock.queue_free()

func _ao_clicar_gerar() -> void:
    var raiz: Node = get_editor_interface().get_edited_scene_root()
    if raiz == null:
        print("Nenhuma cena aberta.")
        return
    _gerar_spawns(raiz)

func _gerar_spawns(raiz: Node) -> void:
    for i: int in range(5):
        var marcador: Marker2D = Marker2D.new()
        marcador.name = "Spawn%d" % i
        marcador.position = Vector2(i * 64, 0)
        raiz.add_child(marcador)
        # owner faz o node ser salvo junto com a cena.
        marcador.owner = raiz

Dois detalhes fazem esse código funcionar de verdade no dia a dia. Primeiro, get_editor_interface().get_edited_scene_root() te dá acesso à cena que o usuário está editando, que é por onde você lê e escreve o conteúdo. Segundo, o marcador.owner = raiz: sem definir o owner, os nodes que você cria por código somem quando a cena é salva e reaberta, porque a Godot só serializa filhos que pertencem à raiz da cena. Esse é um tropeço clássico de quem começa a mexer com geração de conteúdo pelo editor.

Para uma ferramenta de produção séria, vale ainda registrar as ações no histórico de undo com get_undo_redo(), para que Ctrl+Z desfaça o que o plugin criou. Comece sem isso, valide a ideia, e adicione o undo quando a ferramenta já estiver no fluxo da equipe.

Organizando os dados que o plugin manipula

Plugins de editor quase sempre acabam lendo e escrevendo dados do jogo: listas de itens, tabelas de fase, configurações de inimigo. Em vez de encher o dock de campos soltos, a prática mais limpa é fazer o plugin operar sobre Resources customizados. Assim o mesmo dado que o jogo consome em runtime é o dado que o seu dock edita, sem duplicação. Se você ainda não usa esse padrão, vale entender antes como criar recursos customizados na Godot, porque ele é a base de qualquer ferramenta de dados decente.

Vale também um aviso de linguagem. Todo o ecossistema de plugins da Godot documentado aqui é GDScript, e para trabalho de editor ele costuma ser a escolha mais direta pela integração nativa e pelo ciclo de recarga rápido. Se o seu projeto é em C#, a API de EditorPlugin existe também, mas com algumas fricções a mais. Se você ainda está decidindo, este comparativo entre C# e GDScript na Godot ajuda a pesar os dois lados antes de investir tempo em ferramentas.

Erros comuns e como evitá-los

Alguns tropeços aparecem toda vez que alguém começa a escrever um plugin godot pela primeira vez:

  • Esquecer o @tool. O node não reage no editor e você fica achando que o código está errado, quando ele só nunca rodou.
  • @tool sem guarda. Lógica de jogo executando no editor, criando nodes fantasmas ou travando a engine. Sempre proteja com Engine.is_editor_hint().
  • Não limpar no _exit_tree. Docks e botões duplicados a cada recarga. Remova exatamente o que adicionou.
  • Nodes gerados que somem. Faltou setar owner. Sem ele, a cena não salva o que o plugin criou.
  • plugin.cfg mal posicionado. O plugin nem aparece na lista. Confira o caminho res://addons/nome/plugin.cfg e o campo script.

Comece pequeno: um único botão que faz uma coisa útil e economiza tempo real. Quando esse primeiro plugin sobreviver a uma semana de uso, você vai ter a intuição de quando escalar para docks, gizmos e importadores. E se quiser uma base sólida em GDScript e na arquitetura da engine antes de partir para ferramentas mais ambiciosas, o melhor curso de Godot cobre o caminho do jogo pronto até a extensão do editor sem pular etapas.

Fechando

Estender o editor da Godot é uma das coisas que mais mudam a produtividade de um dev intermediário, e é acessível justamente porque usa o GDScript que você já domina. Guarde o mapa mental: @tool faz um node rodar no editor, EditorPlugin estende o editor com docks e botões, plugin.cfg registra tudo, e Engine.is_editor_hint() é a trava que impede seu código de jogo de fazer estrago enquanto você edita. Com esses quatro pilares você já consegue automatizar a maior parte do trabalho repetitivo do seu projeto.

Perguntas frequentes

Qual a diferença entre um script @tool e um EditorPlugin?

Um script @tool roda seu código dentro do editor, geralmente preso a um node específico da cena. Um EditorPlugin estende o próprio editor, adicionando docks, botões, importadores e outras ferramentas globais, sem depender de nenhum node em cena.

Preciso de @tool no script do EditorPlugin?

Não. O arquivo que estende EditorPlugin já roda no contexto do editor por definição, então o @tool ali é redundante. Você usa @tool nos scripts de node que precisam executar lógica enquanto você edita a cena, como desenhar gizmos ou atualizar geometria.

Por que meu código de jogo executou dentro do editor e quebrou?

Porque um script @tool roda tanto no editor quanto no jogo. Proteja qualquer lógica que só faz sentido em runtime (física, input, timers, save) com um if Engine.is_editor_hint(): return no começo, ou separe o código em funções que só o _ready chama fora do editor.

Onde fica a pasta do plugin e como o editor a reconhece?

O plugin mora em res://addons/nome_do_plugin/ e precisa de um plugin.cfg com a seção [plugin] apontando o script principal. Depois você habilita em Projeto > Configurações do Projeto > Plugins. Sem o plugin.cfg no lugar certo, ele nem aparece na lista.

Dá para publicar meu plugin na AssetLib da Godot?

Sim. Se a pasta res://addons/seu_plugin/ estiver bem organizada e com um plugin.cfg válido, você pode enviar o repositório para a AssetLib. Vale incluir um README, uma licença e uma versão mínima da engine para evitar dor de cabeça de quem for instalar.