Tutorial Snake 3D usando Unity - Parte 3

Tutorial Snake 3D usando Unity - Parte 3

Iai galerinha, tudo tranquilo com vocês? Essa foi uma semana de muita correria, muita coisa aconteceu na minha vida, por isso peço desculpas se não consegui manter o horário na postagem dessa semana. Estou trabalhando arduamente para trazer mais novidades ao universo GameDev na minha cidade e região :D

Mas deixa eu te falar sobre o que vamos aprender aqui hoje. Hoje vamos focar em movimentar a cobra ao redor do nosso mapa e poder coletar a fruta. Basicamente:

  • Movimento nas 4 direções
  • Coletar a fruta que aparece aleatoriamente pelo mapa

1. Movimentação

Pra gente conseguir fazer a serpente se mexer só vai ser preciso que a gente altere os valores da posição dela e continue incrementando esse valor com o tempo. Não vai ser tão complicado. Para isso vamos verificar o estado do nosso componente e vamos ver como podemos inserir um código que possa validar o movimento da nossa cobra no comportamento esperado.

Neste momento, nós temos feito apenas o espaço em que a cobra vai percorrer e o script que vai aumentar o tamanho dela. O que precisamos fazer agora é:

  1. Movimentar a cobra
  2. Alterar sua direção

Agora, vamos analisar a função Update do script Snake que já está atrelado na cobra. Esta é a função responsável por rodar código em todos os frames, logo seria um bom local para adicionar movimento já que a cobra sempre vai estar se movimentando. Vamos adicionar o seguinte trecho de código.

public class SnakeComponent : MonoBehaviour 
{
    float speed = 1f; 
    void Update()
    {
      transform.position = transform.position + Vector3.right * speed;
    }
}

Este trecho faz com que a posição da nossa cobra mude com o tempo. Explicando esse trecho de código:

  1. Criamos uma variável chamada speed para poder controlar a velocidade da cobra;
  2. transform.position é o valor do transform do objeto atual, e assim acessamos sua variável position para modificar seu valor;
  3. Vector3.right é uma classe da Unity que nos permite acessar um vetor de 3 posições que tem seu valor calculado como vetor direita (0, 0, 1) nos eixos;
  4. A linha dentro do Update nada mais é que adicionar à posição atual o valor da posição e uma passo pra direita de acordo com a velocidade que a cobra vai percorrer.

E claro, pra podermos enxergar o objeto se mexendo pela tela, vamos adicionar um elemento 3D apenas visual. Lembre-se que podemos alterar isso no futuro. Vou aproveitar e adicionar um novo material nele. Seleciono uma cor e arrasto pro objeto. Podemos fazer com qualquer objeto para dar uma visualização diferente na cena final.

Gif 1 - Alterando o gráfico da cobra

Agora vamos testar se o script de movimento está certo mesmo! Dê play na simulação e vamos ver o resultado.

Gif 2 - Exibindo movimento pra direita

Agora a cobra está super rápida! Ela mal aparece na visualização da nossa câmera. E ela só se movimenta pra direita... Não é bem isso que nós queremos. Vamos alterar esse trecho de código pra poder movimentar ela mais lentamente e em outras direções.

Nesse trecho de código acontece que o Update roda uma quantidade de vezes multiplicada pelo frame rate da sua máquina. Se você tem uma máquina que está rodando esta simulação a alguns belos 180 frames per second (fps) se comparado com outra máquina a meros 90 fps, então este no primeiro computador a velocidade será dobrado pois 180 é o dobro de 90. Este cálculo se aplica a qualquer situação de movimento.

Pra gente poder melhorar este trecho de código, vamos adicionar uma variável chamada Time.deltaTime que tem o valor exato do tempo que durou desde o momento do último frame.

public class SnakeComponent : MonoBehaviour 
{
    float speed = 100f;
    
    void Update()
    {
      transform.position = transform.position + Vector3.right * speed * Time.deltaTime;
    }
}
Gif 3 - Ajustando velocidade

Show, já temos uma velocidade aceitável. Agora só precisamos ajustar a direção do movimento. Vamos adicionar mais algumas linhas de código para poder termos esse controle.


public class SnakeComponent: MonoBehaviour
{
    float speed = 2f;
    Vector3 direction = Vector.right;

    // Update is called once per frame
    void Update()
    {
        if (Input.GetAxisRaw("Horizontal") == 1) direction = Vector3.right;
        if (Input.GetAxisRaw("Horizontal") == -1) direction = Vector3.left;
        if (Input.GetAxisRaw("Vertical") == 1) direction = Vector3.forward;
        if (Input.GetAxisRaw("Vertical") == -1) direction = Vector3.back;
        transform.position += direction * speed * Time.deltaTime;
    }
}

Agora, explicando cada parte adicionada. Essa nova variável "Vector3 direction" é a variável que vai controlar a última direção que a cobra recebeu o movimento, e a parte que tem "Input.GetAxisRaw" é o controle de entrada que verifica o valor de um eixo, de forma 'crua' sendo valores 1, 0 ou -1, cada valor indicado pela direção horizontal ou vertical do controlador. Verificamos se o valor é positivo ou negativo e por fim atribuímos direções ao movimento. Pronto. Agora já podemos testar e ver se nosso movimento está funcionando bem.

Gif 4 - Testando movimento nas 4 direções

Agora, só nos falta fazer o tick de movimento, mas vamos aprender a fazer isso em outro tutorial. O que é o tick de movimento? Bem, você já percebeu que ao movimentar a cobrinha neste jogo ela meio que espera uma posição exata pra poder fazer a curva? Pois é, isso acontece porque o movimento dela acontece de tempos em tempos, ou em tiques de tempos iguais. Vamos aprender a fazer este movimento cronometrado aplicando um código intermediário, por isso vai ficar mais pra frente.

2. Fruta

Agora pessoal, vamos fazer a frutinha. Basicamente, nós podemos usar qualquer modelo 3D pra representá-la. Vamos criar um novo objeto 3D pra representar nossa fruta. Eu vou escolher uma esfera. Vou aproveitar e criar um material e adicionar logo, pra gente poder visualizar umas cores bacanas.

Gif 5 - Ajustando o placeholder da fruta

Agora, nós iremos criar o script da fruta. O código dela vai ser relativamente simples. Nós vamos pegar o tamanho máximo do cenário nos dois eixos e vamos adicionar o seguinte trecho de código:


public class FruitManager: MonoBehaviour
{
    public int width;
    public int height;

    public Transform fruit;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            ChangePosition();
        }
    }

    void ChangePosition()
    {
        Vector3 newPos = Vector3.right * Random.Range(-width, width+1) + Vector3.forward * Random.Range(-heigth, height+1);
        fruit.position = newPos;
        // TODO: add points
    }
}

Show de bola! Agora temos um script para mudar a posição da fruta. Pra gente poder testar isso, só precisamos anexar o script em um novo elemento que vai cuidar de controlar a fruta pra gente. Por que não adicionamos direto na fruta? Bem, é uma boa prática criarmos objetos que são controladores de eventos e não deixar este controlador no mesmo objeto que vai ser controlado pelo evento, para evitar comportamentos indesejados. No nosso caso, vamos criar um novo objeto vazio, chamar de "FruitManager" e anexar o script "FruitManager" nele. Vendo assim parece até óbvio de fazer esse tipo de coisa.

Gif 6 - Criação do Manager

Não esqueça de arrastar o objeto da fruta para o espaço na variável no componente do "FruitManager". Só falta a gente testar ver se está tudo funcionando corretamente. Vamos entrar no modo simulação e tã-dã!

Gif 7 - Estado atual

Eu precisei ajustar um pouco a rotação da câmera para poder mostrar a fruta em alguns ângulos próximos da parede mais abaixo. E parece que precisamos modificar a altura do objeto para que ele não fique atravessando o chão com altura fixada pelo novo Vector3.

void ChangePosition()
{
    Vector3 newPos = Vector3.right * Random.Range(-width, width+1) + Vector3.forward * Random.Range(-height, height+1) + Vector3.up * fruit.position.y;
    fruit.position = newPos;
    // TODO: add points
}

Pronto, isso deve resolver. Lembre de fazer suas próprias alterações caso esteja usando algum modelo diferente do nosso placeholder. Nossa, mais uma palavra no nosso dicionário gamedev. Placeholder quer dizer que um elemento esta ali apenas temporariamente representando um outro elemento final, imagine apenas que este modelo é temporário até que o final seja colocado em sua devida posição.

Conclusão

É isto pessoal. Acho que este pequeno avanço faz com que o jogo comece a ganhar suas primeiras proporções. Fiquem ligados na próxima semana, pois vamos ver os primeiros conceitos de programação sendo aplicados no gerenciamento dos objetos da cena.

Nota

Peço desculpas pelo atraso na postagem. No momento estou reajustando minha rotina com novos trabalhos, agradeço a compreensão de todos. As próximas postagens terão seu horário e dias reservados às sextas-feiras 18h.