Módulo 9 · Aula 9.6 · Chave, porteira e saída
| Carga horária | Dificuldade | Ferramenta | Competências |
|---|---|---|---|
| 8 h/a | ⭐⭐⭐ | Godot 4.7 | C2, C3 |
Objetivos de aprendizagem
- Usar
Area3De o sinalbody_enteredem 3D (igual ao 2D). - Separar quem avisa (chave, porteira, saída) de quem decide (cena do labirinto).
- Abrir uma porta com Tween e desligar a colisão com
set_deferred. - Guardar o melhor tempo, em que o menor número vence.
Pré-requisitos
Aulas 9.3 a 9.5; Aulas 8.4 (sinais), 8.6 (Autoload) e 8.9 (Tween).
Materiais
Godot 4.7; chave-da-porteira (cenas/chave.tscn, porteira.tscn, saida.tscn, scripts/labirinto.gd, scripts/global.gd).
Conceitos
Mesma ideia do Sementes. A semente avisava coletada e a cena do jogo contava o ponto. Aqui:
Chave --body_entered--> chave.gd --pega-------------------> labirinto.gd: _on_chave_pega
Porteira/Perto --body_entered--> porteira.gd --jogador_chegou(jogador)--> labirinto.gd: _on_porteira_jogador_chegou
Saida --body_entered--> saida.gd --jogador_saiu-----------> labirinto.gd: _on_saida_jogador_saiuA porteira não decide. Quando alguém chega perto, ela só avisa quem chegou. A cena do labirinto olha tem_chave e manda abrir, ou mostra o aviso "A porteira está trancada. Ache a chave!". Assim, numa fase nova, a mesma porteira pode abrir com outra regra (três castanhas, um botão).
Corpo e área juntos. A porteira tem duas partes: um StaticBody3D (Corpo), que bloqueia o caminho, e uma Area3D (Perto), maior, que percebe quem se aproxima dos dois lados.
Dobradiça. Para a porta girar pela ponta, e não pelo meio, ela fica dentro de um nó vazio Dobradica posto na ponta. Gira-se a dobradiça, e a porta vai junto. É o mesmo truque de pivô da animação no Módulo 5.
set_deferred. Desligar a colisão bem na hora em que a física está conferindo encontros dá erro (lembra do call_deferred da Aula 8.5?). colisao.set_deferred("disabled", true) deixa para o fim do quadro.
Menor tempo vence. No Sementes, recorde era o maior número de pontos. Aqui é o menor tempo. E o primeiro tempo de todos precisa virar recorde, por isso melhor_tempo começa em 0, que quer dizer "ainda não tem".
Exemplos
| Situação | O que acontece | Conferido no teste |
|---|---|---|
| chega na porteira sem chave | aviso de trancada; a porteira segura o jogador | sim |
| encosta na chave | chave some; placar "Chave: achou!" | sim |
| chega na porteira com chave | colisão desligada; a porta gira 100° em 0,8 s | sim |
| pisa na saída | tela de fim com o tempo e o melhor tempo | sim |
| tempo pior que o melhor | o melhor não muda | sim |
Tutorial passo a passo
Parte A. Ler as três cenas (80 min)
- Abra
chave.tscn:Area3Dcom esfera de colisão, argola (TorusMesh), haste e dente, e umOmniLight3D(Brilho) que faz a chave chamar atenção no corredor. - Abra
porteira.tscne selecionePerto(captura): a caixa da área é mais funda que a porta, para perceber de longe. - Abra
saida.tscn: disco verde com emissão e umLabel3D"SAÍDA" que sempre vira para a câmera (Billboard). - Em
labirinto.tscn, selecionePorteirae veja na aba Sinais ojogador_chegouligado à cena.
Parte B. Fazer funcionar no seu labirinto (160 min)
- Copie as três cenas e os três scripts para o seu projeto da Aula 9.5 e instancie cada uma (Instanciar Cena Filha...).
- Conecte os três sinais à cena do labirinto pela aba Sinais e escreva as três funções (código abaixo).
- Jogue: tente abrir sem chave, pegue a chave, abra, saia.
Parte C. Melhor tempo (80 min)
- Leia
global.gd. Explique para a dupla oif melhor_tempo == 0.0 or tempo < melhor_tempo. - Jogue três vezes. A tela de fim mostra o melhor tempo certo?
Imagens
../../../recursos/imagens/godot/chave-da-porteira-tela-trancada.png
: chegando sem chave.../../../recursos/imagens/godot/chave-da-porteira-tela-aberta.png
: porteira aberta, saída à vista.../../../recursos/imagens/godot/chave-da-porteira-tela-fim.png
: tempo e melhor tempo.../../../recursos/imagens/godot/captura-godot3d-porteira.png
: a cena da porteira com Pertoselecionado.
Indicação da interface
| Item | Nome na tela |
|---|---|
| sinais do nó | aba Sinais, Conectar... |
| instanciar | Instanciar Cena Filha... |
| texto 3D que olha para a câmera | nó Label3D, propriedade Billboard |
O código completo
scripts/porteira.gd:
extends Node3D
## Porteira de madeira. Ela não decide nada: só avisa que o jogador chegou perto
## e sabe se abrir quando mandam.
signal jogador_chegou(jogador: Node3D)
var aberta := false
@onready var dobradica: Node3D = $Dobradica
@onready var colisao: CollisionShape3D = $Dobradica/Corpo/Colisao
func _on_perto_body_entered(corpo: Node3D) -> void:
if corpo.is_in_group("jogador"):
jogador_chegou.emit(corpo)
func abrir() -> void:
if aberta:
return
aberta = true
# Mexer em colisão no meio da física pede set_deferred (como o call_deferred do Módulo 8).
colisao.set_deferred("disabled", true)
var tween := create_tween()
tween.tween_property(dobradica, "rotation_degrees:y", 100.0, 0.8)Trecho de scripts/labirinto.gd:
func mostrar_aviso(texto: String) -> void:
aviso.text = texto
aviso.show()
await get_tree().create_timer(2.5).timeout
if aviso.text == texto:
aviso.hide()
func _on_chave_pega() -> void:
jogador.tem_chave = true
texto_chave.text = "Chave: achou!"
mostrar_aviso("Achou a chave! Agora procure a porteira.")
func _on_porteira_jogador_chegou(quem: Node3D) -> void:
if quem.tem_chave:
porteira.abrir()
else:
mostrar_aviso("A porteira está trancada. Ache a chave!")
func _on_saida_jogador_saiu() -> void:
if terminou:
return
terminou = true
Global.terminar_partida(tempo)
get_tree().change_scene_to_file.call_deferred("res://cenas/fim.tscn")Trecho de scripts/global.gd:
func terminar_partida(tempo: float) -> void:
tempo_da_partida = tempo
if melhor_tempo == 0.0 or tempo < melhor_tempo:
melhor_tempo = tempo
salvar_recorde()Explicação do código
signal jogador_chegou(jogador: Node3D)é um sinal que leva um valor: quem chegou. Era o desafio da semente dourada na Aula 8.4.rotation_degrees:ydeixa o Tween mexer só no giro em volta do Y.- Em
mostrar_aviso, oif aviso.text == textoevita que um aviso velho apague um aviso novo que apareceu nesse meio-tempo. terminouimpede que pisar duas vezes na saída grave o tempo duas vezes.
Resultado esperado
Chave, porteira e saída funcionando no labirinto do aluno, com avisos na tela e melhor tempo salvo.
Problemas comuns
| Sintoma | Causa | Solução |
|---|---|---|
| a porteira nunca avisa | Perto pequeno demais, ou sinal não conectado | conferir a caixa da área e a aba Sinais |
| a porta gira pelo meio | girou o Corpo em vez da Dobradica | girar o nó da ponta |
| porta aberta, mas continua batendo | colisão não desligada | set_deferred("disabled", true) |
| o primeiro tempo nunca vira recorde | melhor_tempo começa em 0 e o if só olha tempo < melhor_tempo | incluir melhor_tempo == 0.0 or |
| a chave some quando a câmera passa | a chave não confere o grupo | is_in_group("jogador") |
Exercício guiado
Na preparação, o teste "porteira abre com a chave" falhou na primeira vez. O jogo estava certo: o teste colocava o jogador a 2,7 m da porteira, fora da caixa Perto, que vai só até 2,2 m. Como o teste poderia ter avisado isso mais cedo? (Resposta possível: conferindo primeiro o aviso de trancada, que também depende da área; ele falhou junto e apontava para a posição.)
Desafio individual
Faça a porteira dar um aviso diferente quando o jogador chega pela segunda vez sem chave ("Ainda trancada...").
Desafio avançado (opcional)
Coloque duas chaves e duas porteiras, cada chave abrindo só a sua (dica: um @export var cor := "azul" na chave e na porteira).
Critérios de avaliação
- Os três sinais funcionando no labirinto próprio.
- Explica por que a porteira não decide sozinha.
- Melhor tempo com menor número vencendo.
Checklist de conclusão
Extensão
Liste três coisas de um jogo que você conhece que "avisam" e quem "decide" o que fazer com o aviso.