Antes de começarmos a implementar a interface e os objetos interagíveis, precisamos montar o nosso personagem.
Crie os dois códigos a seguir e adicione numa capsula.
Depois configure a capsula da seguinte forma
Se você der play, conseguirá andar com a capsula pela cena e controlar a câmera com o mouse.
O próximo passo é pensar no que queremos fazer. Pela praticidade, irei implementar a interação com dois objetos primitivos, de modo que cada interação seja completamente diferente da outra.
Primeiro, vamos com a Interface. Como a ideia é implementar uma interação completamente genérica, já que serão interações muito diferentes entre objetos, essa interface precisará apenas do método de interação. Isso porque, lembrando, o personagem precisa saber apenas que método executar.
A primeira interação que farei é fazer um objeto girar. Criarei então um cubo e colocarei o código nele. Chamarei esse código de SpinInteraction, e ele herdará da MonoBehaviour e do IInteractable.
Você verá que essa herança vai gerar um aviso de erro, isso porque ao herdar da interface, a classe ou struct é obrigada a implementar os método herdados.
O código a seguir usa o Interact() para rodar uma Coroutine que gira o objeto ao redor de si. Como ainda não implementamos a capacidade do player de interagir, vamos testar o Interact() pressionando uma tecla mesmo.
Em cena, o configure o cubo com um collider, para que ele tenha uma área definida, e, portanto, possa ser identificado, e adicione o código acima. Tome a liberdade de ajustar a velocidade como quiser.
Ao pressionar espaço o objeto começará a girar. Para início está bom. Podemos então implementar o código que faz o personagem rodar a interação com objetos.
Esse tipo de código usa a visão da câmera para disparar um raio em cena e pegar, em código, o primeiro objeto encontrado. A partir dele, testamos a herança de IInteractable e rodamos o método Interact().
Para seguir a lógica dos nomes, vou chamar esse código de PlayerInteraction:
Para entender melhor a visão do personagem, eu adicionei a linha do Debug.DrawRay(), que na imagem renderiza a linha vermelha. Veja, também, a configuração dos parâmetros do código da capsula:
Enquanto a linha estiver cruzando o cubo (tive que subir o cubo para ficar na altura do raio), se você apertar E, verá o objeto rodando o método Interact() e girando.
Perceba que em nenhum momento a Capsula precisou saber qual o objeto, ou o código, que implementa o Interact(), ele apenas verificou se o objeto é interagível, uma situação completamente melhor que o exemplo presente lá em cima.
Podemos inclusive adicionar mais objetos interagíveis, e não precisaremos modificar uma linha sequer no código da capsula.
Crie uma esfera e um código chamado ColorInteraction. Nesse código, vamos rodar uma transição entre cores aleatórias.
Configure com a velocidade que quiser, e você verá a cor da esfera transitando.
Aqui terminamos o contexto de uso.
Em resumo, hierarquia de classes constroem uma hierarquia rígida, definindo uma "árvore" de heranças pelos conjuntos de objetos que elas, as classes, representam, considerando atributos e métodos semelhantes.
Já interface, segundo a própria definição dos vários documentos de referência das linguagens, define um contrato em que a classe ou struct que implementa a Interface se compromete a implementar as definições desse contrato.
Talvez essa pergunta já surgiu ou irá surgir - quando devemos usar classe e quando devemos usar interface? qual é melhor entre as duas?
A melhor dependerá sempre do contexto. Eu, Marcos, tento seguir uma regra pessoal sempre que possível, porque claro, cada situação possui algo em particular. A regra é: se preciso agrupar objetos em torno de uma ação ou um único propósito, uso Interface; se preciso de agrupar objetos por toda a informação que existe nesses objetos (atributos e métodos), uso classe.