GameDev

Aprendendo a programar jogos em Unity: desenvolvendo o cronômetro e o contador de itens

Com base no controlador de fase que codificamos, iremos criar os contadores de tempo e de presentes de nossa primeira fase.

em 02/12/2023
Seja bem-vindo(a) ao GameDev: Aprendendo a programar jogos em Unity de hoje! Continuando o empolgante processo de criação de nosso primeiro platformer 2D, iremos inserir na interface de nossa primeira fase elementos gráficos que irão possibilitar ao jogador saber quanto tempo resta e quantos itens faltam ser coletados.

Caso esta seja a primeira vez que você acessa esta série, sinta-se à vontade para juntar-se a nós nesta trilha de aprendizado. A partir do primeiro tópico, você acompanhará todo o processo de criação de um game, desde a instalação da ferramenta Unity em seu computador até a elaboração das regras do jogo, dos itens de mídia, seus códigos e acabamentos.

Nossa série é elaborada de forma a permitir, por meio de exemplos e projetos, que você possa criar “do zero” os games que sempre sonhou em tirar do papel. No momento, estamos desenvolvendo o segundo jogo da série, um platformer de nome Motorista da Pesada. Observe como está ficando nosso jogo: 


No tópico anterior de nossa jornada, iniciamos a programação dos scripts controladores de fases e partidas do game, que serão muito importantes, pois permitirão que as regras e desafios do jogo sejam implementados corretamente. Venha conosco e vamos juntos nesta jornada rumo a novos conhecimentos!

De volta ao Canvas

A criação dos controladores de fases e partidas, no último tópico, irá permitir que, hoje, possamos começar a codificar elementos que mostrarão ao jogador “em que pé” está sua aventura até o momento. Para isso, vamos voltar a utilizar elementos gráficos dos tipos Text e Image, que serão inseridos no Canvas da fase, para montarmos a UI (User Interface, ou Interface de Usuário) de nosso jogo.

Para começar esse processo, vamos abrir o projeto Motorista da Pesada no editor. Abra o Unity Hub e clique duas vezes sobre o item referente ao projeto. Na interface inicial do Unity, na aba Project, abra a pasta Assets, Scenes e, por fim, clique duas vezes no ícone da cena Fase01.

Na aba Hierarchy, selecione o GameObject CanvasFase, que já estará com um componente do tipo Canvas devidamente configurado em sua aba Inspector. Vamos criar um novo GameObject subordinado a ele, configurado com um componente do tipo Text; para isso, clique com o botão direito em CanvasFase e, no menu suspenso, selecione UI e, em seguida, Text, conforme imagem ilustrativa a seguir:

O nome do novo GameObject será “Cronometro”, sem as aspas, conforme exemplo abaixo:

Selecione o objeto Cronometro e, na aba Inspector, iremos alterar alguns parâmetros de seu componente Rect Transform para que o objeto fique posicionado no canto superior esquerdo da tela.

Altere os seguintes parâmetros de Rect Transform:
  • Âncora (ícone destacado em vermelho): Top Left;
  • Pivot X = 0, Y = 1;
  • Pos X = 0, Pos Y = 0, Pos Z = 0;
  • Width = 200; e
  • Height = 100.

Alguns dos parâmetros que alteramos foram os referentes ao Pivot. No Unity, o Pivot de um componente Transform (ou de um Rect Transform) é o ponto de referência ao qual os parâmetros de posicionamento e rotação de um objeto obedecem, ao se aplicarem determinados valores.

No caso de objetos tridimensionais, as seguintes coordenadas são utilizadas por padrão, na hora de se determinar qual a posição do Pivot de um objeto:


Sendo assim, determinando que Cronometro tem em seu Rect Transform parâmetros de Pivot X = 0 e Y = 1, o ponto representado pela seta na imagem a seguir (o Pivot) ficará posicionado nas coordenadas “Top Left” (canto superior esquerdo da tela), com uma distância X = 0 e Y = 0 desse ponto.

Na prática, Cronometro ficará como desejamos, colado ao canto superior esquerdo da tela de jogo. Agora, ainda com o GameObject em questão selecionado, vamos realizar ajustes nos parâmetros de seu componente Text.

Preencha o campo Text com “00:00”, sem as aspas, e escolha Pacifico-Regular como sua fonte (campo Font). Deixe o parâmetro Best Fit selecionado (true) e ajuste os valores de Min Size, Max Size e Alignment conforme imagem a seguir:


O parâmetro Best Fit é o responsável por alterar dinamicamente o tamanho da letra, a partir do tamanho do GameObject que tem o elemento Text atrelado a si. No caso, o tamanho da fonte ficará entre 10 e 50, preenchendo todo o espaço disponível, desde que entre esses dois valores.

Calculando e exibindo o tempo

Agora que já temos um bonito cronômetro na tela, chegou a hora de codificá-lo, para que ele possa receber o valor do tempo corrente da fase, que já está sendo computado pelo script Partida.

Na aba Project, abra a pasta Assets e, em seguida, Scripts. Clique com o botão direito do mouse em um espaço vazio e, no menu suspenso, selecione a opção Create, C# Script. O nome de nosso novo script será “Temporizador”, sem as aspas, conforme exemplo a seguir:

Clique duas vezes sobre o ícone de Temporizador, para abri-lo no Visual Studio. Iremos utilizar apenas a função void Update(). Se desejar, pode apagar a declaração da função void Start().

Dentro dos colchetes de void Update(), insira o seguinte trecho de código:

        int minutos = Mathf.FloorToInt(Partida.TempoRestante / 60);
        int segundos = Mathf.FloorToInt(Partida.TempoRestante % 60);

        gameObject.GetComponent<Text>().text = minutos.ToString("00") + ":" + segundos.ToString("00");

Esse código será responsável por fazer o seguinte: a partir do valor da variável Partida.TempoRestante, é calculada a quantidade de minutos e de segundos restantes. Na primeira linha, a função Mathf.FloorToInt irá arredondar o valor de (Partida.TempoRestante / 60), que é um número decimal, para o maior número inteiro que seja menor ou igual a ele.

Vamos supor que a variável Partida.TempoRestante esteja armazenando o valor 150, representando um total de 150 segundos restantes da partida. Para descobrirmos quantos minutos nos resta, dividiremos 150 por 60 (1 minuto = 60 segundos), tendo como resultado o valor 2,5. Mathf.FloorToInt ”removerá” o 0,5 desse valor, portanto teremos 2 minutos restantes de partida. Mas e os segundos removidos?

A segunda linha de código realizará algo parecido com a primeira, mas, em vez de Mathf.FloorToInt receber como parâmetro (Partida.TempoRestante / 60), estará recebendo (Partida.TempoRestante % 60). Esse símbolo de percentagem (%) na linguagem C# representa o resto da divisão; ou seja, o valor ilustrado abaixo (destacado em vermelho) irá ser repassado à função Mathf.FloorToInt :

Dessa forma, teríamos 2 minutos e 30 segundos armazenados respectivamente nas variáveis minutos e segundos.

A última linha irá alterar o parâmetro Text (conteúdo textual) do componente Text do objeto em questão para uma concatenação entre os minutos restantes, o sinal de “:” e os segundos restantes; sendo que, como as variáveis minutos e segundos são de tipo numérico, e não do tipo String, passam pela função ToString("00") para serem “formatadas” em texto, apresentando, no mínimo, dois caracteres cada. 

Isso é necessário para que, em uma partida com 2 minutos e 30 segundos restantes, no cronômetro apareça 02:30 e não 2:30.

Agora, iremos salvar o script no Visual Studio e voltar ao Unity para adicioná-lo ao objeto Cronometro. Selecione o GameObject em questão e, na aba Inspector, por meio do botão Add Component, acrescente o componente Temporizador, conforme exemplo a seguir:

Vamos simular a execução do jogo, indo à aba Game e pressionando o botão Play. Note que, agora, temos uma indicação visual do tempo que nos resta na partida.

Interrompa a simulação de execução, pressionando novamente o botão Play e voltando à aba Scene.

Contando presentes

Da mesma forma que temos uma representação visual do tempo da partida, que tal criarmos um elemento para indicar quantos presentes faltam ser coletados?

Para isso, selecione o GameObject CanvasFase e, novamente, selecione UI no menu suspenso e, em seguida, Text, conforme exemplo abaixo:

O nome de nosso novo objeto será “ItensRestantes”, sem as aspas. Após inseri-lo, altere os seguintes parâmetros de seu componente Rect Transform:
  • Âncora (ícone destacado em vermelho): Top Center;
  • Pivot X = 0.5, Y = 1;
  • Pos X = 0, Pos Y = 0, Pos Z = 0;
  • Width = 350; e
  • Height = 100.
Diferentemente do primeiro objeto de texto que inserimos, ItensRestantes ficará posicionado na área central da parte superior da tela; por isso é importante fornecer o valor de Pivot X = 0.5 e Pivot Y = 1, deixando o “ponto de referência” do objeto posicionado como no exemplo em destaque a seguir:

Em relação ao componente Text, os parâmetros serão iguais aos adotados para o objeto Cronometro, com exceção de Max Size, que terá valor 45, e o campo Text (conteúdo textual), que deve ser preenchido com “Faltam X presentes”, sem as aspas. Verifique os parâmetros de referência pelos indicativos da imagem a seguir:


Ainda com o objeto ItensRestantes selecionado, vamos criar uma nova Tag para identificá-lo adequadamente. Na aba Inspector, clique na caixa de seleção de nome Tag (indicada em vermelho no exemplo a seguir), e selecione a opção "Add Tag...".

Clique no símbolo de mais (+), preencha o campo de texto com a expressão “ControleItens” (sem as aspas) e clique no botão Save.

Selecione novamente ItensRestantes, clique na caixa de seleção de nome Tag e conceda ao objeto a tag ControleItens.


Essa tag que criamos será utilizada pelos scripts ControladorFase e Itens, a fim de atualizar o que é apresentado ao jogador tanto no início da partida quanto a cada coleta de item realizada pelo cenário.

Vamos começar então pelas alterações a serem realizadas no script ControladorFase. Na aba Project, abra a pasta Assets, Scripts e, por fim, clique duas vezes sobre o ícone do script para abri-lo no Visual Studio.

Logo após a linha em que há o cálculo de quantos objetos restantes estão no cenário (Partida.ObjetosRestantes = GameObject.FindGame...), insira o seguinte trecho de código:

        GameObject.FindGameObjectWithTag("ControleItens").GetComponent<Text>().text =
            "Faltam " + Partida.ObjetosRestantes + " presentes";

Esse código irá trazer, dentre os GameObjects ativos da cena atual, o primeiro que tenha a Tag ControleItens atrelada a si. Após isso, irá alterar o texto do componente Text (do objeto com a tag atrelada) para refletir quantos objetos ainda precisam ser encontrados.

Salve o script e vamos voltar ao Unity para editar, agora, o script Itens. Clique duas vezes sobre seu ícone para abri-lo no Visual Studio.

Até o momento, a única ação que está sendo realizada ao se tocar em um presente é a destruição de seu GameObject, por meio da função Destroy(gameObject).

Antes de o GameObject se autodestruir, vamos pedir a ele para que faça algumas outras tarefas nobres, como atualizar o contador de objetos restantes e, também, o indicador na tela sobre essa informação.

Para isso, substitua a linha que contém Destroy(gameObject) por todo o trecho de código presente abaixo, incluindo as chaves:

            {
                Partida.ObjetosRestantes--;

                if (Partida.ObjetosRestantes > 1)
                    GameObject.FindGameObjectWithTag("ControleItens").GetComponent<Text>().text =
                    "Faltam " + Partida.ObjetosRestantes + " presentes";
                else if (Partida.ObjetosRestantes == 1)
                    GameObject.FindGameObjectWithTag("ControleItens").GetComponent<Text>().text =
                    "Falta 1 presente";
                else
                    GameObject.FindGameObjectWithTag("ControleItens").GetComponent<Text>().text =
                    "Você venceu!";

                Destroy(gameObject);
            }

Esse código é responsável pela realização de uma lógica bem interessante de atividades encadeadas:
  • Ao se identificar o toque no objeto do “tipo” Presente, é subtraída uma unidade do contador Partida.ObjetosRestantes;
  • Após a subtração, é verificada a quantidade de presentes que, de fato, sobrou na cena:
    • Caso reste mais de um presente em cena, o GameObject com a tag ControleItens receberá, em seu componente Text, a nova informação com a quantidade atualizada, respeitando o plural em “Faltam tantos presentes”;
    • Caso seja apenas um presente restante, o texto a ser passado para o GameObject com a tag ControleItens será “Falta 1 presente”, no singular; e
    • Caso não tenha mais presentes a serem coletados, o jogador é parabenizado.
  • Por fim, o GameObject que representa o presente na tela se autodestrói, por meio da função Destroy(gameObject).
Salve o script e vamos voltar ao Unity para testar a execução do jogo e ver toda essa “magia” acontecer:

Interrompa a simulação da execução, pressionando novamente o botão Play. Não se esqueça de, ao final, salvar a cena (menu File, Save) e o projeto (menu File, Save Project) antes de fechar o Unity.

Próximos passos

Começamos, enfim, a indicar visualmente algumas regras do jogo, como a presença de uma contagem regressiva de tempo e a quantidade de presentes a serem coletados. Aos poucos, iremos completar as funcionalidades de nossa primeira fase, deixando-a pronta para “passar o bastão” à fase seguinte, gerando de fato uma aventura de verdade.

Em nossos próximos tópicos iremos dar prosseguimento à implementação dos elementos gráficos e codificados que compõem a primeira fase, para, assim, podermos desenvolver as próximas e ligá-las em uma estrutura de partida bem interessante.

Nosso próximo texto já encontra-se disponível, continue conosco nessa jornada de conhecimento e fique ligado sempre aqui no GameBlast!

Revisão: Ives Boitano

Entendo videogames como sendo uma expressão de arte e lazer e, também, como uma impactante ferramenta de educação. No momento, doutorando em Sistemas da Informação pela EACH-USP, desenvolvendo jogos e sistemas desde 2020. Se quiser bater um papo comigo, nas redes sociais procure por @RodrigoGPontes.
Este texto não representa a opinião do GameBlast. Somos uma comunidade de gamers aberta às visões e experiências de cada autor. Escrevemos sob a licença Creative Commons BY-SA 3.0 - você pode usar e compartilhar este conteúdo desde que credite o autor e veículo original.