Back-End

9 nov, 2016

Classes genéricas em Unity

Publicidade

genericclasses

Quando eu estava escrevendo meu artigo sobre MVC em Unity, percebi que eu usei um monte de, digamos, material não-básico, que pode ser meio confuso se você é novo em programação. Então, aqui está um guia curto para classes genéricas em Unity e quando elas podem ser úteis para você.

O que classes genéricas fazem?

Classes genéricas ou métodos genéricos têm um tipo de parâmetro, mas você não está especificando o tipo desse parâmetro. Agora, onde que você usaria isso? Eu acho que isso é melhor ilustrado com um exemplo. Eu sou uma grande fã da classe List, mas uma coisa que muitas vezes eu uso em jogos e que não é definida pela classe List é a troca de dois objetos. Como eu sou muito preguiçosa para escrever um método em cada classe que precisa desse recurso, eu prefiro estender a própria classe List. O problema é que uma List pode ser de diferentes tipos. Você pode ter uma List de ints, uma List de floats, um lista de Transforms, e a lista continua. Portanto, isso não funcionaria para mim, porque só funciona para ints:

public static void Swap(List<int> list, int indexA, int indexB)
{
	int tmp = list[indexA];
	list[indexA] = list[indexB];
	list[indexB] = tmp;
}

Ao usar um método genérico, posso fazer o método Swap para cada tipo de lista:

public static List<T> Swap<T> (this List<T> list, int indexA, int indexB)
{
	T tmp = list[indexA];
	list[indexA] = list[indexB];
	list[indexB] = tmp;
	return list;
}

// access the method like this:
// List<int> myList = new List<int>() { 8, 3, 2, 4 };
// myList = myList.Swap(1, 2);

Nesse exemplo, T pode ser de qualquer tipo, GameObject, bool, o que quiser. Ele também mostra que List por si só é uma classe genérica. É por isso que você tem que declarar Lists deste jeito:

public List<int> listOfInts;

Você está declarando que você quer dar a sua instância da classe List genérica o tipo int.

Usos práticos em jogos

Agora, quando você está codificando jogos, classes e métodos genéricos também são úteis para outras coisas. Usei-os para o meu sistema MVC, mas também pode ser muito agradável de outra forma – por exemplo, se você tem classes que são sempre emparelhadas, mas existem subtipos dessas classes.

Então, digamos que você está codificando um jogo de Star Wars. Em Star Wars, muitas naves espaciais têm droids bonitinhos, que irão consertá-las em pleno voo. Mas pode haver diferentes tipos de naves espaciais e, claro, diferentes tipos de droids. Então, primeiro, eu vou preparar as classes base para ambos:

public class BaseSpaceship<D> : MonoBehaviour
    where D: BaseDroid
{

    public D myDroid;

    private void OnLowHealth () {
        myDroid.RepairShip();
    }

}

 

public class BaseDroid : MonoBehaviour {

    public void RepairShip () {
        //Repair the ship and get shot while doing it, or isn't it how this works?
    }
   
}

Assim, o BaseSpaceship já definiu myDroid. Mas não sabemos que tipo de droid será. Ainda assim, podemos chamar o método RepairShip() no BaseDroid, o que cada droid é capaz de fazer. Mas também queremos construir a SpaceShipA, que é uma nave espacial que sempre é emparelhada com uma unidade BB-8.

(Por favor, não fiquem com raiva, queridos Star Wars nerds. Unidades BB-8 provavelmente não serão usadas em veículos, mas eu só quero mostrar um ponto aqui, e o BB-8 é realmente bonitinho…)

E o BB-8, claro, tem esse recurso incrível que todos nós amamos, que ele pode oferecer um isqueiro. Por isso, queremos acessar isso com a nossa nave espacial:

public class SpaceShipA : BaseSpaceship<BBEight> {

    public void ThumbsUpToBB () {
        myDroid.PullOutLighter();
    }
   
}

 

public class BBEight : BaseDroid {

	public void PullOutLighter () {
		//So cuuute <3
	}

}

Assim, o SpaceShipA é sempre emparelhado com um BBEight e, portanto, pode acessar as características especiais do BB-8, enquanto ainda está fazendo as outras coisas que BaseSpaceShips e BaseDroids fazem.

Diferença para as interfaces

Interfaces são usadas com bastante frequência e fazem quase o mesmo. A diferença é que, com a interface, eu só posso acessar as coisas que o BaseDroid faz, enquanto eu posso acessar todos os métodos de BBEight se eu fizer isso através de uma classe genérica, que é bastante agradável aos meus olhos.

Desvantagens

Apesar de isso soar muito prático, infelizmente tem também algumas limitações em Unity. Classes genéricas indefinidas, por exemplo, não podem ser serializadas. No nosso exemplo, isso significa que você não pode adicionar BaseSpaceship a um GameObject ou colocar em uma variável pública no editor, desde o seu droid não seja definido. Mesmo se você não quiser acrescentar todos os recursos adicionais ao BaseSpaceship, você ainda tem que criar uma classe herdada de BaseSpaceship com um tipo de drone definido para ser capaz de usá-lo no editor.

Dito isso, eu ainda acho que é um recurso muito útil e vale a pena usar, nas situações adequadas, é claro :).

Nota: esta é uma cópia de um artigo no meu site.

Nota: Eu percebi que ao importar os exemplos de script, todos os < and > foram removidos. Me desculpe por isso!

***

Artigo escrito por Tabea Iseli. A tradução foi feita pela Redação iMasters. O original pode ser conferido em http://www.gamasutra.com/blogs/TabeaIseli/20160928/282216/Generic_classes_in_Unity.php.