Como criar movimentação de personagem para jogos 2D na Unity

Neste tutorial, nós vamos criar a movimentação de personagens para jogos 2D top down. A movimentação que iremos criar será ideal para jogos com pouca ação, como RPGs, mas também veremos uma alternativa para jogos com mais ação, como shooters.

O tutorial será dividido em três partes, na primeira parte nós vamos ver como configurar o projeto e importar os assets necessários. Em seguida, vamos criar uma pequena cena de teste e fazer o personagem se mover e fazer ele colidir com obstáculos no cenário.

Na segunda parte deste tutorial, vamos fazer a câmera seguir o personagem, e depois vamos modificar a câmera para que ela fique mais flexível. E na terceira e última parte vamos adicionar animações ao personagem.

Os assets que usei estão disponíveis aqui, mas eles não são necessários e você pode usar o que você quiser.

O projeto final está disponível no GitHub.

Criando um novo projeto

Para este projeto vamos usar Unity 2021.3.4, a versão mais recente no momento que este tutorial foi escrito. Como não faremos nada que seja possível apenas nesta versão, o conteúdo deste tutorial deve funcionar sem problemas em outras versões. Sendo assim, você pode usar a versão que você preferir. Mas caso encontre algum problema fique a vontade para falar comigo.

Ao abrir o Unity Hub clique no botão “New project”. 

Aqui o Unity Hub oferece alguns templates, nós vamos usar o 2D Core, você pode usar o template que achar melhor. Porém, recomendo usar um template 2D, já que assim não teremos que nos preocupar em configurar o projeto para jogos em 2D após criado.

Por fim, escolha o nome do projeto e a pasta em que você quer salvar o projeto e clique no botão “Create project”.

Importando os assets

Com o projeto aberto, vamos importar alguns assets que serão usados nesta parte do tutorial. Eu disponibilizei alguns assets que você pode baixar no início do tutorial, ou se preferir, usar os seus próprios assets.

Vamos criar uma pasta para importar nossos sprites. 

Na janela “Project”, clique com o botão direito sobre “Assets” e no menu que aparecer selecione “Create > Folder”. Vamos nomear essa pasta “Sprites”.

Após criar a pasta clique duas vezes sobre ela.

Para importar os sprites que serão usados para fazer o cenário. Selecione as imagens no seu sistema operacional e arraste para a Unity e solte dentro da janela “Project”.

Configurando os sprites

Agora que importamos os sprites vamos configurá-los. Selecione um dos sprites do personagem. Mude as propriedades:

  • “Texture Type” para “Sprite (2D and UI)
  • “Sprite Mode” para “Single”
  • “Pixel Per Unit” para 16
  • “Filter Mode” para “Point (no filter)”
  • “Compression” para “None”

Clique no botão Apply. 

Se você não quiser fazer todo esse processo manualmente, você pode conferir esse post em que ensino como criar Presets na Unity.

Repita o processo para todos os sprites.

Montado a cena

Agora que importamos os assets que usaremos, vamos criar um pequeno cenário para podermos testar a movimentação do personagem. Essa etapa não é necessária para o resultado final, mas vai nos ajudar a visualizar o personagem se movendo.

Na janela “Hierarchy”, clique com o botão direito e no menu que se abriu, clique em “Create Empty”, isto vai fazer com a Unity crie um novo GameObject apenas com “Transform” como componente. Nomeie como “Environment”. Esse game object será usado para guardar o cenário de fundo.

Para criar o que será o chão, clique e arraste um dos sprites do cenário, eu vou usar o azul. Solte ele em cima do GameObject que nomeamos “Environment”, a Unity vai criar um novo GameObject com o sprite como filho de “Environment”.

Agora clique com o botão direito em cima do GameObject com o sprite e selecione “Duplicate”. Distribua de forma a criar algo parecido com a imagem abaixo. Para acelerar o processo você pode selecionar vários GameObjects e duplicar com Ctrl + d no windows.

Criando obstáculos

Vamos aproveitar que já estamos aqui e vamos criar alguns obstáculos para o nosso personagem.

Na janela “Hierarchy”, clique com o botão direito e no menu clique em “Create Empty”, para criar um novo GameObject. Nomeie como “Obstacle”.

Arraste o sprite que será usado como gráfico do obstáculo e solte em cima do GameObject que criamos. 

Duplique ele algumas vezes para fazer algo parecido com a imagem abaixo. Ou use sua criatividade para criar algo mais interessante.

Selecione o GameObject “Obstacle”, duplique ele algumas vezes e distribua ele pelo seu cenário.

Movendo o personagem

Finalmente chegamos na parte interessante. Vamos começar a mover o personagem. Mas antes de criarmos nosso primeiro script vamos criar um GameObject para representar o personagem.

Clique com o botão direito na janela “Hierarchy”, no menu que se abriu, selecione “Create Empty”, para criar um GameObject. Nomeie como “Player”.

Em seguida, clique com o botão direito em cima desse GameObject, no menu que se abriu, selecione “2D Object > Sprites > Square”. Isso vai criar um GameObject como filho do GameObject “Player” e com o componente “Sprite Renderer” e um sprite quadrado branco. Por enquanto vamos usar esse sprite para testar, na terceira parte do tutorial vamos alterar para o sprite do personagem.

No Sprite Renderer altere a propriedade “Order in Layer” para 1. Isso fará com que o sprite seja renderizado acima de outros sprites.

Agora, selecione o GameObject “Player” e na janela “Inspector” clique no botão “Add Component”. Na barra de pesquisa procure por “Rigidbody 2D”, clique no nome do componente quando encontrar.

Altere a propriedade “Gravity Scale” para 0. Dessa forma esse GameObject não será afetado pela gravidade, e só se moverá quando mandarmos.

Em seguida, clique na propriedade “Constraints” para expandir as opções. Marque o checkbox “Free Rotation”. Isso vai evitar que o personagem comece a rodopiar quando colidir com a ponta de um obstáculo.

Criando o script

Para mantermos o projeto organizado, vamos criar uma pasta para colocarmos nossos scripts. Nomeie a pasta como “Scripts”.

Após criar a pasta, clique com o botão direito na janela “Project” e selecione “Create > C# Script”. Nomeiei o script como “PlayerController”.

Em seguida, selecione o GameObject “Player” e na janela “Inspector”, você clique no botão “Add Component” e na barra de pesquisa procure pelo nome do script. No nosso caso “PlayerController”.

Uma outra maneira de adicionar scripts a GameObject é selecionar o GameObject no qual você quer adicionar o script e em seguida clicar e arrastar o script para a janela “Inspector” como na imagem abaixo.

Agora que criamos o script e adicionamos ele ao GameObject que representa o personagem, abra ele no seu editor de texto. Para isso dê dois cliques em cima do script.

Digite o seguinte código:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerController : MonoBehaviour
{
    [SerializeField]
    private float speed = 3f;

    private Rigidbody2D rb;
    private Vector2 moveDirection;

    private void Awake()
    {
        rb = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        float horizontal = Input.GetAxisRaw("Horizontal");
        float vertical = Input.GetAxisRaw("Vertical");

        moveDirection = new Vector2(horizontal, vertical);
    }

    private void FixedUpdate()
    {
        Vector3 movePosition = (speed * Time.fixedDeltaTime * moveDirection.normalized) + rb.position;

        rb.MovePosition(movePosition);
    }
}

Volte para a Unity e pressione o botão para entrar no “Play Mode”.

Aperte as teclas WASD ou as setas no teclado e você verá o personagem se mover na tela.

Vamos entender o que o código está fazendo.

Explicando o código

Primeiro nós declaramos a variável speed que será usada para controlar a velocidade com que o personagem se move. Nós também marcamos ela com o atributo SerializeField, isso faz com que possamos editar ela diretamente no editor sem ter que modificar o nosso código.

[SerializeField]
private float speed = 3f;

Nas linhas seguintes declaramos as variáveis “rb” e “moveDirection”. 

private Rigidbody2D rb;
private Vector2 moveDirection;

A variável rb vai ser usada para guardar uma referência ao componente “Rigidbody2D” que nós adicionamos ao GameObject “Player”. Mais tarde essa referência vai ser usada para mover o personagem.

Já a variável moveDirection vai ser usada para guardar a direção que o jogador quer que o personagem se mova.

No método Awake, nós inicializamos a variável rb usando GetComponent. Esse método fornecido pela Unity nos permite encontrar um componente que foi adicionado ao GameObject que esse script foi adicionado.

rb = GetComponent<Rigidbody2D>();

No método Update, que é chamado todo frame pela Unity, é o que nos permite capturar quando o jogador pressiona um botão no teclado. Declaramos duas variáveis, horizontal e vertical, elas guardam o valor retornado pelo método Input.GetAxisRaw

float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");

GetAxisRaw é outro método fornecido pela Unity e retorna valores que permitem saber se o jogador está se movendo para a esquerda, -1, para a direita, 1, ou parado, 0, no caso do “Horizontal”. E no caso “Vertical”, para baixo, -1, para cima, 1, ou parado, 0. 

Em seguida, atribuímos esses valores a variável moveDirection como um Vector2, que é a forma como a Unity representa entre outras coisas, direção.

moveDirection = new Vector2(horizontal, vertical);

Por fim, no método FixedUpdate, que é chamado pela Unity toda vez que ela calcula o sistema de física. Nós declaramos uma variável movePosition, que usamos para calcular a próxima posição que o personagem deve se mover.

Para fazer esse cálculo, primeiro multiplicamos a velocidade com que queremos que o personagem se mova com Time.fixedDeltaTime e com a direção. Por fim, adicionamos a posição atual do personagem. Com isso, temo a próxima posição.

Vector3 movePosition = (speed * Time.fixedDeltaTime * moveDirection.normalized) + rb.position;

Alias, Time.fixedDeltaTime, é outro valor fornecido pela Unity e representa o intervalo com que a física é calculada. Aqui, garante que o personagem se mova de forma constante independente dos quadros por segundo.

E finalmente movemos o personagem com MovePosition, que é um método que está disponível no Rigidbody2D que pegamos a referência mais cedo. Passamos a posição que calculamos na linha anterior e o sistema de física vai mover o GameObject e vai fazer as checagens necessárias para colisões.

rb.MovePosition(movePosition);

Colidindo com objetos

Se você ainda não testou o jogo, vá em frente e teste. Clique no botão para entrar no “Play Mode”. Mova o personagem com WASD ou com as setas.

Ao mover o personagem você vai notar que ele não é parado pelos obstáculos que colocamos no cenário. Isso acontece porque ainda não adicionamos colisores ao personagem nem nos obstáculos, sem isso a Unity não sabe que eles devem colidir.

Selecione um obstáculo, e na janela “Inspector” clique no botão “Add Component”. Em seguida, use a barra de pesquisa para procurar por “Box Collider 2D”, clique no nome do componente.

Se você der zoom no obstáculo no editor, você vai perceber um fino contorno verde. Esse contorno representa a área em que a Unity fará cálculos de colisão e deve se encaixar da melhor forma possível com o gráfico do obstáculo.

Para isso ajuste as propriedades “Offset” e “Size” até que o contorno cubra o obstáculo de forma satisfatória. Você também pode adicionar múltiplos “Box Collider 2D” a um GameObject caso necessário.

Se seus obstáculos forem iguais ao deste tutorial eles devem ficar parecidos com a imagem abaixo.

Agora, repita essas etapas para todos os obstáculos e para o personagem.

Vá em frente e teste. Clique no botão para entrar no “Play Mode”. Mova o personagem com WASD ou com as setas. E agora ele não deve mais conseguir passar por cima dos obstáculos.

Problemas comuns com colisões

Para colisões funcionarem corretamente, além dos vários tipos de “Collider 2D” que a Unity oferece, um dos GameObjects que estão colidindo em jogos 2D precisa ter o componente “Rigidbody 2D” do tipo “Dynamic”. Pois, a Unity não calcula colisões para objetos sem “Rigidbody” nem para “Rigidbody” dos tipos “Kinematic” e “Static”.

No nosso caso apenas o personagem precisa do componente “Rigidbody 2D”.  E nós já adicionamos o componente, pois usamos ele para fazer o personagem se mover.

Por padrão, quando adicionamos um “Rigidbody 2D” a um GameObject ele é do tipo “Dynamic”, mas se o personagem estiver passando pelos obstáculos, confira se a propriedade “Body Type” está correta, se não altere ela.

Outro problema que pode acontecer caso o “Collider” seja muito fino ou o “Rigidbody” estiver se movendo muito rápido é que existe a possibilidade que a Unity não calcule a colisão corretamente. Como no exemplo abaixo.

Para evitar, selecione o GameObject com o “Rigidbody 2D” e mude a propriedade “Collision Detection” para “Continous”. Porém, esse tipo de detecção de colisões requer mais recursos, então use apenas quando necessário.

Conclusão

Chegamos ao final da primeira parte desta série de tutoriais. Nela, criamos a movimentação do personagem. Aprendemos quais são os requisitos para mover o personagem e entendemos como funciona o código para fazer isso acontecer.

Porém, você vai notar dois problemas, a câmera não segue o personagem e ele é apenas um quadrado branco. Esses dois problemas serão resolvidos nas próximas etapas. Mais precisamente, na segunda parte, fazer com que a câmera siga o personagem. E na terceira e última parte iremos adicionar animações.

Não deixe de conferir. Até a próxima!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *