[POO] Herança na Unity

Herança é um conceito do paradigma de programação conhecido como Programação Orientada a Objetos , que tem como objetivo construir e trabalhar com abstrações de objetos do mundo real, em um nível de código. A Herança dá a capacidade de uma classe/objeto herdar comportamentos previamente definidos em outra classe, sem a necessidade de duplicação do código, permitindo a extensão e a sobrescrita de comportamentos já existentes, de acordo com a necessidade do novo objeto/classe. Para definir uma relação de herança no C#, a "palavra" reservada : (dois pontos) deve ser utilizada na definição da classe que estamos criando, indicando de qual outra classe o nosso script deve herdar. public class Jogador : MonoBehaviour { // Classe Jogador que herda da classe MonoBehaviour } Por padrão, todos os scripts criados na Unity herdam de MonoBehaviour, que é a classe base da Unity para Scripts que serão associados aos GameObjects do jogo. Embora essa definição de herança

Câmera seguindo o jogador - 2D

Câmera seguindo o jogador em um mundo 2D

Uma das funcionalidades mais comuns em jogos digitais, principalmente quando falamos de jogos plataforma ou de movimentação lateral (side scrolling), é a movimentação da câmera seguindo o jogador. E já que essa é uma funcionalidade tão comum, vamos falar um pouco sobre ela e ver como podemos implementá-la de forma fácil em nossos jogos.

Câmera seguindo o jogador durante a movimentação
Câmera seguindo o jogador durante a movimentação

Neste exemplo vamos utilizar o conjunto de Assets chamado A platformer in the forest, desenvolvido por Buch e disponível no site OpenGameArt.Org.

Pronto para começar? Então, vamos lá!

Vamos iniciar a implementação da nossa lógica de movimentação da câmera criando uma classe, chamada de ControladorCamera, que será responsável por identificar a posição atual do jogador e movimentar a câmera em direção a ele.

Agora que sabemos o que precisa ser feito, precisamos entender como fazer. Afinal, como podemos mover a câmera em direção ao jogador?

Lerp

Uma das formas mais simples de realizarmos essa movimentação da câmera é utilizar o método Lerp para calcular a nova posição da câmera (para onde a câmera deve mover) a cada frame do jogo.

Hmm...mas o que é o Lerp?
Lerp é como é chamado o método de Interpolação Linear na Unity.
...e o que é a Interpolação Linear?
Podemos descrever a Interpolação Linear como uma forma de encontrar posições intermediárias entre dois pontos, ou seja, a partir de uma posição de origem (posição atual da câmera) e uma posição de destino (posição atual do jogador), nós conseguimos determinar posições intermediárias por onde meu objeto (câmera) deve passar, até chegar ao destino (seguir o jogador). Ficou mais claro?!

Legal! E como o Lerp funciona?
O método Lerp precisa de três parâmetros para ser executado:
  1. Posição de origem
  2. Posição de destino
  3. Passo
No nosso exemplo, a posição de origem é a posição atual da câmera (onde ela está no frame atual do jogo), a posição de destino é a posição para onde queremos mover a câmera, ou seja, a posição atual do jogador (onde o jogador está no frame atual do jogo) e o passo representa o quanto nós queremos mover (interpolar) entre a posição de origem e a posição de destino. O passo pode ser utilizado para definir a velocidade de movimentação da câmera enquanto ela segue o jogador.

Vamos tentar tornar esse entendimento um pouco mais visual, olhando para o exemplo abaixo:

Simulação do Lerp da câmera
Simulação do Lerp da câmera

A bola vermelha (movendo a partir da câmera) representa a posição de origem (posição da câmera) durante cada etapa da movimentação. A bola verde representa a posição de destino (a posição do jogador) e a bolinha (tamanho menor) azul representa a próxima posição da câmera calculada pelo método Lerp. Se reproduzirmos o mesmo exemplo, mas desta vez também executando a movimentação da câmera, o resultado é o seguinte: 

Simulação do Lerp da câmera com a câmera em movimento
Simulação do Lerp da câmera com a câmera em movimento

Podemos expandir o exemplo para visualizarmos a movimentação da câmera e a representação visual do cálculo da próxima posição do Lerp enquanto movimentamos o jogador, conforme pode ser visto abaixo, onde na direita temos a cena (scene) e na esquerda o jogo em execução:

Simulação do Lerp da câmera com movimentação do jogador
Simulação do Lerp da câmera com movimentação do jogador

Obs: No exemplo exposto anteriormente, a câmera está propositalmente com um delay de atualização da movimentação, para facilitar a visualização e o entendimento do Lerp.

Agora que estamos entendidos, vamos começar a nossa implementação. Primeiro, precisamos criar as variáveis que serão utilizadas no cálculo do Lerp (posição de origem, posição de destino e passos), que no nosso caso são respectivamente: posição da câmera, posição do jogador e velocidade de movimentação.
O nosso script ControladorCamera está sendo construído para ser associado com o GameObject da câmera, dessa forma, nós poderemos identificar a posição atual da câmera e fazer alterações nela sem precisar criar uma nova variável, já que utilizaremos a posição do próprio GameObject através da propriedade position (this.transform.position). Sendo assim, precisamos criar apenas duas novas variáveis: jogador e velocidadeMovimentacao.

    /// <summary>
    /// Transform do jogador que será seguido pela câmera
    /// </summary>
    [SerializeField]
    private Transform jogador;

    /// <summary>
    /// Velocidade de movimentação da câmera para seguir o jogador
    /// </summary>
    [SerializeField]
    private float velocidadeMovimentacao;

A variável jogador foi declara com o tipo transform para que possamos associar o Transform do jogador (na Hierarquia da Unity) com a nossa variável, permitindo a consulta da posição atual do jogador sempre que necessário. Qualquer transform que for associado com a variável será utilizado como objeto alvo da movimentação da câmera.
A variável velocidadeMovimentacao será utilizada como o passo do Lerp, ou seja, o quanto nós queremos mover a câmera a cada frame do jogo. Utilizando essas duas variáveis, o Lerp pode ser utilizado da seguinte forma:

        Vector3 posicaoFinal = Vector3.Lerp(
            this.transform.position, // Posição de origem
            this.jogador.position, // Posição de destino
            (this.velocidadeMovimentacao * Time.deltaTime) // Passo
        );

Em seguida, basta utilizar o Lerp  e atualizar a posição da câmera periodicamente e tudo estará resolvido...certo?

    private void FixedUpdate() {
        Vector3 posicaoFinal = Vector3.Lerp(
            this.transform.position, // Posição de origem
            this.jogador.position, // Posição de destino
            (this.velocidadeMovimentacao * Time.deltaTime) // Passo
        );
        // Move a câmera para a nova posição
        this.transform.position = posicaoFinal;
    }

Antes de testarmos, precisamos configurar o nosso ControladorCamera, no Inspector. Basta adicionarmos o script ControladorCamera no GameObject da câmera e em seguida selecionar o objeto que será seguido pela câmera (jogador) e posteriormente a velocidadeMovimentacao de acordo com o comportamente desejado pela câmera.

Configuração do ControladorCamera no Inspector
Configuração do ControladorCamera no Inspector

Ops...algo errado não está certo. O jogo sumiu!

Testando o a movimentação da câmera com Lerp...e com erro
Testando o a movimentação da câmera com Lerp...e com erro

Sim! O jogo sumiu. Mas o que de fato está acontecendo?
Se olharmos para atenção para o transform da câmera durante a execução do Lerp podemos perceber que a posição z da câmera mudou de -10 para um valor bem próximo de 0. Você sabe por que isso acontece?

Alteração do eixo Z da câmera durante o Lerp
Alteração do eixo Z da câmera durante o Lerp

Bom, lembra que o Lerp faz uma interpolação entre duas posições? No nosso exemplo, essa alteração do eixo z pode ser considerada um efeito colateral do Lerp, já que o jogador (posição de destino) está localizado na posição 0 no eixo z, dessa forma, durante o Lerp, a câmera tende a mover-se em direção ao valor 0 no eixo z, para ser aproximar da posição do jogador.

Entendi! Mas temos como corrigir isso?
Sim! E é bem simples. Precisamos apenas alterar nosso código para manter a câmera na mesma posição z que está estava antes de executar o Lerp. Isso pode ser feito adicionando o código abaixo antes de alterarmos a posição da câmera:

    // Mantém a câmera na mesma posição Z, para continuar
    // visualizando os objetos do jogo
    posicaoFinal.z = this.transform.position.z;

Com essa alteração, o nosso FixedUpdate ficará da seguinte forma:

    private void FixedUpdate() {
        Vector3 posicaoFinal = Vector3.Lerp(
            this.transform.position, // Posição de origem
            this.jogador.position, // Posição de destino
            (this.velocidadeMovimentacao * Time.deltaTime) // Passo
        );

        // Mantém a câmera na mesma posição Z, para continuar
        // visualizando os objetos do jogo
        posicaoFinal.z = this.transform.position.z;
        // Move a câmera para a nova posição
        this.transform.position = posicaoFinal;
    }

O resultado final é a câmera seguindo o jogador enquanto ele se move nos eixos x (para os lados) e y (para cima e para baixo):

Movimentação da câmera seguindo o jogador
Movimentação da câmera seguindo o jogador

O script completo da classe ControladorCamera pode ser visto abaixo:

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


public class ControladorCamera : MonoBehaviour
{

    /// <summary>
    /// Transform do jogador que será seguido pela câmera
    /// </summary>
    [SerializeField]
    private Transform jogador;
    
    /// <summary>
    /// Velocidade de movimentação da câmera para seguir o jogador
    /// </summary>
    [SerializeField]
    private float velocidadeMovimentacao;



    private void FixedUpdate() {
        Vector3 posicaoFinal = Vector3.Lerp(
            this.transform.position, // Posição de origem
            this.jogador.position, // Posição de destino
            (this.velocidadeMovimentacao * Time.deltaTime) // Passo
        );

        // Mantém a câmera na mesma posição Z, para continuar
        // visualizando os objetos do jogo
        posicaoFinal.z = this.transform.position.z;
        // Move a câmera para a nova posição
        this.transform.position = posicaoFinal;
    }

}

O projeto completo, desenvolvido para este post, pode ser baixado aqui.

Gostou? Deixa o seu comentário com dúvidas e sugestões. Até mais! o/

Comentários

Postagens mais visitadas deste blog

Transição de Fases (navegação entre Cenas) na Unity

GetComponent - Obtendo a referência para outros componentes do GameObject

[POO] Herança na Unity