[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

Awake x Start

E aí galera, beleza?

Dessa vez vamos falar um pouco sobre os métodos Awake e Start como formas de inicializar os nossos scripts, apresentando as semelhanças e diferenças entre eles.

O Start é um método bastante conhecido pelo desenvolvedores Unity, principalmente por estar presente em todos os novos Scripts C# criados na Unity. De modo geral, o Start é recomendado para ser utilizado na inicialização dos seus scripts, pela sua característica de ser o primeiro método a ser executado, logo após o jogo ser iniciado e o GameObject ser ativado. Mas você sabia que existe um método que é executado antes do Start e que pode te ajudar a resolver problemas de inicialização e comunicação entre seus scripts?

Awake e Start

Os métodos Awake e Start são métodos especiais da Unity, que são automaticamente executados assim que o nosso jogo é iniciado. Essa execução acontece de forma automática tanto no Editor da Unity, durante o processo de desenvolvimento do jogo, quanto durante a execução do jogo após um build ter sido gerado.
Embora os dois métodos possam ser utilizados para inicializar variáveis dos seus scripts, a ordem de execução deles faz com que exista uma diferença sútil entre eles e essa diferença pode te ajudar a resolver alguns problemas.

Fluxo de execução da Unity (fonte: https://docs.unity3d.com)

Start

Ao criar um novo script na Unity, por padrão os métodos Start e Update são automaticamente adicionados fazendo com que os desenvolvedores acostumem-se a utilizar o Start como o principal ponto de inicialização de seus scripts.
Para projetos menores e/ou para scripts que não se relacionam com outros scripts durante o processo de inicialização, a utilização exclusiva do Start costuma ser o suficiente, mas se você já passou pela situação em que dois script (script A e script B) precisam ser inicializados logo após o jogo começar, mas o script B depende que o script A já esteja iniciado, utilizar o Awake pode ser uma solução.

Awake

O Awake é o primeiro método de um script a ser executado. Por ser o primeiro método executado, recomenda-se que seja utilizado para iniciar as variáveis do próprio script, deixando-o pronto para que este script possa ser utilizado por outros, tendo a certeza de que tudo o que era necessário ser inicializado já foi inicializado.

A ordem de execução dos scripts na Unity garante que o método Awake de todos os scripts, ativos, serão executados antes do método Start de qualquer script. Essa ordem de inicialização com o Awake sendo executado sempre antes do Start, permite que a inicialização dos scripts seja organizada (e padronizada) de tal forma que resolva o problema da dependência entre scripts no momento de sua inicialização.

Inicializações relacionadas ao próprio objeto devem ser realizadas no Awake. Isso é recomendado por que o Awake é executado antes do Start, fazendo com que outro script possa acessar o script atual após ele ter sido inicializado.

Exemplo:
Script B acessa o script A durante a sua inicialização. Como a lógica implementada no Start do script B precisa de uma informação do script A, que também só será inicializada/definida no Start, os scripts passam a depender da ordem de inicialização de ambos os scripts A e B. Ou seja, o script B precisa obrigatoriamente ter o seu método Start executado depois do Start do script A.

O grande problema é que a Unity não garante a ordem de execução dos scripts, de tal forma que durante o desenvolvimento o Script B pode ser uma executado depois do Script A, porém, no build, após o lançamento do jogo, essa ordem pode ser arbitrariamente invertida, dependendo de qual plataforma alvo (PC, Android, iOS e etc.) está sendo utilizada para executar o jogo.


public class ScriptA : MonoBehaviour {

    public int numeroA;

    public void Start() {
        // Inicia o número A com valor 10
        this.numeroA = 10;
    }

}

public class ScriptB : MonoBehaviour {

    public ScriptA scriptA;
    public int numeroB;

    public void Start() {
        // Inicia o número B com o valor do número A + 1
        this.numeroB = this.scriptA.numeroA + 1;

        // Resultado: 11 se o ScriptA for iniciado ANTES do ScriptB
        // Resultado: 1 se o ScriptA for iniciado DEPOIS do ScriptB
        Debug.Log("Número B: " + this.numeroB);
    }

}


Para que esse fluxo funcione corretamente, independente da ordem de execução dos scripts, tanto o script A quanto o script B devem configurar a si mesmos no Awake, ou seja, inicializações de variáveis e outros atributos que não dependem de scripts/componentes externos devem ser feitos no método Awake, que é o primeiro método do ciclo de vida de um Script/MonoBehaviour a ser executado. Em seguida, a inicialização das demais variáveis que dependem de scripts/componentes externos, devem ser feitas no Start, tendo a certeza de que os scripts/componentes externos, dos quais eles dependem, já foram previamente inicializados no Awake.

Sendo assim, para que o script B possa acessar o script A no Start, tendo certeza que o script A já foi inicializado anteriormente no Awake, o exemplo de código exibido anteriormente deve ser alterado para utilizar a estrutura abaixo:

public class ScriptA : MonoBehaviour {

    public int numeroA;


    public void Awake() {
        // Inicia o número A com valor 10
        // antes do Start de qualquer script ser executado
        this.numeroA = 10;
    }

}

public class ScriptB : MonoBehaviour {

    public ScriptA scriptA;
    public int numeroB;

    public void Start() {
        // Inicia o número B com o valor do número A + 1
        this.numeroB = this.scriptA.numeroA + 1;

        // Resultado: 11 independente da ordem de inicialização dos scripts
        Debug.Log("Número B: " + this.numeroB);
    }

}

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