[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 seja feita automaticamente pela Unity, nada impede que ela seja alterada por nós, para construirmos a nossa própria estrutura de herança/hierarquia de classes.

public class Jogador : Personagem {

    // Classe jogador herdando de outra classe, chamada Personagem  

}

Neste exemplo, o nosso jogador herda de uma outra classe, também nossa, chamada de Personagem. A classe Personagem possui as definições básicas que são comuns a todos os personagens, como por exemplo os atributos (variáveis) e os comportamentos (métodos). O código abaixo mostra um exemplo de como poderia ser essa classe Personagem.

public class Personagem : MonoBehaviour {

    public float velocidadeMovimento;
    public float forcaAtaque;


    public void Mover() {
        // Executa a lógica de movimentação
    }

    public void Atacar() {
        // Executa a lógica de ataque
    }

}

A partir do momento em que a nossa classe Jogador herda de Personagem, todos os comportamentos de movimentação e ataque, já implementados na classe Personagem, podem ser utilizados na classe Jogador e por objetos/GameObjects que possuem o script Jogador associado.

public class GameManager : MonoBehaviour {

    public Jogador jogador;


    public void Update() {
        // Move o jogador a cada frame, utilizando o método Mover()
        // definido na classe Personagem
        this.jogador.Mover();
    }

}

Além de reutilizar comportamentos já existentes, a herança também permite que a subclasse (a classe que está herdando), neste exemplo a classe Jogador, possa estender ou sobrescrever um ou mais comportamentos já existentes.

A extensão de um comportamento é feito de forma que a subclasse (classe filha, ou seja, quem está herdando) possa executar o comportamento definido na classe base e também um comportamento próprio, como pode ser visto no exemplo abaixo:

public class Atirador : Personagem {

    public override void Atacar() {
        // Ajusta a mira no antes, antes de efetuar o ataque
        MirarNoAlvo();
        // Executa o ataque utilizando a lógica definida na classe base
        base.Atacar();
    }

    private void MirarNoAlvo() {
        // Ajusta a mira do atirado do alvo, antes de efetuar o ataque
    }

}

No exemplo anterior, o personagem Atirador herda da classe base Personagem e estende o comportamento do método "Atacar()" para adicionar um comportamento específico do atirador. Neste caso, o atirador precisa mirar no alvo antes de executar o ataque.
É importante notar que embora tenha sido adicionado um novo comportamento ao método "Atacar()", no momento em que o personagem efetua o ataque (logo após mirar), o único código utilizado é para executar o método "Atacar()" que está definido na classe base, base.Atacar(), de forma que não seja necessário reescrever a lógica do ataque, seja ela qual for.

A sobrescrita de um comportamento consiste em criar uma nova lógica para o comportamento, ignorando o comportamento que havia sido definido na classe base. Para realizar a sobrescrita, o método que será sobrescrito precisa ser marcado com a palavra reservada override, da mesma forma que é feito no processo de extensão de um método. No exemplo abaixo, podemos ver a classe PersonagemVoador, que herda da classe Personagem e tem como objetivo dar um comportamento diferente para este personagem, em relação ao que havia sido definido na classe base. Neste caso, o movimento da classe PersonagemVoador é baseado em uma movimentação pelo ar (voando).

public class PersonagemVoador : Personagem {

    public override void Mover() {
        Voar();
    }

    private void Voar() {
        // Executa a movimentação do personagem pelo ar
    }

}

É importante notar que embora tanto na extensão quanto na sobrescrita a palavra override seja utilizada, essas duas ações se diferem pelo uso ou não da lógica que havia sido definida na classe base. Enquanto o exemplo de extensão do método Atacar da classe Atirador utilizou a lógica definida na classe base através do comando base.Atacar(), o exemplo de sobrescrita do método Mover na classe PersonagemVoador em momento algum utilizou o código do método Mover, definido na classe base, de tal forma que o comportamento de movimentação do PersonagemVoador seja completamente diferente, estando voltado para a lógica definida no método interno, chamado de Voar, neste exemplo.

Para que um comportamento possa ser estendido ou sobrescrito, a classe base (Personagem) precisa marcar os métodos que permitem extensão e sobrescrita com a palavra reservada virtual, como pode ser observado na assinatura dos métodos Mover e Atacar, no trecho de código abaixo:

public class Personagem : MonoBehaviour {

    public float velocidadeMovimento;
    public float forcaAtaque;


    public virtual void Mover() {
        // Executa a lógica de movimentação
    }

    public virtual void Atacar() {
        // Executa a lógica de ataque e permite que
        // o método Atacar seja estendido e/ou sobreescrito por uma classe base
    }

}

Ficou alguma dúvida sobre o funcionamento das palavras reservadas override e virtual, ou sobre o processo de estender/sobrescrever o comportamento de uma classe base? Deixe sua pergunta aqui nos comentários.

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