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).

Adicione um nó Label e renomeie-o para ScoreLabel.

imagem0

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

imagem1

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.

Vá até Substituição de Tema, e expanda Cores e clique na caixa preta ao lado de Cor da fonte para pintar o texto.

imagem2

Escolha um tom escuro para contrastar bem com a cena 3D.

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 IU 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.

Clique na propriedade Default Font e crie um novo DynamicFont.

image7

Expanda a DynamicFont clicando nela e expanda sua seção Font. Lá, você verá um campo vazio Font Data.

image8

Este espera um arquivo de fonte como os que você tem em seu computador. A DynamicFont suporta os seguintes formatos:

  • TrueType (.ttf)

  • OpenType (.otf)

  • Web Open Font Format 1 (.woff)

  • Web Open Font Format 2 (.woff2, desde o Godot 3.5)

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

O texto é um pouco pequeno. Configure o Configurações -> Tamanho para 22 pixels para aumentar o tamanho do texto.

image9

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, ao instanciarmos monstros a partir do código, não podemos fazer a conexão no 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 Principal.gd. Se ainda estiver aberto, você pode clicar em seu nome na coluna da esquerda do editor de scripts.

image10

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

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

func _on_MobTimer_timeout():
    #...
    # We connect the mob to the score label to update the score upon squashing one.
    mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")

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

A segunda linha utiliza o valor da variável score para substituir o substituto %s. Ao utilizar este recurso, o Godot converte automaticamente os valores em texto, o que é conveniente para a saída de texto em etiquetas ou utilizando a função print().

Ver também

Você pode aprender mais sobre a formatação de strings aqui: Formatação de Strings em GDScript.

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

imagem11

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 Principal, 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 Layout na barra de ferramentas.

imagem12

Abra e aplique o comando Full Rect.

imagem13

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

imagem14

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 Layout -> 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.

image15

Em seguida, adicione um Label como filho de Retry e dê a ele o Text "Pressione Enter para tentar novamente."

image16

Para movê-lo e ancorá-lo no centro da tela, aplique Layout -> Center a ele.

image17

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 Principal.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 acessando o menu Cena e clicando em Nova Cena.

image18

Clique no botão Outro nó para criar um AudioStreamPlayer e renomeá-lo para MusicPlayer.

image19

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.

image20

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ó.

image21

Se você executar o jogo agora, a música tocará automaticamente. E mesmo quando você perde e tenta novamente, ela continua.

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.

image22

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.

image23

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 ''Principal.gd'' para referência.

extends Node

export (PackedScene) var mob_scene


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


func _unhandled_input(event):
    if event.is_action_pressed("ui_accept") and $UserInterface/Retry.visible:
        get_tree().reload_current_scene()


func _on_MobTimer_timeout():
    var mob = mob_scene.instance()

    var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
    mob_spawn_location.unit_offset = randf()

    var player_position = $Player.transform.origin
    mob.initialize(mob_spawn_location.translation, player_position)

    add_child(mob)
    mob.connect("squashed", $UserInterface/ScoreLabel, "_on_Mob_squashed")


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