Work in progress

The content of this page was not yet updated for Godot 4.2 and may be outdated. If you know how to improve this page or you can confirm that it's up to date, feel free to open a pull request.

Pontuação e repetição

Nesta parte, adicionaremos a pontuação, a reprodução de música e a capacidade de reiniciar o jogo.

Temos que acompanhar a pontuação atual em uma variável e exibi-la na tela usando uma interface mínima. Para isso, usaremos uma text label.

Na cena principal, adicione um novo nó Control como filho de Main e nomeie-o como UserInterface. Você será levado automaticamente para a tela 2D, onde poderá editar sua Interface de Usuário (UI em inglês).

Adicione um Label nó e nomei-o ScoreLabel

imagem1

No Inspetor, defina o Texto do Label como um substituto como "Pontuação: 0".

imagem2

Além disso, o texto é branco por padrão, como o fundo do nosso jogo. Precisamos mudar sua cor para vê-lo durante a execução.

Role para baixo para Theme Overrides, e expanda Colors e habilite Font Color para colorir o texto em preto (o que contrasta bem com a cena 3D branca)

imagem3

Finalmente, clique e arraste o texto na janela de exibição para afastá-lo do canto superior esquerdo.

imagem4

O nó UserInterface nos permite agrupar nossa UI em um ramo da árvore da cena e utilizar um recurso temático que se propagará a todos os seus filhos. Vamos usá-lo para definir a fonte do nosso jogo.

Criando um tema de interface do usuário

Mais uma vez, selecione o nó UserInterface. No Inspetor, crie um novo recurso temático em Tema -> Tema.

imagem5

Clique sobre ele para abrir o editor de temas No painel inferior. Ele lhe dá uma prévia de como todos os widgets UI embutidos irão ficar com seu recurso temático.

|imagem 6|

Por padrão, um tema tem apenas uma propriedade, a Fonte Padrão.

Ver também

Você pode adicionar mais propriedades ao recurso temático para projetar interfaces de usuário complexas, mas isso está além do escopo desta série. Para saber mais sobre a criação e edição de temas, veja Introdução ao skinning GUI.

Este espera um arquivo de fonte como os que você tem em seu computador. Dois formatos de arquivo de fonte comuns são TrueType Font (TTF) e OpenType Font (OTF).

No painel Sistema de Arquivos, expanda o diretório fonts e clique e arraste o arquivo Montserrat-Medium.ttf incluído no projeto para Default Font. O texto reaparecerá na visualização do tema.

O texto é um pouco pequeno. Configure o Default Font Size para 22 pixels para aumentar o tamanho do texto.

image7

Acompanhando a pontuação

Vamos trabalhar a seguir na pontuação. Anexe um novo script ao ScoreLabel e defina a variável score.

extends Label

var score = 0

A pontuação deve aumentar em 1 cada vez que esmagarmos um monstro. Podemos utilizar o sinal squashed para saber quando isso acontece. Entretanto, porque instanciamos monstros pelo código, não podemos conectar o sinal do inimigo ScoreLabel através do editor.

Ao invés disso, temos que fazer a conexão a partir do código toda vez que geramos um monstro.

Abra o script main.gd. Se ainda estiver aberto, você pode clicar em seu nome na coluna da esquerda do editor de scripts.

image8

Alternativamente, você pode clicar duas vezes no arquivo main.gd no painel Sistema de Arquivos.

Na parte inferior da função _on_mob_timer_timeout(), adicione a seguinte linha.

func _on_mob_timer_timeout():
    #...
    # We connect the mob to the score label to update the score upon squashing one.
    mob.squashed.connect($UserInterface/ScoreLabel._on_mob_squashed.bind())

Esta linha significa que quando o inimigo emitir o sinal squashed, o nó ScoreLabel o receberá e chamará a função _on_mob_squashed().

Volte para o script``ScoreLabel.gd`` para definir a função de chamada de retorno _on_mob_squashed().

Aí, incrementamos a pontuação e atualizamos o texto exibido.

func _on_mob_squashed():
    score += 1
    text = "Score: %s" % score

The second line uses the value of the score variable to replace the placeholder %s. When using this feature, Godot automatically converts values to string text, which is convenient when outputting text in labels or when using the print() function.

Ver também

Você pode aprender mais sobre a formatação de string aqui: Formatação de Strings em GDScript. Em C#, considere usar a interpolação com "$" <https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated>`_.

Agora você pode jogar e esmagar alguns inimigos para ver a pontuação aumentar.

image9

Nota

Em um jogo complexo, você pode querer separar completamente sua interface de usuário do mundo do jogo. Nesse caso, você não manteria o registro da pontuação no rótulo. Em vez disso, você pode querer armazená-la em um objeto separado e dedicado. Mas quando estiver fazendo um protótipo ou quando seu projeto é simples, é bom manter seu código simples. A programação é sempre um ato de equilíbrio.

Tentando novamente o jogo

Agora vamos acrescentar a capacidade de jogar novamente após a morte. Quando o jogador morrer, exibiremos uma mensagem na tela e aguardaremos a entrada.

Volte para a cena main.tscn, selecione o nó UserInterface, adicione um nó ColorRect como filho dele e nomeie-o como Retry. Esse nó preenche um retângulo com uma cor uniforme e servirá como uma sobreposição para escurecer a tela.

Para que se estenda por toda janela de exibição, você pode usar o menu Anchor Preset na barra de ferramentas.

image10

Abra e aplique o comando Full Rect.

imagem11

Nada acontece. Bem, quase nada; apenas os quatro pinos verdes se movem para os cantos da caixa de seleção.

imagem12

Isto porque os nós da IU (todos os que têm um ícone verde) trabalham com âncoras e margens relativas à caixa de delimitação de seus pais. Aqui, o nó UserInterface tem um tamanho pequeno e o Retry é limitado por ele.

Selecione o UserInterface e aplique Anchor Preset -> Full Rect a ele também. O nó Retry deve agora abranger toda janela de exibição.

Vamos mudar sua cor para que escureça a área de jogo. Selecione Retry e no Inspetor, defina sua Cor para algo mais escuro e transparente. Para isso, no seletor de cores, arraste a barra deslizante A para a esquerda. Ela controla o canal alfa da cor, ou seja, sua opacidade.

imagem13

Em seguida, adicione um Label como um filho de Retry e coloque o Texto "Press Enter to retry." Para movê-lo e ancorar no centro da tela, aplique Anchor Preset -> Center a ele.

imagem14

Programando a opção de nova tentativa

Agora podemos ir para o código para mostrar e esconder o nó Retry quando o jogador morre e joga novamente.

Abra o script main.gd. Primeiro, queremos ocultar a sobreposição no início do jogo. Adicione esta linha à função _ready().

func _ready():
    $UserInterface/Retry.hide()

Então, quando o jogador é atingido, mostramos a sobreposição.

func _on_player_hit():
    #...
    $UserInterface/Retry.show()

Finalmente, quando o nó Retry estiver visível, precisamos ouvir a entrada do jogador e reiniciar o jogo se ele pressionar Enter. Para fazer isso, usamos a chamada de retorno interna _unhandled_input().

Se o jogador pressionar a ação de entrada predefinida ui_accept e Retry estiver visível, nós recarregamos a cena atual.

func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        # This restarts the current scene.
        get_tree().reload_current_scene()

A função get_tree() nos dá acesso ao objeto global SceneTree, que nos permite recarregar e reiniciar a cena atual.

Adicionando música

Para adicionar música que toca continuamente ao fundo, vamos utilizar outra característica no Godot: autoloads <doc_singletons_autoload>`.

Para reproduzir áudio, tudo o que você precisa fazer é adicionar um nó AudioStreamPlayer à sua cena e anexar um arquivo de áudio a ele. Quando você inicia a cena, ela pode ser reproduzida automaticamente. Entretanto, quando você recarrega a cena, como fazemos para tocar novamente, os nós de áudio também são reinicializados, e a música começa de novo desde o início.

Você pode usar o recurso de carregamento automático para que Godot carregue um nó ou uma cena automaticamente no início do jogo, fora da cena atual. Você também pode usá-lo para criar objetos globalmente acessíveis.

Crie uma nova cena indo para o menu Scene e clicando Nova Cena ou usando o ícone + ao lado de sua cena aberta atualmente.

image15

Clique no botão Other Node para criar um AudioStreamPlayer e renomei-o para MusicPlayer.

image16

Incluímos uma trilha sonora House In a Forest Loop.ogg, no diretório art/. Clique e arraste-a para a propriedade Stream no diretório Inspetor. Além disso, ative Autoplay para que a música toque automaticamente no início do jogo.

image17

Salve a cena como MusicPlayer.tscn.

Temos que registrá-lo para carregar automaticamente. Vá para Projeto > Configurações do Projeto no menu e mude para a aba AutoLoad.

No campo Caminho, você deseja inserir o caminho para sua cena. Clique no ícone da pasta para abrir o navegador de arquivos e clique duas vezes em MusicPlayer.tscn. Em seguida, clique no botão Adicionar à direita para registrar o nó.

image18

MusicPlayer.tscn now loads into any scene you open or play. So if you run the game now, the music will play automatically in any scene.

Antes de encerrarmos esta lição, aqui vai uma rápida olhada em como funciona por debaixo dos panos. Quando você executa o jogo, seu painel Cena muda para lhe dar duas abas: Remota e Local.

image19

A aba Remoto permite que você visualize a árvore de nós do seu jogo em execução. Lá, você verá o nó Principal e tudo o que a cena contém e os inimigos instanciados na parte inferior.

image20

No topo estão o MusicPlayer carregado automaticamente e um nó raiz, que é a janela de visualização do seu jogo.

E isso é tudo para esta lição. Na próxima parte, adicionaremos uma animação para tornar o jogo muito mais bonito.

Aqui está o script completo main.gd para referência.

extends Node

@export var mob_scene: PackedScene

func _ready():
    $UserInterface/Retry.hide()


func _on_mob_timer_timeout():
    # Create a new instance of the Mob scene.
    var mob = mob_scene.instantiate()

    # Choose a random location on the SpawnPath.
    # We store the reference to the SpawnLocation node.
    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    # And give it a random offset.
    mob_spawn_location.progress_ratio = randf()

    var player_position = $Player.position
    mob.initialize(mob_spawn_location.position, player_position)

    # Spawn the mob by adding it to the Main scene.
    add_child(mob)

    # We connect the mob to the score label to update the score upon squashing one.
    mob.squashed.connect($UserInterface/ScoreLabel._on_mob_squashed.bind())

func _on_player_hit():
    $MobTimer.stop()
    $UserInterface/Retry.show()

func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        # This restarts the current scene.
        get_tree().reload_current_scene()