2 - Movimentação Point and Click + new input system


Antes de começar a fazer a primeira mecânica do jogo, a movimentação do personagem, anotei pontos decisivos de como eu queria que fosse o resultado final. Decidir esses pontos é importante para pensar na lógica do script e saber por qual ponto começar.

Os principais pontos envolvendo a movimentação do personagem que eu quero no meu jogo são:

  1. movimentação em point and click usando o novo input system da unity2D;
  2. poder clicar apenas em objetos escolhidos para ser o chão;
  3. o personagem poder passar pela frente e atrás do mesmo objeto e sua imagem se corrigir de acordo com a perspectiva isométrica;
  4. fazer o personagem desviar sozinho de qualquer obstáculo que esteja no caminho.


1. Após baixar o pacote do input system e criar uma pasta no Assets para as coisas relacionadas ao player, criei um input action chamado "Player" e configurei da seguinte forma:

Action Map "Player" -> Action "Move", com Action Type como um "Button" -> binding sendo o botão esquerdo do mouse.

obs: recomendo ativar o Auto-Save.

Inicialmente usei o input referenciando ele dentro do script, como no exemplo abaixo:

MouseInput input;
void Awake(){ 
    input = new MouseInput();
}
void Start(){  
    input.Player.Move.performed += _ => Metodo();
}

Mas resolvi fazer de outra forma (para deixar o script mais "limpo"), criando no player um componente chamado "Player Input":

obs: colocar as informações conforme o input actionaction map escolhidos; alterar o Behaviour para Invoke Unity Events; colocar o script e o método a ser chamado.

E no script apenas referenciar o uso do input system e fazer o método no seguinte padrão:

using UnityEngine.InputSystem;
. . .
public void OnClick(InputAction.CallbackContext ctx){        
    if (ctx.performed)
        //resto do metodo...   
}

Antes de colocar mais coisas no script de movimento, é bom revisar se o player tem um Rigibody2D e um Collider2D. Como o jogo tem perspectiva isométrica (parecido com um jogo top-down), é necessário colocar o Gravity Scale em 0 e marcar o Freeze Rotation.


A lógica do script de movimento se resume em:

  • criar um float da velocidade que de para alterar no inspector; criar um Vector2 para o destino; e criar um bool para o movimento;
[SerializedField] private float speed;
private Vector2 destination;
private bool isMoving = false;
void Start(){
    isMoving = false; 
}
  • no método chamado depois de clicar: salvar a posição do mouse; definir o destino como a posição do mouse; bool isMoving = true;
if (ctx.performed){
    Vector2 mousePosition = Mouse.current.position.ReadValue();
    Vector2 worldPosition = Camera.main.ScreenToWorldPoint(mousePosition);
    destination = worldPosition;
    isMoving = true;
}
  • no Update: mover a posição do player até o destino usando o MoveTowards; parar quando a posição for igual a do destino;
 if (isMoving){
    transform.position = Vector2.MoveTowards(transform.position, destination, speed * Time.deltaTime); 
    if ((Vector2)transform.position == destination)
        isMoving = false;
}


2. Criei uma layer "Ground" para colocar em todos os objetos considerados como chão; é usado o RaycastHit2D para verificar se essa layer foi clicada. O RaycastHit2D funciona verificando se o click "colidiu" em algo com a layer Ground, mas as outras variáveis usadas, direction e distance, são colocadas como um numero "zerado", pois nesse caso em especifico não são necessárias para a verificação.

[SerializeField] private LayerMask groundLayer;
. . .
public void OnClick(InputAction.CallbackContext ctx){
    if (ctx.performed){
        Vector2 mousePosition = Mouse.current.position.ReadValue();
        Vector2 worldPosition = Camera.main.ScreenToWorldPoint(mousePosition);
        RaycastHit2D hitGround = Physics2D.Raycast(worldPosition, Vector2.zero, Mathf.Infinity, groundLayer);
        if (hitGround.collider != null){
            destination = hitGround.point;
            isMoving = true;
. . .

No inspector: no script dentro do player, é necessário atribuir a layer que seria a "groundLayer"; nos objetos considerados "chão", colocar a layer correta e um Collider2D.


3. Existe várias formas de lidar com a perspectiva e ordem dos sprites, mas resolvi escolher a forma mais fácil sem precisar fazer um script:

  • Primeiro criei 2 Sorting Layers: uma para objetos que sempre ficão atras do jogador, "BackLayer"; e uma para objetos que o player poderá passar atrás e na frente,  "MiddleLayer";
  • Organizei os objetos da hierarquia em pastas diferentes, conforme cada Sorting Layer;
  • O player deverá receber a Sorting Layer "MiddleLayer", mas ficará fora da pasta dos objetos.

Para que os objetos da MiddleLayer se arrumem de acordo com a perspectiva isométrica:

Edit -> Project Settings -> Graphics -> mudar o Transparency Sort Mode para Custom Axis -> mudar o Transparency Sort Axis para 0, 1, 0.


Última coisa importante para revisar é a posição do Collider2D e do Pivot da imagem, pois em um jogo isométrico estes precisam estar no "pé" do player;  e também é necessário fazer essas mudanças nos objetos em cena. Para alterar o Pivot da imagem:

Acessar o Import Settings, clicando na imagem que está em alguma pasta no Assets -> alterar o Pivot para Custom -> alterar o local do Pivot -> clicar em Apply -> no componente Sprite Renderer, alterar o Sprite Sort Point para Pivot

O próximo devlog será focado em explicar como fazer o personagem desviar sozinho de obstáculos.

Leave a comment

Log in with itch.io to leave a comment.