Exemplo do Design Pattern Command na Unity com C#

Exemplo do Design Pattern Command na Unity com C#

Sempre recomendo o livro Game Programming Patterns, que possui versões física e digital à venda, além de uma online gratuita disponibilizada pelo autor, Robert Nystrom. Este livro revisita os design patterns definidos na década de 1990 pela “Gangue dos Quatro”, focando no seu uso para o desenvolvimento de jogos.

O livro conta com excelentes exemplos e com pseudocódigo baseado em C/C++, além de descreve situações em que cada programming pattern pode ser utilizado. Já realizei algumas palestras sobre este assunto e utilizei este livro como base, sendo a mais recente na TDC Porto Alegre 2019. Meu objetivo com este post e, com os próximos que virão, é criar exemplos do uso de cada programming pattern na game engine Unity e com a linguagem de programação C#.

Seguindo a ordem do livro, este primeiro post será sobre o Design Pattern Command. O código deste exemplo, assim como de todos os próximos, pode ser acessado neste repositório do GitHub. Os demais posts desta série podem ser acessados neste link.

Design Pattern Command

O Command é um design pattern que tem como objetivo encapsular um método ou ação. Em uma definição muito simples pode-se dizer que é um padrão para callbacks. Um exemplo bem prático do Command é o input de um jogo. Imagine que seu jogo possui uma opção para que o jogador modifique as ações de cada botão, seja por preferencia pessoal ou acessibilidade. Com a Implementação do Command fica simples modificar o comando que um botão executa, como demonstrado no exemplo a seguir.

Implementação na Unity com C#

Neste exemplo de implementação existem duas ações possíveis para o jogador: Pular e Atirar. Para simplificar o exemplo, cada ação será apenas uma mensagem de log. O código abaixo define exatamente isso, dois comandos que podem ser executados. É importante ter uma interface neste caso, ou pelo menos uma classe base, para que o código que executar estas ações não precise necessariamente saber o tipo de cada classe derivada.

public interface ICommand
{
    void Execute();
}

public class JumpCommand : ICommand
{
    public void Execute()
    {
        Debug.Log("Execute command: Jump");
    }
}

public class FireCommand : ICommand
{
    public void Execute()
    {
        Debug.Log("Execute command: Fire");
    }
}

E é basicamente isso, uma interface com o método que executará uma ação e duas implementações desta interface. Agora ambas ações podem ser utilizadas em outra classe, como será demonstrado no MonoBehaviour abaixo. A classe abaixo possui dois métodos públicos executados por botões na Unity, o ButtonA() e o ButtonB(). Estes métodos executam qualquer comando associado com as respectivas variáveis, as quais possuem o tipo da interface ICommand.

public class DesignPatternCommand : MonoBehaviour
{
    private ICommand _buttonA;
    private ICommand _buttonB;

    private void Start()
    {
      _buttonA = new JumpCommand();
      _buttonB = new FireCommand();
    }

    public void ButtonA()
    {
        _buttonA.Execute();
    }

    public void ButtonB()
    {
        _buttonB.Execute();
    }
}

Quando executado, o código acima irá atribuir a ação de pular para o botão A e a ação de atirar para o botão B. Agora imagine que, neste exemplo, teremos uma opção que permite o jogador inverter os comandos dos botões. Para isso, um método como este abaixo irá alterar o comando dos botões sem a necessidade de qualquer outra modificação no código.

private void ChangeButtons()
{
  _buttonA = new FireCommand();
  _buttonB = new JumpCommand();
}

Este é um exemplo bem simples para explicar o conceito por trás deste programming pattern. A partir disso pode-se ver como o Command é um poderoso recurso para todo programador, pois permite alterar ações sem criar dependências no código. Existem outros exemplos interessantes para o seu uso, como um sistema de avançar e voltar no tempo onde todas as ações do jogador podem ser uma lista de comandos executados. Isso pode ser utilizado até mesmo em um editor para desfazer e refazer uma determinada ação.

Exemplo

Neste repositório do GitHub existe um exemplo utilizando o código acima, porém com 4 botões de ações do jogador e um outro botão que permite embaralhar as ações.

O Command é um padrão simples, mas que é muito poderoso quando entendido e utilizado corretamente na arquitetura de um jogo. Imagine ter que adicionar novos botões no exemplo acima, ou um hardware diferente para o input. Tudo isso pode ser feito de forma prática com o Command. Este programming pattern é um bom aliado para tornar um jogo mais acessível, permitindo a customização do controle do jogo pelo jogador.

comments powered by Disqus