Se hoje for a primeira vez que você tem contato com conteúdos de nossa série, sinta-se especialmente convidado a acompanhar-nos em uma jornada interessante em busca de novos conhecimentos sobre o mundo do desenvolvimento de jogos digitais.
Ao desenvolvermos projetos práticos de programação de jogos, aprendemos sobre como ferramentas de criação de games, como a Unity, podem nos auxiliar a tirar do papel projetos variados, abordando estilos gráficos e de gameplay distintos.
A partir do primeiro tópico da série, abordamos desde os passos iniciais do processo de desenvolvimento de um game, como a instalação e a configuração da ferramenta Unity em nossos computadores, até os elementos em que colocarmos as “mãos na massa” para valer, programando regras e comportamentos de itens, de personagens e de cenários de um jogo, além do posicionamento e da configuração dos objetos pertencentes aos mundos mágicos de nossas aventuras.
Os conceitos apresentados nos textos são abordados por meio de exemplos, aplicados diretamente nos projetos que elaboramos. Por isso, não se preocupe se você não tem conhecimentos prévios de programação de jogos ou de sistemas, pois, mesmo assim, você terá a possibilidade de fazer acontecer os projetos que sempre sonhou em tornar realidade.
Durante a elaboração de nossa série, já produzimos dois games 2D: trata-se de Forest Ping Pong, uma homenagem ao clássico Pong (Atari, 1972); e de Motorista da Pesada, um platformer inspirado no arcade japonês City Connection (Jaleco, 1985).
Atualmente, estamos trabalhando no projeto de construção do jogo Consultório do Dr. Tratanildo. Ambientado em um consultório médico tridimensional, o game mescla aspectos de gameplay inspirados em clássicos dos anos 1980 e 1990, como Dr. Mario (Nintendo, 1990) e Pac-Man (Namco, 1980).
Vale muito a pena visitar os textos em que são detalhados os passos para elaboração dos três games, acessíveis por meio do índice disponibilizado no primeiro texto da série. É possível, inclusive, experimentar os dois primeiros jogos completos, diretamente de seu navegador, por meio de entradas específicas nos últimos textos relacionados a eles.
Caso você tenha gostado dessa ideia, não perca mais tempo: aperte os cintos e junte-se a nós nessa estrada repleta de novos conhecimentos e de muita diversão!
Pausando o game
Em encontros anteriores, configuramos os elementos gráficos que serão exibidos em tela assim que o jogo for pausado, além dos correspondentes aos momentos em que se vence ou se perde o game. As duas últimas telas foram configuradas para serem mostradas adequadamente, já a restante aguardava a implementação da funcionalidade de pausar o game para que pudesse ser exibida.
Vamos implementar essa funcionalidade, realizando intervenções no script controlador de fases do game e em configurações do projeto em si. Para tal, vamos abrir o projeto para edição. No Unity Hub, clique duas vezes sobre o item referente ao projeto Consultório do Dr. Tratanildo. Na interface inicial do Unity, na aba Project, abra a pasta Assets e, em seguida, Scenes. Clique duas vezes sobre o ícone da cena ConsultorioScene.
Inicialmente, configuraremos o reconhecimento, pelo projeto, do botão de menu do controle do Xbox, cujo suporte implementamos há alguns encontros. Na interface do editor do Unity, clique sobre o menu Edit e, em seguida, selecione a opção Project Settings.
Na janela apresentada, selecione o item Input Manager e, em seguida, amplie o quantitativo do campo Axes Size em uma unidade. Na prática, como observamos anteriormente, o último item será duplicado, conforme exemplificado a seguir.
Expanda o grupo de opções relacionado ao último item da listagem e realize as seguintes alterações em seus atributos:
- Name: conceda o nome “Xbox_Start” como valor para o atributo;
- Positive Button: troque o valor do campo para “joystick button 7”;
- Type: selecione a opção Key or Mouse Button;
- Axis: escolha a opção X axis.
Embora o botão de menu do controle do Xbox One não apresente oficialmente o nome “Start” atrelado a si, como essa era a nomenclatura nos tempos do Xbox 360, considerei utilizar esse nome para facilitar a lembrança sobre qual botão estamos tratando, ao codificarmos os scripts. Caso prefira utilizar outro nome, fique à vontade.
Na funcionalidade para pausar o game que implementaremos em instantes, tanto o referido botão do controle do Xbox quanto a tecla Esc do teclado servirão para pausar e para retomar o game.
Realizaremos uma sequência de intervenções no script ControllerFase para que possamos implementar a nova funcionalidade. Feche a janela Project Settings e retorne à interface principal do Unity. Na aba Project, abra a pasta Assets e, em seguida, Scripts. Clique duas vezes sobre o ícone do script ControllerFase para iniciarmos sua edição no Visual Studio.
A primeira intervenção envolverá a declaração de duas novas variáveis, a serem inseridas no final do bloco de variáveis referentes às informações sobre a fase corrente. Localize a declaração da string etapaAtual e, logo após, insira as seguintes linhas de código:
internal string modoDeJogoAnterior = "Início"; // Utilizado para pausar o game
internal bool statusBarraInferiorAoPausar = false;
Utilizaremos o conteúdo da variável modoDeJogoAnterior para decidirmos se o jogo deverá ser pausado ou retomado, devolvendo o game ao modo de jogo anterior à interrupção, caso seja decidido pela retomada de sua execução.
Já statusBarraInferiorAoPausar será a variável cujo conteúdo determinará à lógica do jogo se a barra inferior será reexibida ou não ao se retomar a execução do jogo. Essa necessidade ocorre devido ao fato de que o jogo pode ser pausado em diferentes momentos, com a barra inferior sendo exibida ou não imediatamente antes da interrupção.
Com as variáveis devidamente declaradas, procure o código correspondente à função Update. Logo após o fechamento de suas chaves, imediatamente antes da declaração da função AtenderPaciente, introduza o seguinte grupo de comandos:
public void Pausar()
{
if (modoDeJogoAnterior == "Início" || modoDeJogoAnterior == "Pausado")
{
modoDeJogoAnterior = Geral.ModoDeJogoCorrente;
Geral.ModoDeJogoCorrente = "Pausado";
telaPause.SetActive(true);
telaPause.GetComponentInChildren<Button>().Select();
statusBarraInferiorAoPausar = FindObjectOfType<ConcedePilula>().barraInferior.gameObject.activeSelf;
FindObjectOfType<ConcedePilula>().barraInferior.gameObject.SetActive(false);
// Reexibe a seta do mouse, caso aplicável
if (Geral.ModoDeControle == "Teclado+Mouse")
Cursor.lockState = CursorLockMode.None;
}
else
{
Geral.ModoDeJogoCorrente = modoDeJogoAnterior;
modoDeJogoAnterior = "Pausado";
telaPause.SetActive(false);
Time.timeScale = 1;
FindObjectOfType<ConcedePilula>().barraInferior.gameObject.SetActive(statusBarraInferiorAoPausar);
// Oculta a seta do mouse, caso aplicável
if (Geral.ModoDeControle == "Teclado+Mouse")
Cursor.lockState = CursorLockMode.Locked;
}
}
A nova função Pausar será a principal responsável pela realização dos comandos relacionados à exibição da tela de pause e, também, à retomada do jogo em seu estado anterior.
Note que o uso da variável modoDeJogoAnterior determina se o jogo será pausado ou retomado. Sua atualização ocorre ao se executar um dos dois blocos principais de comandos da função: assumindo o valor de ModoDeJogoCorrente, ao se pausar o game, ou retomando o valor “Pausado”, caso ocorra a ação contrária.
Assim como adotamos como prática nos jogos anteriores, a interrupção ou retomada do jogo se dá por meio da alteração do valor de Time.timeScale. Embora possa parecer estranho à primeira vista não termos concedido o valor 0 à variável ao se pausar o game, realizaremos justamente essa intervenção em outro trecho de código, em instantes, ainda dentro do escopo do script ControllerFase.
É importante também notarmos que, por meio do comando telaPause.GetComponentInChildren<Button>().Select(), o primeiro elemento de tipo Button atrelado a um dos objetos subordinados a telaPause será selecionado. Isso significa que o jogador conseguirá interagir com a interface da tela de pause por meio do teclado ou de um controle de videogame, assim como realizamos anteriormente para os elementos do menu principal do jogo.
Localize, novamente, a função Update. Dentro de seu conteúdo, introduza o seguinte par de linhas de comando logo após o fechamento das chaves do bloco de código executado no momento em que o tempo se esgota (condicional if (tempoRestante < 0)):
else if ((Geral.ModoDeControle.Contains("Teclado") && Input.GetKeyDown(KeyCode.Escape)) || (Geral.ModoDeControle == "Controle" && Input.GetButtonDown("Xbox_Start")))
Pausar();
Em seguida, imediatamente antes do último fechamento de chaves da função Update, introduza o seguinte bloco de comandos:
else if (Geral.ModoDeJogoCorrente == "Pausado")
{
Time.timeScale = 0;
barraInfoAcoes.SetActive(false);
if ((Geral.ModoDeControle.Contains("Teclado") && Input.GetKeyDown(KeyCode.Escape)) || (Geral.ModoDeControle == "Controle" && Input.GetButtonDown("Xbox_Start")))
Pausar();
}
Nossa nova intervenção realiza duas ações distintas. O primeiro par de linhas de código introduzido é responsável por verificar se o jogador pressiona a tecla Esc do teclado ou o botão Xbox_Start do controle de videogame, caso o tempo restante ainda não tenha se esgotado; sendo que, nesse caso, a função Pausar é acionada.
Essa verificação ocorre pois não faz sentido pausarmos o game se ele já tiver sido encerrado, com a tela de derrota em exibição.
Já o segundo grupo de comandos introduzido é responsável por verificar se o jogo está em modo de jogo pausado. Como trata-se de um código dentro da função Update, em casos positivo, três ações serão executadas continuamente :
- O valor de Time.timeScale será zerado, para que o jogo permaneça interrompido;
- A barra de informações na parte superior do Canvas será ocultada temporariamente;
- O jogo conferirá se o jogador pressionou a tecla Esc do teclado ou o botão Xbox_Start do controle naquele momento, para retirar o jogo do modo de interrupção, por meio de chamada à função Pausar.
Note que, agora sim, o jogo será pausado de fato, pois Time.timeScale terá seu valor zerado e, por consequência, todos os contadores de tempo e as ações do módulo de física do Unity são interrompidos.
Melhorias para vitórias e para derrotas
Aproveitando que estamos realizando intervenções relacionadas à exibição da tela de pause de nosso game, vamos ajustar alguns comportamentos pontuais observados quando da exibição das telas de derrota e de vitória em nosso game.
Inicialmente, vamos implementar códigos que permitirão ao jogador retornar ao menu inicial do game, por meio das opções que já são apresentadas pelas duas telas.
Na parte superior do script, referente às bibliotecas em uso pelos códigos (bloco de linhas iniciadas pela expressão using), introduza a seguinte linha de comando:
using UnityEngine.SceneManagement;
A biblioteca SceneManagement é responsável por permitir ao script realizar ações relacionadas à gestão de cenas em nosso jogo. Embora utilizemos apenas uma cena na concepção desse projeto, uma das funções presentes na biblioteca permite recarregá-la, o que, de fato, levará o jogador de volta ao menu inicial do game.
Avance até o final do conteúdo do script e, antes do último fechamento de chaves, introduza o seguinte grupo de códigos:
public void RecarregarCenaAtual()
{
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
}
Basicamente, ao se acionar a função RecarregarCenaAtual, o jogo carregará a cena cujo nome é o mesmo da atualmente em uso. Em nosso caso concreto, será a função responsável por permitir o retorno ao menu inicial.
As últimas intervenções em script que realizaremos hoje envolverão replicarmos a experiência que adotamos para a tela de pause de nosso game às demais telas indicadas, em relação ao selecionamento automático do primeiro elemento de tipo Button atrelado a um dos objetos subordinados às telas.
Localize a função AvancarProximaFase e adicione a seguinte linha de comandos ao conjunto de códigos que é executado assim que o jogador vence os desafios propostos:
telaVitoria.GetComponentInChildren<Button>().Select();
Em seguida, de volta ao conteúdo da função Update, localize o grupo de códigos que é executado quando o tempo restante da partida se esgota e, antes do código correspondente à reexibição da seta do mouse, introduza a seguinte linha de códigos:
telaDerrota.GetComponentInChildren<Button>().Select();
Salve o script e feche o Visual Studio. De volta ao editor do Unity, na aba Hierarchy, vamos desativar momentaneamente a visualização do menu inicial, selecionando o objeto MenuInicial, subordinado a Canvas e, via aba Inspector, deixando vazia a caixa de seleção ao lado de seu nome, conforme exemplificado a seguir.
Selecione o objeto TelaPause, também subordinado a Canvas, e ative-o, por meio da aba Inspector. Selecione seu objeto subordinado ButtonRetornarPause e, na aba Inspector, localize seu componente de tipo Button. Dentro do grupo de ações On Click, adicione uma referência ao objeto ControladorFase e, em seguida, à função ControllerFase.Pausar.
Em seguida, desative TelaPause e ative TelaGameOver. Selecione seu objeto subordinado ButtonVoltarTelaInicial e, na aba Inspector, localize seu componente de tipo Button. Dentro do grupo de ações On Click, adicione uma referência ao objeto ControladorFase e, em seguida, à função ControllerFase.RecarregarCenaAtual.
De forma semelhante, desative TelaGameOver e ative TelaVitoria. Selecione seu objeto subordinado ButtonVoltarTelaInicial e repita os mesmos passos aplicados anteriormente ao componente Button de seu homônimo.
Com tudo devidamente configurado, é hora de testarmos as novas implementações, indo à aba Game e clicando sobre o ícone do botão Play. Experimente pausar o jogo em diferentes situações, como, por exemplo, durante a caminhada do médico pelo cenário, na coleta de pílulas e na interação com o leito hospitalar.
Também experimente verificar o comportamento dos menus de vitória e de derrota do game, em relação ao retorno à tela inicial do jogo.
Interrompa a simulação de execução, clicando novamente sobre o ícone do botão Play e retornando à aba Scene. 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
Ao concluirmos a construção das funcionalidades para pausar o jogo e para retornar ao menu inicial, estamos muito próximos de concluirmos a elaboração de Consultório do Dr. Tratanildo. Basicamente, precisamos finalizar três conjuntos de atividades, antes de publicarmos a versão final de nosso game:
- Correção de bugs envolvendo o menu de preferências de controle e as cutscenes de pacientes recém-tratados;
- Elaboração da tela de créditos do game;
- Configuração da sequência definitiva de pacientes e de desafios.
Em nosso próximo encontro nos dedicaremos a finalizar essas pendências, para que possamos, enfim, libertar do mundo conceitual e trazer ao público essa aventura tão excêntrica e divertida.
Nos encontraremos novamente no próximo dia 30 de agosto. Até mais! Fique ligado sempre nas novidades do GameBlast!
Revisão: Ives Boitano





















