Se esta for a primeira vez que você está tendo contato com conteúdos de nossa série, considere-se especialmente convidado a juntar-se a nós em uma interessante trilha de aprendizagem. Desenvolvendo projetos práticos de programação de games, aprenderemos juntos diferentes conceitos sobre como podemos utilizar a ferramenta Unity para tirar do papel jogos dos mais variados estilos gráficos e de gameplay.
À primeira vista, os conteúdos da série podem parecer de difícil compreensão, mas, pelo contrário, a realidade é bem diferente: nossa série é desenvolvida tanto para “marinheiros de primeira viagem” quanto para quem já tem certa familiaridade com programação de jogos e sistemas.
A partir do primeiro tópico da série, abordamos desde a instalação e configuração do Unity em nossos computadores até as etapas que envolvem, dentre outros, a codificação de scripts controladores de comportamentos e regras de um jogo, o posicionamento de elementos visuais em cena, a configuração de colisores e a configuração de trilhas e efeitos sonoros em nossos projetos.
Por falar em projetos, no momento estamos desenvolvendo o game Consultório do Dr. Tratanildo: trata-se de um puzzle ambientado em um excêntrico consultório médico tridimensional, cujas características de gameplay prestam homenagem a clássicos dos videogames e dos arcades, tais como Dr. Mario (Nintendo, 1990) e Pac-Man (Namco, 1980).
Além do projeto atual, já desenvolvemos durante a série outros dois jogos: Forest Ping Pong e Motorista da Pesada. Aproveite o índice de tópicos disponibilizado na página do primeiro tópico da série e não deixe de conferi-los.
Gostou da ideia de criar seu próprio jogo do zero? Então não perca esta oportunidade e siga conosco nesta trilha de novos conhecimentos e de muita diversão!
Entendendo as Coroutines
Uma das principais formas que o Unity nos fornece para que possamos codificar em um game ações a serem realizadas de forma paralela é por meio das Coroutines.
As Coroutines são tipos especiais de funções que podem ser declaradas e programadas dentro dos scripts de forma semelhante às funções que já estamos habituados a codificar normalmente. Inclusive, os comandos internos que podemos utilizar nas Coroutines, em sua maioria, são os mesmos que costumamos acrescentar às rotinas de regras e de comportamentos de nossos scripts controladores.
A grande diferença entre uma Coroutine e uma função “comum” é o fato de que a primeira permite ter sua execução interrompida por um determinado período de tempo, seja este explicitamente ditado pelo programador (por exemplo, dois segundos), seja calculado pelo jogo a partir de determinadas circunstâncias do momento (por exemplo, o tempo necessário para se processar um frame ou finalizar um download).
Com essa importante característica, além de podermos codificar comandos que serão realizados em períodos de tempo distintos, podemos criar sequências lógicas no código envolvendo períodos de inatividade sem que o game inteiro tenha sua execução pausada por causa disso, pois a interrupção da execução de uma Coroutine não necessariamente exerce influência na execução do restante dos códigos de um jogo.
Que tal aprendermos na prática um pouco mais sobre as interessantes propriedades das Coroutines? Vamos realizar edições em nosso projeto para implementarmos, por meio de tal ferramenta, uma sequência de ações a ser executada pelo paciente ao chegar no consultório.
Mão na massa
Vamos abrir nosso projeto para edição, indo ao Unity Hub e clicando duas vezes sobre o item referente ao projeto Consultório do Dr. Tratanildo. Na interface inicial do editor, na aba Project, abra a pasta Assets, Scenes e, por fim, clique duas vezes no ícone da cena ConsultorioScene.
Na aba Project, abra as pasta Assets e, em seguida, Scripts. Clique duas vezes sobre o ícone do script ControllerFase para iniciarmos sua edição no Visual Studio.
Iniciaremos a codificação de uma nova Coroutine observando se a biblioteca que permite sua implementação já está declarada no início do script. Nas linhas iniciais de ControllerFase, verifique se uma (ou ambas) das duas linhas de código descritas abaixo está declarada; caso contrário, insira-as no bloco inicial de códigos:
- using System.Collections;
- using System.Collections.Generic;
Após a verificação, vamos iniciar a codificação de fato. Logo após o fechamento da última chave da função AtenderPaciente(), insira o seguinte bloco de código:
IEnumerator caminhadaInicialPaciente()
{
// Aguardar 1 segundo e começar a caminhada
pacienteCenario.GetComponent<Animator>().SetBool("caminharPeloCenario", false);
yield return new WaitForSeconds(1f);
pacienteCenario.GetComponent<Animator>().SetBool("caminharPeloCenario", true);
// Interromper caminhada quando paciente chegar bem perto de posicaoDialogoPaciente
while (Vector3.Distance(pacienteCenario.transform.position, posicaoDialogoPaciente)>0.25f)
{
pacienteCenario.transform.LookAt(posicaoDialogoPaciente);
yield return new WaitForEndOfFrame();
}
// Rotacionar paciente para conversar com o doutor
pacienteCenario.transform.position = posicaoDialogoPaciente;
pacienteCenario.GetComponent<Animator>().SetBool("caminharPeloCenario", false);
pacienteCenario.transform.LookAt(doutorConversa.transform);
}
O código que acabamos de introduzir ao script ControllerFase é a nossa primeira Coroutine, nomeada caminhadaInicialPaciente. Ela será responsável pela seguinte sequência de ações a ser realizada pelo paciente:
- Aguardar um segundo parado em frente à porta do consultório;
- Iniciar a caminhada pelo cenário, por meio de modificação do valor do parâmetro caminharPeloCenario do Animator Controller de pacienteCenario;
- Interrupção da caminhada ao se aproximar da posição desejada (posicaoDialogoPaciente);
- Ajustes na rotação do paciente, tanto durante a caminhada (em direção a posicaoDialogoPaciente) quanto logo após seu término (em direção a doutorConversa).
Note que existem algumas diferenças fundamentais entre as funções que implementamos até o momento em nossos scripts e uma Coroutine. Destaco em especial duas, que já podem ser analisadas pelo trecho de código que acabamos de introduzir ao script:
- Uma Coroutine normalmente é declarada como sendo uma função do tipo IEnumerator;
- As Coroutines apresentam um ou mais retornos (yield returns) que, em vez de serem valores “tradicionais” (de tipos como int, float ou string, por exemplo), representam o tempo cujo código terá sua execução suspensa naquele momento.
Referente a esta última característica, vamos analisar o primeiro bloco de comandos introduzido:
A interrupção de um segundo entre a aplicação de valores para o parâmetro caminharPeloCenario, referente à animação do paciente caminhando pelo cenário, é concretizada pelo comando yield return new WaitForSeconds, sendo 1f o valor, em segundos, dessa interrupção (um segundo).
Agora, vamos dar uma olhada no segundo bloco de comandos introduzido:
Aproveitando-se do fato de que a animação que aplicamos ao paciente realiza o deslocamento de seu GameObject pelo cenário, criamos uma estrutura de repetição que analisa, quadro a quadro, a distância entre o posicionamento global do paciente no cenário (pacienteCenario.transform.position) e a posição do paciente no cenário em que ele irá dialogar com o doutor (posicaoDialogoPaciente). Caso a distância seja maior do que 0.25f, ajustamos a rotação do paciente em direção a posicaoDialogoPaciente (por meio da função LookAt) e interrompemos a execução do código até o final do processamento do quadro em questão (yield return new WaitForEndOfFrame()).
O comando de retorno da Coroutine, aqui, é essencial para que o jogo continue a ser executado de forma adequada, já que, até que haja a aproximação do personagem com a posição pretendida no cenário, a Coroutine sempre “devolve as rédeas da execução” da lógica ao game, permitindo que continue havendo responsividade.
Por fim, vamos analisar o último bloco de comandos introduzido:
Note que, logo após o fim da execução da estrutura de repetição, determinamos o fim de sua caminhada pelo cenário e ajustamos tanto seu posicionamento quanto sua rotação, direcionando o olhar do paciente ao médico.
É interessante observar, também, que não há a obrigatoriedade de se utilizar um yield return final na estrutura de uma Coroutine. No entanto, é necessário que haja ao menos um yield return no corpo de seu código, mesmo que seja em sua linha inicial.
Feita a análise do conteúdo de nossa primeira Coroutine, vamos referenciá-la dentro da estrutura de AtenderPaciente, permitindo que as ações planejadas sejam concretizadas. No final do bloco de comandos da referida função (antes do fechamento das chaves), insira as seguintes linhas de código:
// Caminhada inicial do paciente em direção ao médico
StartCoroutine(caminhadaInicialPaciente());
É por meio do método StartCoroutine que realizamos a chamada à execução dos comandos declarados em uma Coroutine. Posteriormente, iremos aprender formas de acompanhar se uma Coroutine está sendo executada ou se já foi finalizada, mas, por ora, uma chamada simples à caminhadaInicialPaciente() já nos basta para realizar as ações pretendidas.
Salve o script e feche o Visual Studio. Vamos voltar ao editor do Unity para vermos na prática os comandos que acabamos de codificar sendo executados, indo à aba Game e clicando sobre o ícone do botão Play.
Assim como planejamos, todas as ações da sequência de caminhada do paciente em direção ao médico foram executadas nos tempos adequados, respeitando as regras que determinamos na Coroutine.
Interrompa a simulação, clicando novamente sobre o ícone do botão Play e voltando à aba Scene. Não se esqueça de salvar a cena (menu File, opção Save) e o projeto (menu File, opção Save Project) antes de fechar o editor.
Próximos passos
Hoje pudemos experimentar um pouco das potencialidades que a utilização de Coroutines pode proporcionar ao programador de jogos; como, por exemplo, a possibilidade de se elaborar fluxos de ações com personagens e objetos cuja execução possa aguardar determinada quantidade de tempo ou a conclusão de condições em específico.
Em nossos próximos encontros, continuaremos a implementar as funcionalidades do game que envolvam as interações iniciais entre paciente e Tratanildo Doencita.
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













