Nodes do Godot: Entenda a Árvore de Cena de Uma Vez

Entenda os nodes do Godot de uma vez: como a árvore de cena funciona, o que Node, Node2D e Control fazem e quando usar composição no lugar de herança.
Nodes do Godot Explicados: A Árvore de Cena na Prática
Quem chega no Godot vindo de Unity ou de programação "normal" costuma travar no mesmo lugar: os nodes do Godot parecem simples demais pra serem o sistema inteiro. E são o sistema inteiro mesmo. Não existe GameObject com componentes pendurados, não existe entidade separada da lógica. Existe node, node dentro de node, e a árvore que isso forma. Quando esse modelo encaixa na sua cabeça, o editor inteiro passa a fazer sentido. Antes disso, você fica copiando estrutura de tutorial sem saber por quê.
Esse artigo é o mapa que eu queria ter lido no começo: o que um node é de verdade, como a árvore de cena funciona em runtime, quais classes base importam, e a decisão de design que separa projeto organizado de projeto espaguete, composição versus herança.
O que é um node, sem rodeio
Um node é a menor unidade de funcionalidade do Godot. Cada um faz uma coisa: Sprite2D desenha uma imagem, Camera2D enquadra a tela, Timer conta tempo, AudioStreamPlayer toca som. Sozinho, quase nenhum node é um objeto de jogo completo. Um personagem útil é uma pilha deles:
Player (CharacterBody2D)
├── Sprite2D
├── CollisionShape2D
├── AnimationPlayer
└── Camera2D
Todo node tem quatro propriedades garantidas: um nome (único entre os irmãos), um pai (no máximo um), filhos (quantos quiser) e, opcionalmente, um script. O script não substitui o node: ele estende. Quando você escreve extends CharacterBody2D no topo de um GDScript, você está criando uma classe que herda tudo do CharacterBody2D e acrescenta o seu comportamento por cima. É assim que um inimigo ganha inteligência artificial na Godot: a mesma árvore de nodes mais um script que decide o que ele faz.
Essa é a primeira sacada importante: no Godot, o node é o componente e o objeto ao mesmo tempo. O "GameObject" da Unity equivale, mais ou menos, ao node raiz de uma cena, e os componentes equivalem aos filhos. Se você ainda está decidindo entre as duas, o Godot vs Unity compara esses dois modelos de cena a fundo. A diferença é que no Godot os filhos também são objetos completos, com posição, script e filhos próprios.
A árvore de cena dos nodes do Godot em runtime
Quando o jogo roda, todos os nodes vivem numa estrutura única chamada scene tree, acessível por get_tree(). A raiz absoluta é um node especial chamado root (um Viewport), e a sua cena principal é filha dele. Tudo que aparece, toca, colide ou processa no seu jogo está nessa árvore. O que não está na árvore não executa: um node criado em memória mas nunca adicionado com add_child() não recebe _process, não desenha, não existe pro jogador.
A árvore define três coisas que você usa o tempo todo:
Ordem de processamento. A árvore é percorrida de cima pra baixo. Em _process e _physics_process, o pai roda antes dos filhos. Já no _ready é o contrário: os filhos ficam prontos antes do pai. Faz sentido quando você pensa: o pai só pode se declarar pronto quando tudo que ele contém já está montado. É por isso que dentro do _ready do pai é seguro acessar os filhos, mas um filho não deve assumir nada sobre o pai nesse momento.
Transformações em cascata. Em nodes com posição (Node2D, Node3D, Control), a posição é relativa ao pai. Mova o pai e todos os filhos vão junto. É assim que a câmera segue o player sem uma linha de código: ela é filha dele.
Ciclo de vida. Entrar na árvore dispara _enter_tree e depois _ready (esse só na primeira vez). Sair dispara _exit_tree. Pra destruir um node, use queue_free(), que agenda a remoção pro fim do frame, em vez de free(), que apaga na hora e pode quebrar quem ainda referencia o objeto naquele frame.
Acessando nodes pela árvore
O jeito mais comum de pegar uma referência é o atalho $, que é açúcar pra get_node():
extends CharacterBody2D
# @onready adia a atribuição pra quando o node entra na árvore.
# Sem isso, a linha rodaria na construção do script, antes do filho existir.
@onready var sprite: Sprite2D = $Sprite2D
@onready var anim: AnimationPlayer = $AnimationPlayer
func _ready() -> void:
sprite.flip_h = true
anim.play("idle")
O caminho funciona como diretório: $Sprite2D pega o filho direto, $UI/HealthBar desce dois níveis, get_parent() sobe um. E aqui vale uma regra de ouro da comunidade Godot que eu assino embaixo: chame pra baixo, sinalize pra cima. O pai pode chamar métodos dos filhos diretamente, porque ele os criou e sabe que existem. O filho não deve chamar o pai, porque isso o amarra a um contexto específico. Quando o filho precisa avisar algo, ele emite um sinal e quem estiver acima decide o que fazer:
# hurtbox.gd, filho de qualquer coisa que toma dano
extends Area2D
signal dano_recebido(quantidade: int)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("projeteis"):
dano_recebido.emit(10)
body.queue_free()
O pai conecta e reage do jeito dele:
func _ready() -> void:
$Hurtbox.dano_recebido.connect(_on_dano_recebido)
func _on_dano_recebido(quantidade: int) -> void:
vida -= quantidade
A Hurtbox não sabe se está num player, num inimigo ou num barril explosivo. E é exatamente isso que a torna reutilizável nos três.
As quatro classes base que você precisa conhecer
São centenas de tipos de node, mas a hierarquia se resume a quatro raízes. Errar a base é o erro estrutural mais comum de iniciante.
Node é a base de tudo e não tem posição no espaço. Use pra lógica pura: gerenciador de estado, componente de vida, spawner, máquina de estados. Se o seu node não precisa estar "em algum lugar" da tela, ele deve ser um Node simples.
Node2D adiciona posição, rotação e escala em 2D. Todo o mundo 2D herda daqui: Sprite2D, CharacterBody2D, Area2D, TileMapLayer.
Node3D é o equivalente pro 3D, com transformação completa no espaço tridimensional. MeshInstance3D, Camera3D e os corpos físicos 3D descendem dele.
Control é a base da interface. Botão, label, painel, barra de vida. Control não usa o sistema de posição do Node2D: ele tem âncoras e margens, feitas pra UI que se adapta a qualquer resolução. Misturar Control com Node2D na mesma subárvore esperando que se comportem igual é receita de layout quebrado.
Na dúvida, pergunte: precisa de posição? Se não, Node. É 2D, 3D ou interface? Pronto, a base está escolhida.
Cenas são nodes empacotados
Aqui o modelo fecha o círculo: uma cena no Godot é só uma árvore de nodes salva num arquivo .tscn. O Player que montamos lá em cima vira player.tscn, e a partir daí você o instancia onde quiser, quantas vezes quiser:
const INIMIGO := preload("res://inimigo.tscn")
func spawnar_inimigo(pos: Vector2) -> void:
var inimigo := INIMIGO.instantiate()
inimigo.global_position = pos
add_child(inimigo)
Cada instância é uma cópia independente da árvore inteira: sprite, colisão, script, tudo. Mudou a cena original, todas as instâncias herdam a mudança. Na prática, cenas são os seus prefabs, suas classes visuais, seus blocos de montar. Um nível é uma cena que instancia cenas de plataforma, que instanciam cenas de inimigo, que instanciam cenas de projétil.
A pergunta "isso devia ser uma cena separada?" tem uma resposta honesta: se você vai usar em mais de um lugar, ou se quer testar isolado apertando F6, separa. Se é parte inseparável de uma coisa só, deixa junto.
Composição vs herança: a decisão que organiza (ou destrói) seu projeto
GDScript tem herança, e extends é útil. Mas o instinto de quem vem de orientação a objetos clássica é modelar o jogo numa árvore de classes: Personagem, que vira Inimigo, que vira InimigoVoador, que vira InimigoVoadorQueAtira. Isso funciona até a terceira semana. Depois aparece o inimigo terrestre que atira, e ele precisa do código de tiro que está preso no ramo dos voadores. Aí começa o copia e cola, ou a gambiarra de subir o método pra base e encher a classe mãe de código que metade dos filhos não usa.
O Godot foi desenhado pra outra abordagem: composição. Em vez de perguntar "o que esse objeto É", pergunte "o que esse objeto TEM". Comportamentos viram nodes filhos, e você monta objetos encaixando peças:
InimigoVoador (CharacterBody2D)
├── Sprite2D
├── CollisionShape2D
├── Vida (Node)
├── MovimentoVoador (Node)
└── Atirador (Node2D)
Torreta (StaticBody2D)
├── Sprite2D
├── CollisionShape2D
├── Vida (Node)
└── Atirador (Node2D)
O mesmo componente Atirador serve nos dois, sem herança nenhuma entre inimigo e torreta. Um componente de vida reutilizável é meia dúzia de linhas:
# vida.gd: filho de qualquer coisa que pode morrer.
extends Node
signal morreu
signal mudou(atual: int, maximo: int)
@export var maximo := 100
var atual: int
func _ready() -> void:
atual = maximo
func receber_dano(quantidade: int) -> void:
atual = maxi(atual - quantidade, 0)
mudou.emit(atual, maximo)
if atual == 0:
morreu.emit()
Quem usa, conecta o sinal e segue a vida: o player toca animação de morte, a torreta explode, o barril solta loot. O componente não conhece nenhum deles.
Isso não significa que herança é proibida. Ela continua sendo a ferramenta certa pra variações rasas de uma mesma coisa: um inimigo_base.gd com vida e dano, estendido por dois ou três tipos concretos, é limpo e direto. O problema é profundidade. Minha régua prática: herança até dois níveis pra compartilhar o que é idêntico, composição pra tudo que é comportamento encaixável. Se você está criando uma classe intermediária só pra dois filhos dividirem um método, pare e transforme aquele método num node componente.
Um catálogo rápido dos nodes que você mais vai encontrar
São centenas de tipos, mas no dia a dia você reencontra o mesmo punhado o tempo todo. Vale ter um mapa mental agrupado por função, porque escolher o node certo pro trabalho poupa gambiarra depois. Aqui estão os que aparecem em quase todo projeto 2D, organizados pelo que fazem.
Mostrar coisa na tela. Sprite2D desenha uma imagem estática ou um quadro de spritesheet. AnimatedSprite2D toca uma sequência de quadros direto, bom pra animação simples sem timeline. AnimationPlayer é o irmão poderoso: anima qualquer propriedade de qualquer node ao longo do tempo (posição, cor, chamada de função), e é ele que você usa pra animação séria. TileMapLayer monta cenário a partir de um tileset, uma camada por vez.
Mover e colidir. CharacterBody2D é o corpo que você controla por código, com move_and_slide(), usado em player e inimigo. RigidBody2D é o corpo que a física controla, pra coisa que tomba e quica. StaticBody2D é parede e chão que não se mexem. Area2D não colide de verdade, só detecta entrada e saída: é o node de hitbox, zona de dano, item coletável, gatilho. Decorar a diferença entre Area2D e os corpos resolve metade dos bugs de colisão de iniciante.
Reagir e temporizar. Timer conta um intervalo e emite timeout, perfeito pra cooldown, spawn e delay. AudioStreamPlayer (e a versão 2D, com posição no espaço) toca som. RayCast2D lança uma linha invisível que pergunta "tem algo nesse caminho?", base de linha de visão de inimigo e detecção de chão.
Organizar a câmera e a cena. Camera2D enquadra e segue, geralmente como filha do player. Node puro, sem posição, é o seu contêiner de lógica: gerenciador de fase, máquina de estados, autoload. Marker2D é só um ponto no espaço com nome, útil pra marcar "ponto de spawn" ou "boca do canhão" sem desenhar nada.
Montar interface. Tudo que é HUD e menu vive sob Control. Label mostra texto, Button recebe clique, ProgressBar vira barra de vida, TextureRect exibe imagem na UI, e os contêineres (VBoxContainer, HBoxContainer, MarginContainer) arrumam os filhos sozinhos pra o layout se adaptar à resolução. Lembre da regra da seção anterior: Control não compartilha o sistema de posição do Node2D, então não misture os dois esperando que se comportem igual.
O 3D tem o espelho de quase tudo isso (Sprite3D, CharacterBody3D, Camera3D, MeshInstance3D, RayCast3D), com a mesma lógica de árvore e herança. Aprendeu o modelo em 2D, o 3D é o mesmo jogo com um eixo a mais. E quando um node pronto não faz exatamente o que você quer, a saída não é lutar contra ele: é pendurar um script e estender, ou compor com um filho, como vimos acima.
Três hábitos que evitam dor de cabeça
Não abuse de get_parent() e caminhos longos. Um get_node("../../UI/HUD/Barra") quebra na primeira reorganização da cena. Prefira sinais, grupos (add_to_group e get_tree().call_group) ou exportar a referência com @export var alvo: Node2D e arrastar no Inspector.
Cuidado com a quantidade, mas sem paranoia. Node tem custo, e milhares de nodes processando todo frame pesam. Mas o erro de iniciante é o oposto: amontoar toda a lógica num script gigante "pra economizar nodes". Organize primeiro, otimize quando o profiler reclamar.
Nomeie nodes pelo papel, não pelo tipo. Hurtbox diz mais que Area2D, e MusicaDeFundo diz mais que AudioStreamPlayer2. O nome é o caminho que você vai digitar em $, então faça ele contar a história.
Grupos: falando com muitos nodes de uma vez
A árvore resolve a comunicação de pai pra filho e de filho pra pai, mas falta um caso: avisar um monte de nodes espalhados que não têm relação de parentesco entre si. Pausar todos os inimigos, somar pontos de todas as moedas, recarregar todas as armas. Pra isso o Godot tem grupos, que são etiquetas de texto que você cola em qualquer node.
Adicione um node a um grupo pelo editor (aba Node > Groups) ou por código:
func _ready() -> void:
add_to_group("inimigos")
E, de qualquer lugar, chame um método em todos os membros do grupo de uma vez:
func pausar_inimigos() -> void:
get_tree().call_group("inimigos", "set_physics_process", false)
func contar_inimigos() -> int:
return get_tree().get_node_count_in_group("inimigos")
O ganho é desacoplamento: quem dá a ordem não precisa de referência direta a cada inimigo, nem de saber onde eles estão na árvore, e o inimigo também não sabe quem o chamou. Isso casa com a regra "chame pra baixo, sinalize pra cima" da seção anterior: grupo é a terceira via, pra quando o alvo não está nem acima nem abaixo de você na árvore. Use com parcimônia, porque call_group percorre todos os membros toda vez; pra algo que roda a cada frame, prefira guardar a lista você mesmo.
Fechando
Nodes do Godot não são um detalhe da engine, são a engine. O modelo cabe em quatro frases: tudo é node, nodes formam árvores, árvores salvas são cenas, e cenas se instanciam dentro de cenas. Comunicação desce por chamada direta e sobe por sinal. E na hora de estruturar, componha primeiro, herde com moderação.
Se quiser fixar, pegue um projeto seu (ou comece um) e extraia um comportamento repetido pra um node componente: vida, knockback, drop de item, qualquer um. Conecte por sinal, reuse em dois objetos diferentes e sinta a diferença. Esse exercício de uma hora ensina mais sobre a árvore de cena do que qualquer leitura. E quando bater a dúvida de qual linguagem usar para escrever esses scripts, compare C# vs GDScript na Godot para escolher com critério.


