Caso esta seja a primeira vez que você tem acesso a conteúdos de nossa série, sinta-se convidado a juntar-se a nós em uma interessante trilha de aprendizagem. Por meio do desenvolvimento de projetos práticos de programação de jogos, temos a oportunidade de aprender mais sobre como a ferramenta Unity pode nos auxiliar a tirar do papel aquele game que sempre sonhamos em tornar realidade.
A partir do primeiro tópico da série, são abordadas diferentes técnicas, ferramentas e processos que fazem parte da sequência de ações necessárias para se criar um jogo do zero.
Mesmo se você não tiver experiências prévias com o mundo da programação de jogos ou de sistemas em geral, não se preocupe, pois, por meio dos textos já publicados, são exemplificadas tarefas que vão desde a instalação e configuração da ferramenta em nossos computadores até as atividades que nos ajudarão, de fato, a compor fases e desafios em nossos games, tais como o posicionamento e a configuração de elementos multimídia em cena e a programação de scripts controladores dos comportamentos dos elementos presentes em uma aventura virtual.
No momento, estamos desenvolvendo o game Consultório do Dr. Tratanildo, um puzzle ambientado em um consultório médico tridimensional. Trata-se de um jogo que presta homenagem a clássicos dos anos 1980 e 1990, tais como Dr. Mario (Nintendo, 1990) e Pac-Man (Namco, 1980), bebendo também da fonte de títulos menos aclamados, como Tama (Time Warner Interactive, 1994).
Gostou da ideia? Então aproveite esta oportunidade e siga conosco nesta trilha de novos conhecimentos e de muita diversão!
Tarefas simultâneas
Durante a elaboração de um game, é normal que, em determinados momentos da jogatina, o programador deseje que múltiplos comportamentos ou ações possam ocorrer simultaneamente. Mesmo nos jogos que elaboramos até o momento, ações como a coleta de um item não interrompem a aplicação de movimentos ou da ação da gravidade a um personagem, como podemos notar, por exemplo, ao se jogar fases de Motorista da Pesada.
Muitos dos comportamentos simultâneos que programamos para os elementos de nossos games, na realidade, não são tão “simultâneos” assim, mesmo que visualmente aparentem ser. Em geral, ao atrelarmos scripts controladores aos elementos de um jogo, a execução dos códigos que acrescentamos a funções como Start, Update e OnTriggerEnter será realizada de forma sequencial por meio de técnicas de concorrência, que respeitam a um fluxo de ordem de execução elencado pelo Unity, de acordo com o tipo de função declarada dentro de um script.
Em programação de jogos e de sistemas em geral, as duas principais abordagens utilizadas para se lidar com a execução de comandos de forma simultânea são a concorrência e o paralelismo. É importante entendermos esses conceitos para um melhor entendimento das “mágicas” que ocorrem por trás dos panos enquanto nossos games são executados.
Concorrência e paralelismo
Na abordagem baseada em concorrência, todos os comandos são executados por uma única unidade de processamento (um núcleo ou uma thread do processador de um computador, videogame ou smartphone), porém suas instruções são ordenadas de forma a permitir que a unidade de processamento dê conta de todo o trabalho de processamento “aparentemente” ao mesmo tempo.
A utilização de métodos de execução de instruções baseados em concorrência é viável pelo fato de que, normalmente, o tempo que o computador leva para a conclusão de uma tarefa é bem menor do que o tempo de percepção de uma ação pelo ser humano.
Já a abordagem baseada em paralelismo envolve a divisão das tarefas por múltiplas unidades de processamento, fazendo com que, de fato, haja a realização simultânea das atividades propostas pelos comandos programados.
Embora o Unity dê suporte a esse tipo de operação (por meio de funções do Unity Job System), nem toda plataforma cuja ferramenta consegue gerar executáveis dá suporte a multithreading. É o caso, por exemplo, de builds geradas para a Web, por meio da plataforma WebGL.
Algumas tarefas realizadas internamente pelo Unity que demandam constante necessidade de processamento, tais como a gestão da renderização das imagens em determinados sistemas, já se aproveitam internamente de recursos de paralelismo, sem precisarmos nos preocupar com esse tipo de questão. Contudo, a maioria dos comandos que programamos via scripts e, também, tarefas específicas, como as que envolvem o motor de física do Unity, são executadas por meio de técnicas de concorrência.
A imagem a seguir representa o fluxo de execução concorrencial do Unity, contendo os diversos tipos de funções aplicáveis aos scripts e suportados pela engine.
Embora a ilustração esteja em inglês, é possível notar que existem grupos específicos de funções e métodos cujos códigos são lidos e interpretados pelo Unity antes dos demais. No momento, vamos destacar quatro grupos de funções:
- Grupo de inicialização (Initialization): A execução de códigos presentes em funções como Awake, OnEnable e Start precede as demais, justamente para permitir que determinadas ações necessárias para a correta execução da lógica do game possa ocorrer sem sobressaltos.
Por exemplo, variáveis essenciais para a lógica de um game, tais como “quantidade de vidas”, por exemplo, normalmente devem receber seus valores por intermédios de funções de inicialização para impedir o surgimento de erros ao se executar funções de checagem, muito presentes em funções como Update. - Grupo de física (Physics): Códigos presentes em funções como OnTriggerEnter, OnTriggerExit e FixedUpdate, além de instruções internas de física do game, fazem parte deste grupo.
Sua execução precede à de códigos de controle da lógica do game para permitir que, ao se avaliar alguma regra do game relativa a condições envolvendo distâncias, força ou outros elementos correlatos, o game já tenha à mão os valores adequados para se tomar decisões adequadas. - Grupo de lógica do game (Game logic): Além da já citada função Update, métodos que tenham correlação com a lógica das regras de um jogo fazem parte do grupo.
Um caso curioso refere-se aos comandos internos executados pelo Unity a partir do fluxo de animação de um elemento em cena, que tanto são executados durante as etapas correspondentes ao grupo de física quanto ao de lógica do game. - Grupo de renderização da cena (Scene rendering): Trata-se de um grupo que condensa as ações realizadas pelo Unity para a exibição dos elementos em tela, sendo estes executados logo após a conclusão das etapas referentes à lógica do game.
Alguns dos elementos categorizados em cada grupo são executados frequentemente pelo game, seja a cada frame (como é o caso de Update e das funções de renderização), seja em escalas de tempo diferentes (como FixedUpdate e outras funções pertencentes ao grupo de física). Já outros, como Awake e Start, somente são executados no momento em que um elemento surge em cena ou é instanciado pela primeira vez.
Coroutines
Ainda observando o fluxo de priorizações apresentado, vamos realizar um destaque especial a um subgrupo de funções pertencente ao grupo de lógica:
Embora não esteja explicitamente representado, este subgrupo representa um tipo de função que permite ao game realizar ações de forma “parcelada”, tendo sua execução retomada em determinados períodos de tempo definidos pelo programador. São as Coroutines, importantíssimas para o correto desenvolvimento de lógicas dentro de um jogo feito em Unity.
Com Coroutines, conseguimos realizar diferentes ações contínuas sem que haja um bloqueio no fluxo de execução concorrencial do Unity. É possível, por exemplo, retomar a execução dos códigos de uma Coroutine após determinada quantidade de segundos (yield WaitForSeconds), após o término da execução dos códigos em um frame (yield WaitForEndOfFrame), após o término do download de um elemento da Web (yield WWW), dentre outras formas.
Próximos encontros
Conseguimos, hoje, entender melhor como o Unity se comporta em relação ao paralelismo de execução de seus comandos internos e, também, dos códigos que introduzimos nos projetos, por meio de funções implementadas nos scripts controladores.
No próximo encontro, iremos entender melhor como uma Coroutine opera na prática, iniciando a implementação de uma dentro do corpo do script controlador do paciente. A nova Coroutine será criada para nos auxiliar a controlar os movimentos iniciais do paciente pelo cenário.
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








