projeto

Implementando a Lógica de Jogo: Pontuação e Game Over

Aprenda sobre implementando a lógica de jogo: pontuação e game over

60 min
Aula 5 de 5

Implementando a Lógica de Jogo: Pontuação e Game Over

Olá, futuro desenvolvedor de games! 👋 Nesta aula crucial do nosso projeto, vamos mergulhar na implementação de dois pilares fundamentais para a interatividade e rejogabilidade de qualquer game: o sistema de pontuação e a lógica de Game Over.

Um jogo sem pontuação é como um placar em branco – não há como medir o progresso ou a maestria do jogador. E sem um Game Over, não há desafio, risco ou a satisfação de superar uma adversidade. Prepare-se para dar vida e significado às ações do seu jogador! 🚀

🎯 Introdução

Ao final desta aula, você será capaz de:

  • Criar um sistema de pontuação dinâmico que reage às ações do jogador.
  • Exibir a pontuação de forma clara na interface do usuário (HUD).
  • Implementar condições para acionar o estado de "Game Over".
  • Desenvolver uma tela de Game Over com opções para o jogador (ex: reiniciar).
  • Utilizar um Singleton para gerenciar o estado global do jogo, seguindo as melhores práticas da Godot Engine.

Vamos construir um jogo que não apenas funciona, mas que também engaja e desafia!

🎮 Pontuação: Acompanhando Seu Progresso

A pontuação é um feedback direto para o jogador, indicando o quão bem ele está se saindo. Ela pode ser incrementada ao coletar itens, derrotar inimigos, completar objetivos ou evitar obstáculos.

📝 Conceito

Para gerenciar a pontuação de forma eficaz em Godot, especialmente em um jogo com múltiplas cenas e elementos que precisam interagir com ela, a melhor prática é usar um Singleton (ou AutoLoad). Um Singleton é um script que é carregado automaticamente no início do jogo e permanece acessível de qualquer lugar, tornando-o perfeito para gerenciar estados globais como pontuação, saúde do jogador ou configurações do jogo.

🛠️ Implementação

Vamos criar um Singleton chamado GameManager para cuidar da nossa pontuação e do estado geral do jogo.

Passo 1: Criando um Singleton para o Gerenciamento do Jogo (GameManager)

  1. Crie um novo script GDScript na pasta res://Scripts/ (ou onde você organiza seus scripts) e nomeie-o GameManager.gd.

  2. Adicione o seguinte código:

    # GameManager.gd
    extends Node
     
    signal score_updated(new_score: int)
    signal game_over_triggered()
     
    var current_score: int = 0
    var game_is_over: bool = false
     
    func _ready():
        # Certifica-se de que o jogo não está pausado e o estado inicial está correto
        get_tree().paused = false
        current_score = 0
        game_is_over = false
     
    func add_score(amount: int):
        if not game_is_over:
            current_score += amount
            emit_score_updated()
            print("Pontuação atual: ", current_score) # Para debug
     
    func reset_score():
        current_score = 0
        emit_score_updated()
     
    func emit_score_updated():
        emit_signal("score_updated", current_score)
     
    func trigger_game_over():
        if not game_is_over:
            game_is_over = true
            get_tree().paused = true # Pausa o jogo
            emit_signal("game_over_triggered")
            print("GAME OVER!")
     
    func restart_game():
        get_tree().paused = false
        current_score = 0
        game_is_over = false
        # Recarrega a cena atual para reiniciar o jogo
        get_tree().reload_current_scene()
        # Ou, se você tiver uma cena de menu principal:
        # get_tree().change_scene_to_file("res://Scenes/MainMenu.tscn")
  3. Configure como AutoLoad (Singleton):

    • Vá em Projeto -> Configurações do Projeto...
    • Na aba AutoLoad, clique no botão de pasta (...) ao lado de Path.
    • Selecione o script GameManager.gd.
    • No campo Node Name, digite GameManager.
    • Clique em Adicionar.

    Agora, GameManager estará acessível globalmente em qualquer script usando GameManager.add_score(10) ou GameManager.current_score.

Passo 2: Exibindo a Pontuação na Tela (HUD)

Vamos criar uma interface simples para exibir a pontuação.

  1. Na sua cena principal do jogo (ex: World.tscn), adicione um nó CanvasLayer. Este nó garante que a UI seja renderizada sobre o restante do jogo, independentemente da câmera.

  2. Dentro do CanvasLayer, adicione um nó Label.

  3. Renomeie o Label para ScoreLabel.

  4. No Inspector do ScoreLabel, defina um Text inicial como "Pontos: 0". Ajuste a fonte, tamanho e cor conforme desejar. Posicione-o no canto superior da tela.

  5. Anexe um novo script ao CanvasLayer (ou diretamente ao ScoreLabel, se preferir) e nomeie-o HUD.gd.

    # HUD.gd
    extends CanvasLayer
     
    @onready var score_label: Label = %ScoreLabel # Usando o operador % para pegar o nó pelo nome
     
    func _ready():
        # Conecta o sinal do GameManager para atualizar a pontuação
        GameManager.score_updated.connect(self._on_score_updated)
        # Garante que a pontuação inicial seja exibida
        _on_score_updated(GameManager.current_score)
     
    func _on_score_updated(new_score: int):
        score_label.text = "Pontos: " + str(new_score)

    Nota sobre @onready var score_label: Label = %ScoreLabel: Este é um atalho conveniente no Godot 4 para obter nós filhos ou irmãos pelo nome. Certifique-se de que o ScoreLabel esteja dentro do CanvasLayer e que o nome corresponda.

Passo 3: Atualizando a Pontuação

Agora, precisamos que algo no jogo realmente adicione pontos. Vamos supor que você tenha um item coletável, como uma "Moeda".

  1. Abra o script do seu item coletável (ex: Coin.gd).

  2. No método que detecta a colisão com o jogador (provavelmente _on_body_entered ou _on_area_entered), adicione a chamada ao GameManager.

    # Coin.gd (Exemplo)
    extends Area2D
     
    @export var score_value: int = 10 # Valor de pontuação desta moeda
     
    func _on_Coin_body_entered(body: Node2D):
        if body.name == "Player": # Certifique-se de que é o jogador
            GameManager.add_score(score_value)
            queue_free() # Remove a moeda após ser coletada

    Certifique-se de que o sinal body_entered (para Area2D) ou _on_body_entered (para CharacterBody2D/RigidBody2D) esteja conectado corretamente no editor.

💡 Boas Práticas

  • Sinais para Comunicação: O uso de sinais (score_updated, game_over_triggered) no GameManager é uma excelente prática. Isso desacopla os componentes: o GameManager não precisa saber quem se importa com a pontuação; ele apenas avisa que ela mudou. O HUD e a tela de Game Over podem então "ouvir" esses sinais.
  • Separação de Preocupações: O GameManager gerencia o estado do jogo. O HUD exibe esse estado. O Coin interage com o GameManager para mudar o estado. Cada script tem uma responsabilidade clara.

💀 Game Over: O Fim do Jogo

O Game Over é o momento em que o jogador falha em seus objetivos e o jogo termina. É uma parte essencial do ciclo de gameplay que oferece desafio e incentiva o jogador a tentar novamente.

📝 Conceito

O Game Over pode ser acionado por várias condições:

  • Saúde do jogador chega a zero.
  • Tempo limite esgotado.
  • Cair de um abismo.
  • Ser atingido por um inimigo um certo número de vezes.

Quando o Game Over é acionado, geralmente queremos:

  1. Parar o jogo (pausar o SceneTree).
  2. Exibir uma tela de Game Over.
  3. Oferecer opções como "Tentar Novamente" ou "Voltar ao Menu Principal".

🛠️ Implementação

Vamos usar o nosso GameManager para orquestrar o Game Over e criar uma tela dedicada.

Passo 1: Definindo Condições de Game Over

Vamos supor que seu jogador tenha um script Player.gd com uma variável de saúde.

  1. No script do seu jogador (Player.gd), adicione uma variável de saúde e um método para sofrer dano.

    # Player.gd (Exemplo, adicione ao seu script existente)
    extends CharacterBody2D
     
    @export var max_health: int = 3
    var current_health: int = max_health:
        set(value):
            current_health = clampi(value, 0, max_health) # Garante que a saúde fique entre 0 e max_health
            if current_health <= 0:
                # Se a saúde chegar a zero, aciona o Game Over
                GameManager.trigger_game_over()
                # Opcional: esconde o jogador ou toca uma animação de morte
                hide()
                set_physics_process(false) # Para o movimento do jogador
                set_process_input(false) # Para de receber input
     
    func take_damage(amount: int):
        if not GameManager.game_is_over:
            current_health -= amount
            print("Saúde do jogador: ", current_health)
            # Opcional: emitir um sinal para atualizar a barra de vida no HUD
  2. Em um script de inimigo ou armadilha (ex: Enemy.gd), chame o método take_damage do jogador quando houver uma colisão.

    # Enemy.gd (Exemplo)
    extends CharacterBody2D
     
    @export var damage_amount: int = 1
     
    func _on_body_entered(body: Node2D):
        if body.name == "Player": # Ou verifique o grupo do jogador
            var player = body as Player
            if player:
                player.take_damage(damage_amount)

Passo 2: Criando a Tela de Game Over

Vamos criar uma cena separada para a tela de Game Over, que será instanciada e exibida quando o jogo terminar.

  1. Crie uma nova cena (Scene -> New Scene).

  2. Adicione um nó CanvasLayer como raiz (para que fique sobre o jogo).

  3. Dentro do CanvasLayer, adicione um nó Control (pode ser um Panel para um fundo).

    • Ajuste o Layout do Control para Full Rect para que ele ocupe a tela inteira.
    • Mude a cor de fundo do Panel para algo escuro e semi-transparente.
  4. Dentro do Control/Panel, adicione:

    • Um Label com o texto "GAME OVER". Centralize-o e ajuste a fonte.
    • Outro Label para exibir a pontuação final (ex: "Sua Pontuação: 0").
    • Um Button com o texto "Tentar Novamente".
    • (Opcional) Outro Button com o texto "Menu Principal".
  5. Salve a cena como GameOverScreen.tscn em res://Scenes/UI/.

  6. Anexe um novo script ao CanvasLayer (ou ao Control raiz da cena de Game Over) e nomeie-o GameOverScreen.gd.

    # GameOverScreen.gd
    extends CanvasLayer
     
    @onready var game_over_label: Label = %GameOverLabel # Assumindo um Label nomeado GameOverLabel
    @onready var final_score_label: Label = %FinalScoreLabel # Assumindo um Label nomeado FinalScoreLabel
    @onready var retry_button: Button = %RetryButton
     
    func _ready():
        # Esconde a tela de Game Over por padrão
        hide()
        # Conecta o sinal do GameManager para mostrar a tela quando o jogo terminar
        GameManager.game_over_triggered.connect(self._on_game_over_triggered)
        # Conecta o botão de tentar novamente
        retry_button.pressed.connect(self._on_retry_button_pressed)
     
    func _on_game_over_triggered():
        show() # Mostra a tela de Game Over
        final_score_label.text = "Sua Pontuação: " + str(GameManager.current_score)
        # Opcional: Desabilitar input do jogador se ainda não foi feito
        # get_tree().get_first_node_in_group("player").set_process_input(false)
     
    func _on_retry_button_pressed():
        # Reinicia o jogo através do GameManager
        GameManager.restart_game()
        hide() # Esconde a tela de Game Over

    Lembre-se de conectar os sinais pressed() dos botões no editor!

Passo 3: Gerenciando o Estado do Jogo com o GameManager

Nosso GameManager já tem os métodos trigger_game_over() e restart_game(). Agora, precisamos instanciar a cena GameOverScreen.tscn quando o jogo começar, mas mantê-la oculta até que seja necessária.

  1. Abra a sua cena principal do jogo (ex: World.tscn).

  2. Instancie a cena GameOverScreen.tscn como filha da sua cena principal. Ela deve estar oculta por padrão (o script _ready dela faz isso).

    # GameManager.gd (Revisado, se necessário, mas já temos o essencial)
    # ...
    func trigger_game_over():
        if not game_is_over:
            game_is_over = true
            get_tree().paused = true # Pausa o jogo
            emit_signal("game_over_triggered") # Este sinal será capturado pela GameOverScreen
            print("GAME OVER!")
     
    func restart_game():
        get_tree().paused = false
        current_score = 0
        game_is_over = false
        get_tree().reload_current_scene() # Recarrega a cena atual

Com isso, quando GameManager.trigger_game_over() for chamado (ex: pelo jogador ao perder toda a vida), o GameManager pausará o jogo e emitirá o sinal. A GameOverScreen capturará esse sinal, se mostrará e exibirá a pontuação final. Quando o botão "Tentar Novamente" for pressionado, o GameManager.restart_game() será chamado, reiniciando a cena e resetando o estado do jogo.

🔗 Integração Completa: Unindo as Peças

Aqui está um diagrama simplificado de como os componentes interagem:

graph TD
    A[GameManager (Singleton)] --> B(Player.gd):::player_node
    A --> C(HUD.gd):::ui_node
    A --> D(GameOverScreen.gd):::ui_node
    E[Coin.gd] --> A
    F[Enemy.gd] --> B
 
    B -- trigger_game_over() --> A
    A -- game_over_triggered() --> D
    A -- score_updated() --> C
    D -- restart_game() --> A
    click A "https://docs.godotengine.org/en/stable/getting_started/step_by_step/singletons_autoload.html" "Documentação Godot: Singletons"
    click B "https://docs.godotengine.org/en/stable/tutorials/2d/platformer/index.html" "Documentação Godot: Platformer"
    click C "https://docs.godotengine.org/en/stable/tutorials/2d/your_first_2d_game.html#ui-hud" "Documentação Godot: HUD"
 
    classDef player_node fill:#90EE90,stroke:#333,stroke-width:2px;
    classDef ui_node fill:#ADD8E6,stroke:#333,stroke-width:2px;
  1. GameManager (Singleton): O coração da lógica global. Ele mantém a pontuação (current_score), o estado de Game Over (game_is_over), e métodos para manipular esses estados (add_score, trigger_game_over, restart_game). Ele emite sinais (score_updated, game_over_triggered) para notificar outros componentes sobre mudanças de estado.
  2. Player.gd: Contém a lógica de saúde do jogador. Quando a saúde chega a zero, ele chama GameManager.trigger_game_over().
  3. Coin.gd (ou outros coletáveis): Quando o jogador colide com um item coletável, ele chama GameManager.add_score() para aumentar a pontuação.
  4. Enemy.gd (ou outros perigos): Quando o jogador colide com um inimigo, ele chama um método no Player para causar dano, que por sua vez pode levar ao Game Over.
  5. HUD.gd: Escuta o sinal score_updated do GameManager e atualiza o Label da pontuação na tela.
  6. GameOverScreen.gd: Escuta o sinal game_over_triggered do GameManager. Quando acionado, ele se torna visível, exibe a pontuação final e oferece um botão "Tentar Novamente" que chama GameManager.restart_game().

Esta arquitetura garante um código modular, fácil de manter e expandir.

🚀 Desafios Práticos

Agora é a sua vez de colocar a mão na massa e solidificar seu aprendizado!

  • Integre a Pontuação: Certifique-se de que todos os itens coletáveis do seu jogo (moedas, power-ups, etc.) adicionem pontos ao GameManager quando coletados pelo jogador.
  • Implemente o Game Over por Queda: Se o seu jogo tem plataformas, faça com que o jogador receba Game Over se cair para fora do mapa (ex: em um Area2D no fundo da cena).
  • Exiba a Pontuação Final: Garanta que a tela de Game Over mostre a pontuação que o jogador alcançou antes de perder.
  • Botão "Menu Principal": Adicione um segundo botão na tela de Game Over para "Menu Principal" e faça-o carregar uma cena de menu principal (mesmo que seja uma cena vazia por enquanto).
  • Sons para Feedback:
    • Adicione um som curto e satisfatório quando o jogador coleta um item e ganha pontos.
    • Adicione um som dramático ou de falha quando o Game Over é acionado. Dica: Use um nó AudioStreamPlayer2D para sons no mundo ou AudioStreamPlayer para sons da UI.
  • Animação de Game Over: Faça a tela de Game Over aparecer com uma transição suave (ex: fade-in) ou uma animação simples usando Tween ou AnimationPlayer.

📚 Resumo e Próximos Passos

Parabéns! 🎉 Você deu um grande passo para transformar seu protótipo em um jogo completo e envolvente. Aprendemos a:

  • Utilizar Singletons (GameManager) para gerenciar o estado global do jogo.
  • Implementar um sistema de pontuação com feedback visual na HUD.
  • Definir condições para o Game Over.
  • Criar uma tela de Game Over interativa.
  • Empregar sinais para uma comunicação eficiente e desacoplada entre os nós.

A lógica de pontuação e Game Over é a espinha dorsal da experiência do jogador. Com ela, seu jogo ganha um propósito e um ciclo de vida.

Nos próximos módulos, vamos explorar como adicionar mais complexidade e polimento ao seu jogo, como:

  • Salvamento de Dados: Guardar pontuações altas ou progresso do jogador.
  • Menus e Transições: Criar um menu principal, telas de pausa e transições suaves entre cenas.
  • Mais Elementos de Gameplay: Inimigos mais complexos, power-ups variados e muito mais!

Continue praticando e experimentando. O desenvolvimento de jogos é uma jornada de aprendizado contínuo! Até a próxima aula! 🚀🎮

© 2025 Escola All Dev. Todos os direitos reservados.

Implementando a Lógica de Jogo: Pontuação e Game Over - Fundamentos do Godot: Desenvolvimento de games para iniciantes | escola.all.dev.br