Aula de Godot · Nível 2

Tudo no Godot é um Nó

Você já faz o personagem andar. Agora vamos entender por que ele anda: as peças (nós), a árvore que junta essas peças e a linguagem GDScript que dá vida a elas. Cada parte tem uma demonstração para mexer.

Viewport · monte o Jogadorclique nos nós para adicionar
Jogador CharacterBody2D
Um nó sozinho não faz quase nada. Adicione filhos e veja o Jogador ganhar forma.
Conceito 1

O que é um nó

Um nó é uma peça com uma função só. O Sprite2D mostra uma imagem. O CollisionShape2D dá um corpo para bater nas coisas. O Camera2D decide o que aparece na tela. Sozinhos eles fazem pouco; juntos viram um personagem.

Na demonstração lá em cima, tire o CollisionShape2D e repare no aviso. O Godot mostra esse mesmo aviso (o triângulo amarelo) quando um corpo está sem forma de colisão.

Fala do professor

“Pensa no Lego. Uma pecinha sozinha não é nada. Mas uma roda, mais um volante, mais um banco, vira um carro. No Godot cada pecinha se chama nó, e o carro montado se chama cena.”

  • Olha que legal: na demo, adicione só o Sprite2D. Dá para ver o robô, mas ele não “existe” para a física. Ele atravessaria uma parede.
  • Pergunte: “Quais nós o seu personagem tem hoje? Abre o projeto e me mostra.”
Conceito 2

Pai e filho: a árvore de cena

Os nós ficam numa árvore. O nó de cima é o pai e os de dentro são os filhos. A regra de ouro: o filho vai aonde o pai vai. A posição do filho é contada a partir do pai. Isso se chama posição local; a posição de verdade na tela é a global.

Viewport · pai e filho
Jogador
Espada
Fala do professor

“A espada é filha do Jogador. Quando o Jogador anda, ninguém precisa mandar a espada andar: ela vai junto, sozinha. É como a sua mochila: você não precisa mandar a mochila ir pra escola, ela vai porque está presa em você.”

  • Olha que legal: mexa só no slider do Jogador. A espada segue. Depois mexa só na Espada: ela muda de lado, mas continua presa ao Jogador.
  • Pergunte: “Se eu quero que a câmera siga o personagem, onde eu coloco o nó Camera2D?” (Resposta: como filho do personagem.)
Conceito 4

GDScript: o “Python” do Godot

GDScript foi feita para parecer Python. Se você aprender um, já sabe metade do outro. As duas usam indentação (o recuo com Tab) para saber o que fica dentro de quê. A diferença maior: em Python a função começa com def, e em GDScript com func.

Python
# uma variável
vida = 3

# uma função
def levar_dano(quanto):
	global vida
	vida = vida - quanto
	if vida <= 0:
		print("Game Over")

levar_dano(1)
GDScript
extends CharacterBody2D

# uma variável
var vida = 3

# uma função
func levar_dano(quanto):
	vida = vida - quanto
	if vida <= 0:
		print("Game Over")
		queue_free()  # some da tela
Fala do professor

“Olha como é quase igual! Quem aprende GDScript está aprendendo Python de brinde, e Python é uma das linguagens mais usadas do mundo: inteligência artificial, sites, robótica.”

  • Olha que legal: a primeira linha, extends CharacterBody2D, diz que esse script “é” um CharacterBody2D. Por isso ele já sabe usar velocity e move_and_slide() sem a gente programar isso.
  • Pergunte: “O que acontece se eu tirar o Tab da linha do print?” (O Godot acusa erro, porque não sabe mais que o print está dentro do if.)
Conceito 5

Variáveis e tipos

Variável é uma caixinha com nome que guarda um valor. O tipo diz o que cabe na caixinha: número inteiro (int), número com vírgula (float), texto (String), verdadeiro ou falso (bool) e posição x,y (Vector2). Mexa nos controles e veja o código e o robô mudarem juntos.

Variáveis ao vivo

  
Fala do professor

“O jogo inteiro é feito de caixinhas. A vida do personagem, os pontos, a fase em que você está, se a porta está aberta ou fechada: tudo é variável.”

  • Olha que legal: coloque a velocidade em 0. O robô para, mas o código continua rodando. Ele só está andando “0 pixels por segundo”.
  • Pergunte: “Que tipo de caixinha você usaria para guardar se o jogador pegou a chave?” (bool)
  • Dica: escrever o tipo (var vida: int = 3) é opcional, mas ajuda o Godot a avisar erros mais cedo.
Conceito 6

As funções que o Godot chama sozinho

Algumas funções têm nome especial e começam com _. Você não chama essas funções: o Godot chama por você, na hora certa. _ready() roda uma vez quando o nó entra na cena. _process(delta) roda a cada quadro desenhado. _physics_process(delta) roda 60 vezes por segundo, certinho, e é onde fica o movimento. _input(event) roda quando você aperta uma tecla ou clica.

Contador de chamadas
_ready()
0
vezes. Só no começo.
_process(delta)
0
quadros desenhados
_physics_process(delta)
0
passos de física
delta
0.000
segundos desde o último quadro
Aperte “Rodar cena” (é o F5 do Godot).
func _ready():
	print("Nasci!")  # roda 1 vez

func _physics_process(delta):
	# roda 60 vezes por segundo
	position.x += velocidade * delta

func _input(event):
	if event.is_action_pressed("ui_accept"):
		print("Apertou Espaço ou Enter!")
Fala do professor

“O _physics_process é o coração do jogo batendo: 60 batidas por segundo. Cada batida, o personagem anda um pouquinho. Como é muito rápido, o olho vê movimento, igual desenho animado.”

  • Olha que legal: rode por 10 segundos e veja o contador de física passar de 600. Isso é 60 × 10.
  • O que é delta: o tempo entre uma batida e outra. Multiplicar a velocidade por delta faz o jogo andar igual em computador rápido e em computador lento.
  • Pergunte: “Onde você colocaria um código que toca a música do jogo uma vez só, no começo?” (no _ready)
Conceito 7

if, elif, else: o jogo tomando decisões

O if pergunta algo que é verdadeiro ou falso. Se for verdade, roda o que está dentro. O elif (“senão, se”) faz outra pergunta, e o else (“senão”) pega todo o resto. Só um caminho roda de cada vez. Arraste a vida e veja qual linha acende.

Qual caminho roda?

  
Fala do professor

“O if é o cérebro do jogo. O inimigo te viu? A porta está trancada? Você pegou todas as moedas? Toda decisão do jogo é um if.”

  • Olha que legal: coloque a vida em exatamente 30. Cai no else, porque 30 não é menor que 30. Os sinais < e <= fazem diferença!
  • Comparações: == igual, != diferente, < menor, > maior. Um = só guarda valor; dois == comparam.
Conceito 8

for: repetir sem copiar e colar

O for repete um bloco de código várias vezes. range(5) gera os números 0, 1, 2, 3, 4, e a variável i recebe um de cada vez. Repare: começa no zero.

Criando moedas com for

  
Fala do professor

“Imagina colocar 100 moedas na fase arrastando uma por uma. Cansativo, né? Com 2 linhas de for o computador faz isso pra gente. Programador é preguiçoso do jeito certo: faz o computador trabalhar.”

  • Olha que legal: coloque 12 e rode. Cada moeda fica num lugar diferente porque a posição usa o i: i * 50.
Conceito 9

Funções: dar nome a uma ação

Uma função é um pedaço de código com nome, que você chama quando quiser. Ela pode receber valores (os parâmetros, entre parênteses) e pode devolver um resultado com return.

Chamando pular(forca)
func pular(forca: float) -> void:
	if is_on_floor():
		velocity.y = -forca  # para cima é negativo!

func calcular_dano(base: int, critico: bool) -> int:
	if critico:
		return base * 2
	return base

# usando:
pular(300)
var dano = calcular_dano(10, true)  # dano vale 20
Fala do professor

“Função é como um botão de controle remoto. Você não precisa saber como a TV funciona por dentro, só aperta ‘aumentar volume’. Aqui o botão se chama pular, e o número entre parênteses diz a força.”

  • Olha que legal: no Godot, o eixo Y é para baixo. Por isso pular é velocidade negativa. Isso confunde todo mundo no começo!
  • Pergunte: “Que ações do seu jogo poderiam virar funções?” (atacar, morrer, abrir porta, pegar item)
Conceito 10 · o mais importante

Sinais: os nós conversando

Um sinal é um aviso que um nó dá quando algo acontece com ele. A Area2D avisa “alguém entrou em mim” (body_entered). O Button avisa “fui apertado” (pressed). O Timer avisa “acabou o tempo” (timeout). Outros nós escutam o aviso e reagem. Arraste o robô até as moedas (ou clique na área e use as setas).

Viewport · sinal body_enteredpontos: 0
Nenhum sinal ainda. Encoste o robô numa moeda.
extends Area2D  # moeda.gd

signal coletada  # um sinal criado por nós

func _ready():
	body_entered.connect(_on_body_entered)

func _on_body_entered(body):
	if body.name == "Jogador":
		coletada.emit()   # avisa quem estiver escutando
		queue_free()
Fala do professor

“Sinal é como a campainha da sua casa. A campainha não sabe quem vai atender. Ela só toca. Quem estiver ouvindo vai até a porta. A moeda faz a mesma coisa: só avisa ‘me pegaram!’, e o placar, o som e o que mais quiser escutar reagem.”

  • Olha que legal: no editor, selecione qualquer nó e abra a aba Nó (ao lado do Inspetor). Ali aparece a lista de todos os sinais que ele pode dar. Dá para conectar com dois cliques, sem código.
  • Por que é importante: com sinais, a moeda não precisa conhecer o placar. Cada nó cuida da sua vida, e o jogo fica organizado mesmo quando cresce.
  • Pergunte: “Que sinal você usaria para o inimigo perceber que o jogador chegou perto?” (body_entered de uma Area2D em volta do inimigo)
Conceito 11

@export: mexer no código pelo Inspetor

Colocando @export antes de uma variável, ela aparece no Inspetor. Aí dá para ter um script só e vários inimigos diferentes: cada cópia com sua vida, velocidade e cor, sem mexer no código.

Inspetor · inimigo.gd
vida
velocidade
cor
pode_voar

  
Fala do professor

“Os dois inimigos usam o mesmíssimo script. A diferença está só nos valores do Inspetor. É assim que jogos grandes fazem dezenas de inimigos sem escrever dezenas de códigos.”

  • Olha que legal: com @export, até quem não programa (um designer de fases) consegue ajustar o jogo. Peça ao aluno para exportar a velocidade do personagem dele e testar valores com o jogo rodando.
Conceito 12

Cenas como moldes: instanciar

Uma cena salva (inimigo.tscn) é um molde. Com preload o jogo carrega o molde, e com instantiate() cria uma cópia nova enquanto o jogo roda. Junto com um Timer, dá para fazer inimigos nascerem sem parar. Ligue o Timer e veja a árvore de cena crescer e diminuir.

Spawner · Timer.timeout a cada 1 s
Timer desligado.
extends Node2D  # principal.gd

const INIMIGO = preload("res://cenas/inimigo.tscn")

func _on_timer_timeout():
	var novo = INIMIGO.instantiate()
	novo.position = Vector2(randf_range(0, 400), 0)
	add_child(novo)

# e no inimigo.gd, para sumir ao sair da tela:
func _physics_process(delta):
	position.y += 120 * delta
	if position.y > 700:
		queue_free()
Fala do professor

“A cena é como a forminha de biscoito. Você faz a forminha uma vez e tira quantos biscoitos quiser. Tiros, inimigos, moedas, explosões: tudo nasce assim.”

  • Olha que legal: repare que cada inimigo que sai da tela é apagado com queue_free(). Se não apagar, eles continuam existindo invisíveis e o jogo fica lento.
  • Dica de editor: com o jogo rodando, a aba Remoto no painel Cena mostra a árvore ao vivo, igual a esta demonstração.
Para o professor

Roteiro de 60 minutos

A ideia é alternar: mostrar a demonstração, fazer a pergunta e deixar o aluno aplicar no jogo dele. Ele já sabe fazer o personagem andar, então use o projeto dele como laboratório.

TempoO que fazerNo jogo do aluno
0–5 minAbrir esta página. Montar o Jogador na demonstração do topo.Ele mostra a árvore de cena do personagem dele e explica cada nó.
5–15 minNós, pai e filho, catálogo.Colocar uma Camera2D como filha do personagem e ver a câmera seguir.
15–25 minGDScript e Python, variáveis e tipos.Ler o script de movimento dele e apontar cada variável e o tipo dela.
25–35 min_ready e _physics_process, if, funções.Colocar print("Nasci!") no _ready e ver aparecer no painel Saída.
35–50 minSinais (com calma, é o mais importante).Criar uma moeda com Area2D que some ao ser tocada, usando body_entered.
50–57 min@export e instanciar.Colocar @export na velocidade e ajustar pelo Inspetor.
57–60 minRevisão com as perguntas abaixo.Anotar uma ideia para a próxima aula.

Perguntas de revisão

  1. Se o pai anda, o que acontece com o filho? (vai junto)
  2. Qual função roda uma vez só, quando o nó nasce? (_ready)
  3. Qual a diferença entre = e ==? (guardar e comparar)
  4. O que um sinal faz? (avisa que algo aconteceu, e quem escuta reage)
  5. Para que serve o @export? (mudar o valor pelo Inspetor)

Se sobrar tempo ou para casa

  1. Fazer um placar com Label que sobe quando a moeda emite o sinal coletada.
  2. Usar um Timer para fazer moedas nascerem a cada 2 segundos.
  3. Criar uma função levar_dano(quanto) e um if que reinicia a fase quando a vida chega a 0, com get_tree().reload_current_scene().
Dica para você, professor

Você não precisa saber tudo. Quando o aluno perguntar algo que você não sabe, diga: “Boa pergunta, vamos descobrir juntos”, e pesquise na documentação do Godot (docs.godotengine.org) ou pergunte para mim. Ensinar o aluno a pesquisar é uma das lições mais valiosas da programação.