Caso você esteja acessando esta série pela primeira vez, junte-se a nós e venha desbravar esta trilha de aprendizado, que nos levará ao desenvolvimento de divertidos jogos e, consequentemente, a aprender muito sobre o processo. A partir do primeiro tópico, você poderá acompanhar as etapas de criação de um game utilizando a ferramenta Unity, desde a instalação e configuração do programa em seu computador até o desenvolvimento dos jogos em si, seus códigos, lógicas de programação, inserção de multimídias, criação dos desafios e execução dos testes envolvidos.
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 jogo Motorista da Pesada. Trata-se de um platformer 2D cujo objetivo a ser alcançado pelo gamer é a coleta de todas as caixas de presente espalhadas pelas fases no menor tempo possível, antes que o tempo se esgote, além de evitar encostar nas bombas, que são posicionadas estrategicamente para atrapalhar o jogador.
No tópico anterior de nossa jornada, implementamos o sistema de vidas do game e espalhamos algumas bombinhas traiçoeiras pelo caminho, avançando mais um pouco no processo de desenvolvimento de nosso game. Venha conosco e vamos juntos nesta jornada rumo a novos conhecimentos!
Quando a ordem dos objetos importa
O primeiro ajuste que iremos realizar em nosso projeto refere-se ao bug que ocorre ao se pausar, ganhar ou perder uma partida: o contador de vidas restantes fica sobreposto às telas apresentadas, destoando do restante dos exibidores de tempo e presentes restantes.
O ajuste para esse bug é bem simples, envolvendo a alteração da ordem de exibição de alguns itens do Canvas de nossa fase. Para iniciarmos as alterações, 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.
Observe que, subordinado ao GameObject CanvasFase, o objeto ContadorVidas está listado após os objetos referentes às telas de pause, vitória e derrota.
Por padrão, o Unity renderiza (desenha na tela) os objetos pertencentes a um Canvas de forma sequencial, na ordem apresentada na aba Hierarchy. Portanto, a cada frame, em nosso jogo são apresentados o cronômetro, a listagem de itens restantes, o botão de pause e, por fim, o contador de vidas.
Ao se ativar um dos GameObjects referentes às telas de pause, vitória ou derrota, como estão ordenados antes do objeto ContadorVidas, acabam sendo desenhados em tela antes do referido contador.
Para corrigir isso, na aba Hierarchy, iremos clicar sobre o objeto ContadorVidas e, sem soltar o botão do mouse, “arrastá-lo” para cima, fazendo com que fique ordenado entre os GameObjects BotaoPause e TelaPause, conforme exemplo a seguir:
Teste a execução do jogo, indo à aba Game e clicando sobre o botão Play. Experimente pausar o game e veja que interessante ficou a tela agora:
O mesmo ocorre, por exemplo, ao se perder a partida. Experimente e, ao final, não se esqueça de interromper a simulação, clicando novamente sobre o botão Play e retornando à aba Scene.
Placar final
Por falar em game over, vamos corrigir um pequeno problema presente bem no centro de sua tela, referente ao contador final de presentes que, no momento, não está sendo atualizado.
Para corrigir isso, iremos acrescentar algumas linhas de código a dois scripts que já estamos utilizando há algum tempo. O primeiro a ser editado será o script Itens. Para tal, na aba Project, abra o conteúdo da pasta Assets, vá até sua subpasta Scripts e, por fim, clique duas vezes sobre o ícone do script Itens.
Partida.PontosJogador++;
Da mesma forma que diminuímos em uma unidade o valor da variável Partida.ObjetosRestantes, ao se coletar um presente, iremos acrescentar também em uma unidade a variável Partida.PontosJogador.
Feito isso, salve o script e minimize o Visual Studio. No editor do Unity, ainda na aba Project e com a pasta Scripts aberta, clique duas vezes sobre o ícone do script ControladorFase.
A primeira alteração que iremos fazer é referente ao início de uma partida. Dentro do conteúdo da função void Start(), insira, dentro dos colchetes das condições de início de uma fase (Partida.TempoRestante <= 0 ou Partida.Vidas == 0), a seguinte linha de código:
Partida.PontosJogador = 0;
Dessa forma, garantimos que, ao se iniciar uma partida, o placar seja zerado, sem influência do resultado de partidas anteriores.
Ainda no script ControladorFase, dentro do conteúdo da função void Perdeu(), iremos acrescentar, logo após a linha que ativará a exibição da tela de derrota, o seguinte trecho de código:
string textoPlacar = "Placar final: " + Partida.PontosJogador + " presente";
if (Partida.PontosJogador > 1)
textoPlacar += "s";
telaPerdeu.transform.Find("PlacarFinal").GetComponent<Text>().text = textoPlacar;
Nesse trecho de código são realizadas as seguintes ações:
- É declarada uma variável temporária textoPlacar, que irá armazenar uma string com o placar final da partida;
- Caso o placar de presentes coletados seja maior do que apenas uma unidade coletada, é acrescentado um “s” no final da referida string, deixando a frase no plural, com a palavra final “presentes”;
- Por meio da função Find(string), é realizada uma busca entre os objetos subordinados ao GameObject telaPerdeu que tenham o nome igual a “PlacarFinal” (sem as aspas). Após localizado, é realizada a alteração do conteúdo textual do componente Text do referido objeto para o conteúdo da variável textoPlacar.
O objetivo das operações realizadas é mostrar, ao gamer, qual foi a quantidade total de presentes coletados em toda sua trajetória na partida finalizada.
Salve o script, minimize o Visual Studio e, no editor do Unity, experimente simular a execução do game, indo à aba Game e clicando sobre o botão Play. Estoure três bombas pelos caminhos e veja o conteúdo da tela, adequadamente exibido. Não se esqueça de, ao final da simulação, interromper a execução.
Um botão fantasma
Outro bug relacionado ao placar final do jogo, que também iremos corrigir agora, é o fato de que, ao se clicar em seu texto, a tela de game over é suprimida, um comportamento indesejado, visto que o placar do jogo, neste game em específico, é apenas informativo.
Esse comportamento estranho deriva-se do fato de que implementamos as telas de vitória e de derrota como “clones” da tela de pause, cujas frases apresentadas são, de fato, botões para retomar a partida ou voltar ao menu inicial.
Dessa forma, vamos remover o componente Button do objeto referente ao placar final, para que esse comportamento não volte a ocorrer.
Na aba Hierarchy, selecione o GameObject TelaPerdeu, subordinado ao objeto CanvasFase, e o ative, clicando na caixa de seleção ao lado de seu nome na aba Inspector, conforme exemplo a seguir:
Selecione agora, na aba Hierarchy, seu objeto subordinado PlacarFinal. Na aba Inspector, na extremidade superior direita da área referente ao componente Button, clique no símbolo de três pontos e, no menu suspenso, selecione a opção Remove Component.
Na aba Hierarchy, selecione novamente o GameObject TelaPerdeu e desative-o, clicando novamente na caixa de seleção ao lado de seu nome na aba Inspector.
Dessa forma, PlacarFinal apresentará apenas o comportamento que é esperado para um objeto mostrador do placar final de uma partida.
Fim do tempo
Outro pequeno detalhe, que não passará despercebido por nossos “olhos de águia”, também tem relação com o game over: trata-se do fato de que, ao se esgotar o tempo previsto para a partida, simplesmente não é exibida a tela de fim de jogo; em seu lugar são exibidos estranhos minutos e segundos “negativos” no cronômetro do jogo, como mostra o exemplo:
Dessa forma, temos que tomar determinadas ações tanto para impedir a exibição de números negativos no placar quanto para que, ao se esgotar o tempo, seja declarado o fim da partida.
No script ControladorFase temos um trecho de código que é responsável pelo decréscimo gradativo do tempo do jogo, e será lá que iremos realizar determinadas alterações para solucionar esse problema.
Então, na aba Project, dentro da pasta Scripts, devemos novamente clicar duas vezes sobre o ícone correspondente ao script ControladorFase para editá-lo no Visual Studio.
Substitua todo o conteúdo interno dos colchetes da função void Update() pelo trecho de código descrito abaixo:
if (controladorAtivo)
Partida.TempoRestante -= Time.deltaTime;
if (Partida.TempoRestante < 0)
{
controladorAtivo = false;
Partida.TempoRestante = 0;
Perdeu();
}
A primeira parte do trecho de código exibido mantém-se a mesma, sendo o decréscimo do tempo controlado pela variável booleana ControladorAtivo.
O segundo trecho, inédito, irá verificar se o tempo da partida se esgotou. Em caso positivo, as seguintes ações serão tomadas:
- Concessão de um valor falso (false) para ControladorAtivo, interrompendo o decréscimo do tempo;
- Concessão do valor zero para a variável Partida.TempoRestante, impedindo que eventuais valores negativos de tempo sejam apresentados em tela; e
- Chamada à função Perdeu(), responsável, dentre outros, por exibir a tela de game over e o placar final da partida.
Salve o script, minimize o Visual Studio e, no editor do Unity, simule a execução do game, aguardando o tempo se encerrar para ver o jogo, de fato, respeitar a todas as regras de fim de jogo que propusemos para Motorista da Pesada.
Finalize a simulação da execução, clicando novamente sobre o botão Play. Não se esqueça de salvar a cena (menu File, Save) e o projeto (menu File, Save Project) antes de fechar o Unity.
Próximos passos
Corrigimos os principais pontos que observamos em nosso encontro anterior, finalizando de fato a implementação dos sistemas que envolvem o fim de uma partida, seja por meio da perda de todas as vidas, seja pelo término do tempo disponível. Além disso, temos agora um sistema de placar funcional, que considera a quantidade total de presentes coletados e informa ao jogador seu progresso, ao término da partida.
Em nossos próximos encontros iremos ajustar aspectos relacionados ao sistema de pulos do carrinho nas fases, iremos inserir as trilhas e efeitos sonoros do game e deixaremos tudo pronto para a elaboração das outras fases que irão compor nossa grande aventura bidimensional.
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