Fundamentos do Godot: Desenvolvimento de games para iniciantes
Gerenciamento de Tempo e Temporizadores (Timers)
Aprenda sobre gerenciamento de tempo e temporizadores (timers)
Gerenciamento de Tempo e Temporizadores (Timers) ⏱️
Bem-vindos à aula sobre Gerenciamento de Tempo e Temporizadores! Nesta seção, vamos explorar como controlar o fluxo do tempo em seus jogos Godot, permitindo que você crie eventos com atraso, ações recorrentes e mecânicas baseadas em tempo.
O tempo é um elemento fundamental em qualquer jogo. Seja para um inimigo atirar a cada 3 segundos, uma poção de vida que recarrega após um tempo, ou um power-up que dura apenas 10 segundos, os temporizadores são ferramentas indispensáveis.
1. Introdução Clara
Em Godot, temos várias maneiras de lidar com o tempo. A mais comum e versátil é através do nó Timer e, para situações mais simples e de uso único, o SceneTreeTimer. Compreender e utilizar essas ferramentas de forma eficaz é crucial para criar experiências de jogo dinâmicas e responsivas.
Nesta aula prática, vamos mergulhar nos detalhes de como usar o nó Timer e o SceneTreeTimer com exemplos de código atualizados para o GDScript 4.x.
2. Explicação Detalhada com Exemplos
Por que precisamos de Temporizadores? 🤔
- Atrasos: Executar uma ação após um certo período (ex: explosão após 2 segundos).
- Eventos Recorrentes: Repetir uma ação em intervalos regulares (ex: inimigo atirando a cada 3 segundos).
- Cooldowns: Impedir que o jogador use uma habilidade repetidamente (ex: habilidade com 5 segundos de recarga).
- Duração de Efeitos: Fazer com que um efeito ou power-up dure por um tempo limitado.
O Nó Timer ⏳
O nó Timer é a forma mais robusta e flexível de gerenciar tempo em Godot. Ele é um nó que pode ser adicionado à sua cena e configurado através do Inspetor ou via código.
Propriedades Chave:
wait_time: (float) O tempo, em segundos, que o temporizador espera antes de emitir o sinaltimeout.one_shot: (bool) Setrue, o temporizador emitirátimeoutapenas uma vez e parará. Sefalse, ele repetirá indefinidamente.autostart: (bool) Setrue, o temporizador começará a contagem regressiva assim que a cena for carregada.process_callback: (enum) Define como o temporizador processa o tempo (pode ser_processou_physics_process).
Métodos Chave:
start(time_sec: float = -1.0): Inicia o temporizador. Setime_secfor fornecido, ele definewait_timeantes de iniciar.stop(): Para o temporizador.is_stopped(): Retornatruese o temporizador estiver parado.
Sinal Chave:
timeout: Emitido quando o temporizador atinge owait_timedefinido.
SceneTreeTimer (Atrasos de Uso Único) ⏱️➡️🎯
Para atrasos simples e de uso único, especialmente quando você não precisa de um nó Timer persistente na sua cena, Godot 4 oferece o SceneTreeTimer. Ele é útil para pausas rápidas ou para esperar por um evento sem a necessidade de criar e gerenciar um nó Timer completo.
Você pode usá-lo com await para pausar a execução do código até que o tempo expire, ou conectar seu sinal timeout a uma função.
3. Código de Exemplo Oficial (Adaptado da Documentação)
Vamos criar uma cena simples para demonstrar o uso do nó Timer e do SceneTreeTimer.
Exemplo 1: Usando o Nó Timer para Eventos Recorrentes
- Crie uma nova cena 2D (Node2D).
- Adicione um nó
Timercomo filho do Node2D e renomeie-o paraMeuTimerRecorrente. - No Inspetor, defina:
Wait Time:1.0(1 segundo)One Shot:false(para que ele repita)Autostart:true(para que ele comece automaticamente)
- Anexe um script ao seu Node2D principal (ou ao próprio
MeuTimerRecorrente).
# Arquivo: main_scene.gd
extends Node2D
@onready var meu_timer_recorrente: Timer = $MeuTimerRecorrente
var contador: int = 0
func _ready() -> void:
# Conecta o sinal 'timeout' do timer à função '_on_meu_timer_recorrente_timeout'
meu_timer_recorrente.timeout.connect(_on_meu_timer_recorrente_timeout)
# Se 'Autostart' fosse false, usaríamos meu_timer_recorrente.start() aqui
print("Timer recorrente iniciado. Olhe o console a cada segundo!")
func _on_meu_timer_recorrente_timeout() -> void:
contador += 1
print("Tick! O timer recorrente disparou! Contagem: ", contador)
if contador >= 5:
print("Parando o timer recorrente após 5 ticks.")
meu_timer_recorrente.stop()Ao executar esta cena, você verá "Tick! O timer recorrente disparou!" no console a cada segundo, até atingir 5 ticks.
Exemplo 2: Usando SceneTreeTimer para um Atraso Simples
Vamos adicionar um botão à nossa cena que, ao ser clicado, exibe uma mensagem após um pequeno atraso, usando SceneTreeTimer.
- Na mesma cena 2D, adicione um nó
Buttoncomo filho. - Renomeie-o para
BotaoAtraso. - No Inspetor do
BotaoAtraso, definaTextcomo "Clique para Atraso". - Conecte o sinal
pressed()doBotaoAtrasoao script do Node2D.
# Arquivo: main_scene.gd (continuando o script anterior)
extends Node2D
@onready var meu_timer_recorrente: Timer = $MeuTimerRecorrente
@onready var botao_atraso: Button = $BotaoAtraso
var contador: int = 0
func _ready() -> void:
meu_timer_recorrente.timeout.connect(_on_meu_timer_recorrente_timeout)
print("Timer recorrente iniciado. Olhe o console a cada segundo!")
print("Botão de atraso pronto.")
func _on_meu_timer_recorrente_timeout() -> void:
contador += 1
print("Tick! O timer recorrente disparou! Contagem: ", contador)
if contador >= 5:
print("Parando o timer recorrente após 5 ticks.")
meu_timer_recorrente.stop()
func _on_botao_atraso_pressed() -> void:
print("Botão clicado! Esperando 2 segundos...")
# Cria um timer de cena que espera 2 segundos
await get_tree().create_timer(2.0).timeout
print("Atraso de 2 segundos terminado! Mensagem após o clique.")Ao clicar no botão, você verá a primeira mensagem imediatamente e, após 2 segundos, a segunda mensagem aparecerá no console. A beleza do await é que ele pausa a execução apenas desta função, não o jogo inteiro.
Exemplo 3: Cooldown de Habilidade com Timer
Vamos simular uma habilidade de jogador que tem um tempo de recarga.
- Adicione outro
Buttonà sua cena, renomeie-o paraBotaoHabilidade. - Defina
Textcomo "Usar Habilidade". - Adicione um nó
Timercomo filho do Node2D, renomeie-o paraTimerCooldownHabilidade. - No Inspetor do
TimerCooldownHabilidade, defina:Wait Time:3.0(3 segundos de cooldown)One Shot:true(o cooldown acontece apenas uma vez por uso)Autostart:false
- Conecte o sinal
pressed()doBotaoHabilidadee o sinaltimeout()doTimerCooldownHabilidadeao script do Node2D.
# Arquivo: main_scene.gd (continuando o script)
extends Node2D
@onready var meu_timer_recorrente: Timer = $MeuTimerRecorrente
@onready var botao_atraso: Button = $BotaoAtraso
@onready var botao_habilidade: Button = $BotaoHabilidade
@onready var timer_cooldown_habilidade: Timer = $TimerCooldownHabilidade
var contador: int = 0
var habilidade_pronta: bool = true
func _ready() -> void:
meu_timer_recorrente.timeout.connect(_on_meu_timer_recorrente_timeout)
timer_cooldown_habilidade.timeout.connect(_on_timer_cooldown_habilidade_timeout)
print("Timer recorrente iniciado. Olhe o console a cada segundo!")
print("Botão de atraso pronto.")
print("Habilidade pronta para uso.")
func _on_meu_timer_recorrente_timeout() -> void:
contador += 1
print("Tick! O timer recorrente disparou! Contagem: ", contador)
if contador >= 5:
print("Parando o timer recorrente após 5 ticks.")
meu_timer_recorrente.stop()
func _on_botao_atraso_pressed() -> void:
print("Botão clicado! Esperando 2 segundos...")
await get_tree().create_timer(2.0).timeout
print("Atraso de 2 segundos terminado! Mensagem após o clique.")
func _on_botao_habilidade_pressed() -> void:
if habilidade_pronta:
print("Habilidade usada! Entrando em cooldown...")
habilidade_pronta = false
botao_habilidade.disabled = true # Desabilita o botão durante o cooldown
timer_cooldown_habilidade.start() # Inicia o timer de cooldown
else:
print("Habilidade em cooldown! Tempo restante: %.1f segundos" % timer_cooldown_habilidade.time_left)
func _on_timer_cooldown_habilidade_timeout() -> void:
print("Cooldown da habilidade terminado! Habilidade pronta novamente.")
habilidade_pronta = true
botao_habilidade.disabled = false # Habilita o botão novamenteNeste exemplo, o botão "Usar Habilidade" só pode ser clicado novamente após o TimerCooldownHabilidade ter terminado sua contagem regressiva de 3 segundos. Enquanto está em cooldown, o botão fica desabilitado e uma mensagem informa o tempo restante.
4. Integração com Múltiplas Tecnologias (N/A)
Este módulo foca exclusivamente no GDScript e nos recursos nativos do Godot Engine para gerenciamento de tempo. Não há integração com tecnologias externas neste tópico específico, pois o Godot oferece todas as ferramentas necessárias para isso de forma interna e otimizada.
5. Exercícios/Desafios 🚀
Agora é a sua vez de praticar! Crie uma nova cena e tente implementar os seguintes desafios:
Desafio 1: Inimigo Atirador Periódico 👾🔫
Crie um inimigo simples (pode ser um CharacterBody2D ou Node2D com uma Sprite2D) que atira projéteis a cada 2.5 segundos.
- Crie uma nova cena para o inimigo.
- Adicione um nó
Timerao inimigo. - Configure o
Timerpara disparar a cada 2.5 segundos (wait_time = 2.5,one_shot = false,autostart = true). - Conecte o sinal
timeoutdoTimera uma função no script do inimigo. - Dentro dessa função, imprima uma mensagem como "Inimigo atirou!" ou instancie um projétil (se você já tiver um).
Desafio 2: Ciclo Dia/Noite Simples ☀️🌙
Implemente um ciclo dia/noite básico que muda a cor de fundo da tela (ou um ColorRect) a cada 5 segundos, alternando entre uma cor clara (dia) e uma cor escura (noite).
- Crie uma nova cena principal.
- Adicione um nó
ColorRectcomo filho do nó raiz para representar o céu. Certifique-se de que ele cubra a tela inteira. - Adicione um nó
Timerà cena. - Configure o
Timerpara disparar a cada 5 segundos (wait_time = 5.0,one_shot = false,autostart = true). - No script da cena principal, mantenha uma variável booleana
e_dia(inicialmentetrue). - Conecte o sinal
timeoutdoTimera uma função. - Dentro da função:
- Inverta o valor de
e_dia. - Se
e_diafortrue, mude a cor doColorRectpara uma cor clara (ex:Color.SKY_BLUE). - Se
e_diaforfalse, mude a cor doColorRectpara uma cor escura (ex:Color.DARK_BLUE).
- Inverta o valor de
Desafio 3: Power-up de Duração Limitada 🍄✨
Crie um power-up que, ao ser "coletado" (simulado por um clique de botão), ativa um efeito por 7 segundos e depois o desativa automaticamente.
- Crie uma nova cena principal.
- Adicione um
Buttoncom o texto "Coletar Power-up". - Adicione um
Labelpara exibir o status do power-up (ex: "Power-up: Inativo" ou "Power-up: Ativo!"). - Adicione um nó
Timerà cena, renomeie-o paraTimerPowerUp. - Configure o
TimerPowerUppara terwait_time = 7.0,one_shot = true,autostart = false. - No script da cena principal:
- Crie uma variável booleana
power_up_ativo(inicialmentefalse). - Conecte o sinal
pressed()do botão a uma função. - Conecte o sinal
timeout()doTimerPowerUpa outra função.
- Crie uma variável booleana
- Na função
_on_button_pressed():- Se
power_up_ativoforfalse:- Defina
power_up_ativo = true. - Atualize o texto do
Labelpara "Power-up: Ativo!". - Inicie o
TimerPowerUp. - Desabilite o botão para evitar múltiplos cliques enquanto o power-up está ativo.
- Defina
- Se
power_up_ativofortrue, imprima uma mensagem como "Power-up já está ativo!".
- Se
- Na função
_on_timer_power_up_timeout():- Defina
power_up_ativo = false. - Atualize o texto do
Labelpara "Power-up: Inativo". - Habilite o botão novamente.
- Defina
6. Resumo e Próximos Passos
Nesta aula, você aprendeu sobre a importância do gerenciamento de tempo em jogos e como utilizar as ferramentas de temporização do Godot:
- Nó
Timer: Ideal para eventos recorrentes, cooldowns e atrasos controlados, com propriedades configuráveis no Inspetor. SceneTreeTimer: Perfeito para atrasos de uso único e pausas assíncronas no código usandoawait.
Dominar o uso de temporizadores abre um leque enorme de possibilidades para a criação de mecânicas de jogo complexas e dinâmicas.
Próximos Passos:
- Experimente combinar temporizadores com animações e efeitos visuais.
- Explore como os temporizadores podem ser pausados e retomados em um menu de pausa do jogo.
- Pense em como você pode usar temporizadores para gerenciar a vida útil de projéteis ou partículas.
Continue praticando e aplicando esses conceitos em seus próprios projetos! 🎮✨