Caso esta seja a primeira vez que acessa nossa série, sinta-se à vontade. Convidamos você a acompanhar todo o conteúdo que foi desenvolvido até agora. A partir de nosso primeiro tópico você irá aprender todo o processo de instalação e configuração da ferramenta Unity, elaboração, codificação e criação de games a partir do zero, por meio de diversos exemplos e projetos que estamos elaborando passo a passo.
Estamos no momento desenvolvendo nosso segundo jogo desta série. Seu nome é Motorista da Pesada e trata-se de um platformer 2D que irá apresentar múltiplas fases e desafios. No tópico anterior de nossa jornada nós “ligamos” a gravidade em nosso game e, por meio de scripts e componentes, permitimos ao personagem pular de plataforma em plataforma para percorrer os caminhos da fase, utilizando-se para isso de algumas funções de física oferecidas pelo Unity.
Venha conosco e vamos juntos nesta trilha rumo a novos conhecimentos!
Um carro desgovernado
Em nosso último encontro implantamos a movimentação do personagem pelo cenário e as devidas colisões entre o carrinho, o chão e as plataformas. Apesar do promissor avanço, ainda temos de corrigir alguns aspectos dessa movimentação, pois em alguns momentos o carro pode ficar totalmente “desgovernado”, andando de cabeça pra baixo ou mesmo caindo para fora do cenário:
Então vamos “voltar à prancheta” para realizar essas correções, abrindo o Unity Hub e clicando duas vezes sobre o item referente ao projeto Motorista da Pesada. Na interface inicial do Unity, na aba Project, abra a pasta Assets, Scenes e, por fim, clique duas vezes no ícone da cena Fase01.
Na aba Hierarchy, selecione o objeto Personagem. Na aba Inspector, na área correspondente ao componente Rigidbody 2D, clique sobre o item Constrains. Dentre os itens que forem exibidos, deixe selecionado o de nome Freeze Position X, conforme ilustração a seguir:
Esse parâmetro do Rigidbody 2D irá “congelar” a posição X do componente Transform de Personagem, mesmo que haja uma colisão entre o objeto e outro item da fase, como uma plataforma flutuante. Assim, solucionamos o problema do personagem atravessando as laterais do cenário e sumindo.
Antes de testarmos se houve essa melhoria na movimentação, ainda em Rigidbody 2D, deixe o parâmetro Collision Detection com o valor “Continuous”, conforme ilustração a seguir. Esse parâmetro permite ao Unity saber a prioridade de detecção de colisão de um objeto, evitando eventuais problemas de um objeto ser “engolido” por outro se não houver um cálculo de colisão adequado.
Experimente simular a execução do jogo, indo à aba Game e clicando sobre o botão Play. Veja que, agora, o personagem permanece no centro do cenário:
Voar, voar... subir, subir...
Dá para perceber que, apesar de o problema do personagem sumir pelas laterais ter sido solucionado, outro tipo de “sumiço” pode ocorrer, desta vez em direção ao céu. Iremos agora solucionar esse desafio colocando um colisor transparente na parte de cima de nosso cenário.
Interrompa a simulação da execução do game e volte à aba Scene. Clique com o botão direito do mouse sobre o GameObject Itens e selecione Create Empty no menu suspenso que for exibido. Dê o nome para o novo objeto de BloqueioCima, conforme exemplo a seguir:
Selecione o GameObject BloqueioCima e, na aba Inspector, modifique o parâmetro Position Y de seu Transform para o valor 7.5. Adicione também ao objeto um componente do tipo Box Collider 2D (botão Add Component, opção Physics 2D) e conceda-lhe os valores de Size X = 15 e Y = 1, assim como ilustrado abaixo.
Experimente executar o game agora e veja que, literalmente, no céu de fato há um limite para nosso motorista maluco.
Evitando capotamentos
A maior das vantagens na utilização dos componentes e funções de física do Unity é que os cálculos de colisões, aceleração, movimentos e reações são realizados pelo própria engine do jogo; porém em alguns casos precisamos colocar “freios” nas interações físicas que são calculadas, por exemplo, para evitar situações como esta:
De fato, apesar de os cálculos de interação entre corpos terem sido realizados corretamente, seria inviável um carro andar em uma pista de ponta-cabeça ou inclinado a 90 graus. Devemos então orientar o jogo sobre o que pode ou não ocorrer, de acordo com o que consideramos desejável para a boa interação entre o personagem e o cenário. Dessa forma, iremos desenvolver scripts para limitar alguns movimentos do personagem.
Na aba Project, clique duas vezes sobre o ícone da pasta Assets e, depois, acesse a pasta Scripts. Clique com o botão direito sobre alguma área vazia da pasta e selecione a opção Create, C# Script. O nome de nosso novo script será LimiteAngulo.
Clique duas vezes sobre o ícone do script para abri-lo no Visual Studio. Após aberto, apague o conteúdo referente ao método void Start() e, antes de void Update(), declare as seguintes variáveis:
public float limite1, limite2;
private float rotZ;
Dentro dos colchetes de void Update(), insira o seguinte trecho de código:
if (transform.eulerAngles.z < (360 - transform.eulerAngles.z))
rotZ = Mathf.Clamp(transform.eulerAngles.z, 0, limite1);
else
rotZ = Mathf.Clamp(transform.eulerAngles.z, 360 + limite2, 360);
transform.eulerAngles = new Vector3(transform.eulerAngles.x, transform.eulerAngles.y, rotZ);
Nesse script estamos trabalhando com o conceito de rotação de um objeto no cenário, e vale entender melhor como o Unity trabalha esse conceito antes de continuarmos.
Sistemas de rotação no Unity
Internamente, o Unity processa cálculos envolvendo rotação utilizando transformações numéricas em uma extensão do conjunto dos números complexos conhecida como Quaternions. Se você, por exemplo, tentar editar via código os parâmetros de um componente transform.rotation, terá que trabalhar diretamente com esse sistema numérico, que é bem contraintuitivo em relação ao que costumamos utilizar em trigonometria e geometria, com ângulos e graus.
Para que possamos trabalhar de forma mais simplificada e próxima ao que utilizamos na vida real, o Unity fornece funções que permitem a leitura e a edição de informações sobre rotação utilizando informações numéricas em graus. São os ângulos de Euler (transform.eulerAngles), apresentados como um vetor tridimensional (Vector3) contendo a informação da rotação, em graus, nos eixos X, Y e Z de um objeto.
O que é exibido graficamente no editor do Unity para nós como Rotation do componente Transform, na verdade, é uma representação de transform.eulerAngles, e não de transform.rotation , como o nome poderia nos induzir a pensar.
Como há uma conversão interna de EulerAngles para Quaternion no processamento de dados internamente pelo Unity, pode haver dois ou mais valores de EulerAngles que equivalem a um mesmo valor em Quaternion. Por exemplo, se no editor do Unity você inserir para um objeto um valor de rotação Z = -360, 0 ou 720, todos representarão a mesma posição, pois, em geometria, quando se aplica uma ou mais voltas de 360 graus para um objeto, ele acaba voltando à sua posição inicial.
Sendo assim, mesmo que no editor seja apresentado, por exemplo, uma rotação de valor Z = -45, pelas conversões implícitas feitas pelo Unity a escala sempre compreenderá um valor entre 0 e 360.
No caso concreto de nosso personagem, iremos determinar que ele não deverá capotar; ou seja, a amplitude do seu movimento de rotação deverá ser definida apenas entre os valores das variáveis limite1 e limite2. Experimentalmente você pode verificar, ajustando os parâmetros no próprio editor do Unity, quais são os valores aceitáveis; vide exemplo a seguir:
Veja que, para valores de rotação do eixo Z entre 35 e -35, temos uma inclinação aceitável de nosso carrinho, então o ideal é que o valor de transform.eulerAngles.z se mantenha entre esses valores mínimo e máximo, fazendo com que o carrinho não capote.
A função do Unity que permite a uma variável obter um valor entre dois limites, e que utilizamos no código, é a Mathf.Clamp(float value, float min, float max). Porém, temos um desafio extra, que é o fato de que, internamente, a engine do Unity irá converter os valores informados para que fiquem sempre entre 0 e 360 graus. A seguir é ilustrada a conversão:
Dado isso, sabendo que no editor gráfico é mais fácil e intuitivo trabalhar com valores positivos e negativos, em nosso código realizamos o seguinte macete: se o valor corrente de transform.eulerAngles.z (já convertido internamente pelo Unity para algo entre 0 e 360) for menor do que (360-transform.eulerAngles.z), para a variável rotZ será considerado como limite máximo 360 e mínimo (360 + limite2), por limite2 se tratar de uma variável com valor negativo; já em caso contrário, para a variável rotZ será considerado como limite mínimo 0 e máximo limite1, variável cujo valor é positivo.
Dessa forma, não há problemas em se passar os valores observados no editor do Unity (positivos e negativos) como parâmetros para esse script. Ao final, transform.eulerAngles continuará com os seus valores originais de rotação para os eixos X e Y, modificando apenas o valor do eixo Z para o calculado em rotZ.
Agora, iremos salvar o script no Visual Studio e minimizá-lo para voltarmos ao editor do Unity. Selecione o objeto Personagem e, na aba Inspector, adicione o componente Limite Angulo (botão Add Component, Scripts), inserindo como parâmetros para Limite 1 = 35 e Limite 2 = -35, conforme imagem a seguir:
Teste a execução do game e veja, na prática, como um simples script, associado a uma complexa cadeia de cálculos matemáticos e conversões internas, pode facilitar a nossa vida ao limitar a rotação do objeto apenas ao desejável.
Finalize a simulação da execução. Não se esqueça de, ao final, salvar a cena (menu File, Save) e o projeto (menu File, Save Project) antes de fechar o Unity.
Próximos passos
Aos poucos, estamos tornando nosso primeiro platformer 2D cada vez mais polido, refinado e responsivo. Ao mesmo tempo, estamos aprendendo diversos aspectos interessantes, tanto em relação ao funcionamento interno da ferramenta Unity, quanto sobre métodos práticos para se resolver diferentes desafios lógicos que são apresentados.
Após passarmos por uma “saraivada” de conceitos aritméticos, trigonométricos e geométricos, não seria muito legal ganhar um belo presente? E se forem múltiplos presentes? Pois é, em nosso próximo tópico iremos povoar o cenário com belas caixinhas de presentes coletáveis, além de concluir a etapa de refinamento dos movimentos de nosso simpático colega de quatro rodas.
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