Godot do Zero: Cenas, nós e composição do jogo 2D

Capítulo 2

Tempo estimado de leitura: 8 minutos

+ Exercício

O que são cenas e nós (e por que isso muda sua forma de construir jogos)

Na Godot, um jogo 2D é construído como uma composição de cenas reutilizáveis, e cada cena é uma árvore de nós. Pense em cena como um “bloco” que você pode salvar, instanciar várias vezes e combinar com outros blocos. Pense em como uma peça com uma responsabilidade clara: desenhar um sprite, detectar colisão, tocar áudio, controlar movimento, organizar filhos, etc.

Essa abordagem favorece: reuso (um inimigo pode ser instanciado várias vezes), organização (cada nó faz uma coisa), e manutenção (alterar uma cena-base atualiza variações por herança).

Cena como “prefab”

Quando você salva uma cena (por exemplo, Player.tscn), ela vira um recurso que pode ser instanciado dentro de outras cenas. Isso é equivalente ao conceito de “prefab”: um modelo pronto para ser colocado no mundo quantas vezes você quiser.

Árvore de nós: hierarquia, responsabilidades e organização

Uma cena sempre tem um nó raiz e pode ter filhos, netos, etc. A hierarquia importa porque:

  • Transformações 2D (posição, rotação, escala) são herdadas: mover o pai move os filhos.
  • Organização lógica: agrupar nós relacionados (ex.: Player com Sprite2D e CollisionShape2D).
  • Ordem de desenho: em 2D, a ordem visual depende de camadas e/ou profundidade (mais abaixo).

Quando usar Node2D, CharacterBody2D, Area2D e CanvasLayer

TipoUse quando…Exemplo típico
Node2DVocê precisa de transformações 2D (posição/rotação/escala), mas não precisa de física de personagem pronta.Um marcador, um container de elementos, um inimigo estático, um spawner.
CharacterBody2DVocê quer mover um personagem com colisão sólida e controle de movimento (ex.: move_and_slide()).Jogador que anda e colide com paredes/chão.
Area2DVocê quer detectar sobreposições (entrar/sair), gatilhos e hitboxes, sem ser um corpo sólido por padrão.Hitbox de ataque, coletáveis, zona de dano, sensor de visão.
CanvasLayerVocê quer UI/HUD desenhada acima do mundo, independente da câmera e da profundidade do cenário.Barra de vida, pontuação, botões, overlay.

Transformações 2D na prática (posição, rotação, escala)

Em nós 2D (como Node2D, CharacterBody2D, Area2D), você trabalha com propriedades como position, rotation e scale. Como a transformação é hierárquica, um Sprite2D filho “segue” o pai.

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

# Exemplo simples: mover um Node2D para a direita ao longo do tempo (em _process)
extends Node2D

func _process(delta):
	position.x += 100.0 * delta

Em personagens com física (como CharacterBody2D), o movimento normalmente é feito por velocidade e métodos de movimento, não alterando posição diretamente a cada frame.

Ordem de desenho (quem aparece na frente?)

Em 2D, a Godot pode ordenar o desenho por diferentes mecanismos. Os mais comuns:

  • Z Index (em nós como CanvasItem, ex.: Sprite2D): valores maiores desenham na frente.
  • Y Sort (quando você usa um nó apropriado para ordenar por Y): objetos “mais abaixo” (maior Y) podem aparecer na frente, útil para visão top-down.
  • CanvasLayer: UI em uma camada separada, sempre acima/abaixo conforme a layer.

Regra prática: use CanvasLayer para HUD; use Z Index para ajustes pontuais; use ordenação por Y quando fizer sentido para profundidade “2.5D” em top-down.

Passo a passo: criando cenas reutilizáveis (Main, Player, Enemy)

1) Criar a cena do Player (Player.tscn)

Objetivo: um personagem controlável com colisão sólida.

  • Crie uma nova cena com nó raiz CharacterBody2D e renomeie para Player.
  • Adicione um Sprite2D como filho (para visual).
  • Adicione um CollisionShape2D como filho (para colisão). Configure uma forma (ex.: RectangleShape2D).
  • Anexe um script ao Player (GDScript) para movimentação básica.
extends CharacterBody2D

@export var speed: float = 220.0

func _physics_process(delta):
	var input_vector = Vector2(
		Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left"),
		Input.get_action_strength("ui_down") - Input.get_action_strength("ui_up")
	)
	velocity = input_vector.normalized() * speed
	move_and_slide()

Observação: este exemplo usa ações padrão (ui_up, ui_down, etc.). Se você estiver usando ações personalizadas, ajuste os nomes.

Salve a cena como Player.tscn.

2) Criar a cena do Enemy (Enemy.tscn)

Objetivo: um inimigo simples com presença física (colisão) e uma área de detecção (sensor).

  • Crie uma nova cena com nó raiz Node2D e renomeie para Enemy.
  • Adicione um Sprite2D (visual).
  • Para colisão sólida, você pode escolher duas abordagens:
  • Abordagem A (mais simples para começar): manter como Node2D e usar apenas Area2D para detectar o player (sem colisão sólida).
  • Abordagem B (colisão sólida): trocar o nó raiz para CharacterBody2D ou RigidBody2D (dependendo do comportamento). Para este capítulo, foque na composição e mantenha o inimigo simples.

Vamos usar Node2D + Area2D para reforçar o papel de sensor:

  • Adicione um Area2D como filho do Enemy e renomeie para DetectionArea.
  • Dentro de DetectionArea, adicione CollisionShape2D e configure a forma (ex.: círculo).
  • Conecte o sinal body_entered do DetectionArea ao script do Enemy (ou faça via código).
extends Node2D

func _ready():
	$DetectionArea.body_entered.connect(_on_detection_body_entered)

func _on_detection_body_entered(body):
	if body.name == "Player":
		print("Player detectado!")

Salve a cena como Enemy.tscn.

3) Criar a cena principal (Main.tscn) e instanciar Player/Enemy

Objetivo: uma cena que compõe o jogo instanciando outras cenas.

  • Crie uma nova cena com nó raiz Node2D e renomeie para Main.
  • Crie um nó filho Node2D chamado World (onde ficam player/inimigos).
  • Crie um nó CanvasLayer chamado HUD (onde ficará UI). Dentro dele você pode adicionar um Label (opcional) para testar.
  • No Main, anexe um script para instanciar as cenas.
extends Node2D

@export var player_scene: PackedScene
@export var enemy_scene: PackedScene

func _ready():
	var player = player_scene.instantiate()
	$World.add_child(player)
	player.position = Vector2(200, 200)

	for i in 3:
		var enemy = enemy_scene.instantiate()
		$World.add_child(enemy)
		enemy.position = Vector2(400 + i * 80, 220)

Passo importante: no Inspector do Main, arraste Player.tscn para player_scene e Enemy.tscn para enemy_scene. Assim você evita caminhos hardcoded e mantém o projeto flexível.

Salvamento, instanciação e edição: fluxo de trabalho recomendado

  • Crie uma cena pequena (ex.: Player), teste isoladamente.
  • Salve como .tscn e trate como “prefab”.
  • Instancie em Main e ajuste posições/quantidade.
  • Quando precisar de variações (ex.: inimigo rápido), use herança de cena.

Herança de cenas: criando variações sem duplicar trabalho

Herança de cena permite criar uma cena “filha” baseada em outra. Você reaproveita a estrutura e altera apenas o necessário (valores exportados, sprites, comportamento).

Passo a passo: Enemy base e EnemyFast

  • Garanta que Enemy.tscn seja sua base.
  • Crie uma nova cena a partir de Enemy.tscn usando a opção de instanciar/derivar (criar cena herdada).
  • Salve como EnemyFast.tscn.
  • No script, use variáveis exportadas para permitir variação sem reescrever lógica.
extends Node2D

@export var patrol_speed: float = 60.0

func _process(delta):
	position.x += patrol_speed * delta

Na cena herdada EnemyFast.tscn, altere patrol_speed no Inspector (ex.: 120). Assim você cria variações mantendo a mesma base.

Agrupamento lógico: organizando e acessando conjuntos de nós

Além da hierarquia, você pode usar Groups para marcar nós por função (ex.: todos os inimigos). Isso facilita buscar e aplicar lógica em lote.

  • Adicione o Enemy ao grupo enemies (via Inspector > Node > Groups).
  • No Main, você pode encontrar todos os inimigos:
for e in get_tree().get_nodes_in_group("enemies"):
	e.position.x += 10

Exercícios práticos (para fixar composição com cenas)

Exercício 1: Estrutura mínima das cenas

  • Player.tscn: raiz CharacterBody2D + Sprite2D + CollisionShape2D.
  • Enemy.tscn: raiz Node2D + Sprite2D + Area2D (com CollisionShape2D).
  • Main.tscn: raiz Node2D + World (Node2D) + HUD (CanvasLayer).

Exercício 2: Instanciação mista (editor + código)

  • Instancie o Player manualmente no editor dentro de World.
  • Instancie os Enemy por código (loop) no _ready().
  • Compare o fluxo: quando é mais prático instanciar no editor e quando é melhor por código?

Exercício 3: Herança de cena para variações

  • Crie EnemyFast.tscn herdando de Enemy.tscn.
  • Altere apenas um parâmetro exportado (velocidade, tamanho da área de detecção, cor/modulação do sprite).
  • No Main, instancie 2 inimigos normais e 1 rápido.

Exercício 4: Camadas e HUD com CanvasLayer

  • Dentro de HUD, adicione um Label com um texto de teste (ex.: “HP: 3”).
  • Mova o player e inimigos: confirme que o HUD permanece “por cima” e não se mistura com o mundo.
  • Opcional: ajuste layer do CanvasLayer se você tiver múltiplas camadas de UI.

Agora responda o exercício sobre o conteúdo:

Ao montar a cena Main com os nós World e HUD, qual abordagem garante que a interface (HUD) fique desenhada acima do mundo e não seja afetada pela câmera ou pela profundidade do cenário?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

O CanvasLayer desenha a UI em uma camada separada do mundo, mantendo o HUD acima (ou abaixo) conforme a layer, sem depender da profundidade do cenário ou do movimento da câmera.

Próximo capitúlo

Godot do Zero: Fundamentos de GDScript aplicados ao 2D

Arrow Right Icon
Capa do Ebook gratuito Godot do Zero: Criando seu Primeiro Jogo 2D com GDScript
12%

Godot do Zero: Criando seu Primeiro Jogo 2D com GDScript

Novo curso

17 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.