Se esta for a primeira vez que você tem contato com um conteúdo de nossa série, aproveite para juntar-se a nós em uma divertida jornada de aprendizados sobre o fascinante mundo do desenvolvimento de jogos. Nesta série, o maior objetivo é descobrirmos juntos sobre como podemos utilizar a ferramenta Unity para nos ajudar a criar games de diferentes estilos, por meio do desenvolvimento de interessantes projetos práticos.
No momento, estamos trabalhando no projeto Consultório do Dr. Tratanildo. Trata-se do desenvolvimento de um platformer 3D bem peculiar: ambientado em um consultório médico, o objetivo do jogo é a realização de curiosos tratamentos médicos por meio do uso de miraculosas pílulas, descobertas e administradas pelo excêntrico doutor Tratanildo Doencita.
Não se preocupe se, até hoje, você nunca teve contato com conteúdos referentes ao mundo da programação de jogos: a partir do primeiro texto da série, abordamos desde os tópicos mais básicos, como a instalação da ferramenta em nossos computadores, até aqueles considerados mais desafiadores, como a codificação de scripts controladores de comportamentos e regras, posicionamento de elementos multimídia nos cenários e criação de menus e interfaces.
Venha conosco para, juntos, seguirmos nesta caminhada rumo a novos conhecimentos!
Botando ordem no labirinto
Depois de passarmos alguns encontros ajustando importantes aspectos do labirinto, como a sincronização da inclinação com o leito hospitalar e o controle dos movimentos por meio das setas do teclado, chegou a hora de iniciarmos a configuração de seus conteúdos internos, começando pelos elementos que representam os obstáculos.
Os pacientes que comparecerão ao consultório de doutor Tratanildo poderão apresentar diferentes “layouts internos” de seus corpos, representados pelos obstáculos dentro do labirinto. Até podemos inserir via editor do Unity conjuntos de GameObjects para cada paciente, mas esta ação seria bem contraproducente, dependendo de quantos conjuntos diferentes de pacientes forem ao consultório.
Por isso, codificaremos agora um script que realizará a construção dos caminhos internos do labirinto de forma dinâmica. Vamos abrir, então, nosso 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 editor, na aba Project, abra a pasta Assets, Scenes e, por fim, clique duas vezes no ícone da cena ConsultorioScene.
Na aba Hierarchy, vamos dar uma olhada em como está a composição atual dos elementos que representam os obstáculos: procure os objetos subordinados a Obstaculos, que, por sua vez, encontra-se subordinado a Labirinto, conforme imagem a seguir:
No momento, temos alguns GameObjects em formato de cubos servindo como obstáculos: eles estão, até o momento, estacionados de forma permanente dentro da estrutura de nosso labirinto. Vamos remover todos eles, com exceção do objeto de nome Cube, que será, a partir de agora, nosso objeto de referência para a construção dinâmica dos caminhos pelo script que programaremos em instantes.
Após excluir os demais elementos, renomeie Cube para “CubeRef”, sem as aspas. Na aba Inspector, altere os valores dos atributos de seu componente Transform para os valores descritos a seguir:
- Position X, Y e Z = 0;
- Rotation X, Y e Z = 0;
- Scale X, Y e Z = 1.
Por fim, desative o GameObject, deixando sua caixa de seleção vazia (destacada em azul na imagem a seguir):
CubeRef permanecerá desativado durante toda a execução do game, mas sua presença no projeto será muito útil, pois iremos “cloná-lo” via script, aproveitando suas propriedades (como a estrutura cúbica de seu modelo e suas configurações de colisão) para a configuração dos novos elementos a serem dispostos subordinadamente a Obstaculos.
Programando o script
Na aba Project, abra a pasta Assets e, em seguida, Scripts. Crie um novo script de nome “MontaLabirinto”, sem as aspas, conforme exemplificado a seguir:
Clique duas vezes sobre o ícone do novo script para iniciarmos sua edição no Visual Studio. A primeira ação que realizaremos dentro do conteúdo do novo script é a remoção do código que representa as funções void Start() e void Update(), pois não as utilizaremos no momento:
Dentro do espaço vazio que surgiu após as remoções, iremos inserir diversos códigos interessantes. Vamos começar pela declaração de algumas variáveis: insira o código a seguir dentro das chaves de public class MontaLabirinto:
public GameObject cuboReferencia;
public Vector3[] posicoesCubos, dimensoesCubos;
internal GameObject[] cubosCriados = new GameObject[0];
A variável pública cuboReferencia servirá para que possamos indicar ao Unity qual é o GameObject modelo a ser utilizado como base para a criação dos novos elementos que servirão como obstáculos dentro do labirinto.
Já os vetores públicos posicoesCubos e dimensoesCubos, por serem de tipo Vector3, nos serão bem úteis para indicarmos quais serão os posicionamentos de cada elemento a ser criado dentro do labirinto (atributos Position de seus componentes Transform) e suas respectivas dimensões (atributos Scale de seus componentes Transform).
Por fim, temos um vetor de GameObjects chamado cubosCriados, útil para que possamos rastrear posteriormente quais foram os obstáculos criados e, assim que um novo paciente entrar no consultório, possamos removê-los, deixando o labirinto pronto para ter seus caminhos configurados novamente.
Após a declaração das variáveis, insira as seguintes linhas de código:
public void Montar()
{
// Apagar cubos, se já houver
foreach (GameObject go in cubosCriados)
Destroy(go);
// Criar novos cubos do labirinto, com base nos parâmetros
// indicados em posicoesCubos e dimensoesCubos
cubosCriados = new GameObject[posicoesCubos.Length];
for (int i = 0; i < posicoesCubos.Length; i++)
{
cubosCriados[i] = Instantiate(cuboReferencia, cuboReferencia.transform.parent);
cubosCriados[i].SetActive(true);
cubosCriados[i].name = "CuboObstaculo_" + i;
cubosCriados[i].transform.localPosition = posicoesCubos[i];
cubosCriados[i].transform.localScale = dimensoesCubos[i];
}
}
A função Montar() será a responsável pela realização das seguintes ações, assim que for acionada:
- Remover GameObjects presentes no labirinto, caso existam (por meio de chamadas à função Destroy(GameObject) do Unity);
- Inserir novos GameObjects baseados em cuboReferencia, subordinados hierarquicamente ao elemento a qual cuboReferencia já é subordinado, ou seja, a seu parent (por meio de chamadas à função Instantiate(cuboReferencia, cuboReferencia.transform.parent));
- Configurar o nome dos novos GameObjects criados e, por fim, dimensioná-los e posicioná-los adequadamente em relação a Obstaculos.
Por fim, para podermos testar os comportamentos programados, após o bloco de código recém-introduzido, insira o seguinte trecho de código:
private void OnEnable()
{
Montar();
}
Basicamente, o pequeno trecho de código fará com que, assim que o script for ativado, a função Montar() será executada, o que permitirá a nós observar a aplicação dos comportamentos “ao vivo” em nosso game. Salve o script e feche o Visual Studio.
Ajustes e testes
Volte ao editor do Unity e, na aba Hierarchy, selecione o objeto Labirinto. Na aba Inspector, por meio do botão Add Component, insira um componente do tipo Monta Labirinto.
Para o campo Cubo Referencia, selecione o item de nome CubeRef. Já para os atributos Posicoes Cubos e Dimensoes Cubos, no campo ao lado de seus títulos, digite o valor 4 e pressione Enter. Serão exibidos alguns campos numéricos, conforme exemplificado pela imagem a seguir:
- Element 0: X = 5.5, Y = 0, Z = 4.5;
- Element 1: X = 0, Y = 0, Z = -4.5;
- Element 2: X = -3.5, Y = 0, Z = 5;
- Element 3: X = -5, Y = 0, Z = -5;
Já para as linhas referentes ao atributo Dimensoes Cubos, serão estes os valores a serem concedidos:
- Element 0: X = 1, Y = 1, Z = 10;
- Element 1: X = 1, Y = 1, Z = 10;
- Element 2: X = 3, Y = 1, Z = 6;
- Element 3: X = 2, Y = 1, Z = 6;
A princípio, já estamos prontos para testar o novo comportamento de nosso labirinto, mas, para evitar que as pílulas que já estão no labirinto sejam sobrepostas aos elementos que surgirão, vamos realizar pequenas alterações nos valores dos atributos Position de seus elementos Transform.
Para Pilula_vermelha, subordinado a Medicamentos, conceda os seguintes valores:
- Position X = -7, Y = 0.5, Z = 0.
Já para Pilula_verde, também subordinado a Medicamentos, conceda os seguintes valores:
- Position X = 7.5, Y = 0.5, Z = 7.
Agora sim, como de costume, vamos simular a execução de nosso jogo, indo à aba Game e clicando sobre o ícone do botão Play:
Observe que, além de termos novos elementos tridimensionais dispostos dentro da estrutura do labirinto, na aba Hierarchy podemos perceber a presença dos cubos gerados automaticamente pela execução do script, nomeados CuboObstaculo_0, CuboObstaculo_1, CuboObstaculo_2 e CuboObstaculo_3.
Interrompa a simulação da execução, pressionando novamente o ícone do botão Play. Vamos voltar à aba Scene para realizarmos um pequeno experimento. Na aba Hierarchy, selecione Labirinto. Na aba Inspector, dentro da estrutura do componente Monta Labirinto, altere os valores de Element 0 do atributo Dimensoes Cubos para os descritos a seguir:
- Element 0: X = 8, Y = 1, Z = 1.
Realize nova simulação de execução do game para ver que, por meio de uma pequena alteração, diferentes desafios podem ser gerados dinamicamente para nossos labirintos. No exemplo, alteramos a disposição das dimensões do primeiro cubo, mudando toda a dinâmica para que o jogador alcance o agente de doenças com o medicamento correto:
Após interromper a simulação, pressionando novamente o botão Play, 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
Mais uma vez, realizamos uma interessante intervenção em nosso projeto por meio da criação de um script controlador. Posteriormente, quando o game estiver mais próximo de sua forma final, o script codificado hoje será bem útil para que o game possa gerar desafios únicos ao jogador, tornando os desafios mais agradáveis e interessantes de serem encarados pelos gamers.
Em nossos próximos encontros, continuaremos a implementar melhorias comportamentais e visuais em relação ao labirinto e aos componentes que fazem parte de seu contexto no jogo.
Nosso próximo encontro será no dia 29 de dezembro. Para quem nos lê de forma síncrona, aproveito para desejar um ótimo natal e boas festas. Até mais! Fique sempre ligado nas novidades do GameBlast!
Revisão: Davi Sousa