Padrões de Projeto Aplicados ao Desenvolvimento de Jogos Digitais [EEClone] [SÉRIE]
Artigos, avançado, Game, Serie
Esta é a segunda publicação da série "Padrões de Projetos Aplicados ao Desenvolvimento de Jogos Digitais" de Yves Nogueira, ex-aluno do curso de Sistemas de Informação da FA7. Dessa vez ele aborda o EEClone, um jogo acadêmico para ensino de padrões, e discute os utilizados na sua implementação.
O EEClone foi um jogo desenvolvido com o objetivo de ser uma ferramenta no ensino de padrões de projeto para alunos de ciência da computação, tendo sido inspirado em um jogo pré-existente chamado Every Extend.
Nele, o jogador controla um bomba em um ambiente com obstáculos móveis, perdendo uma vida caso colida com algum obstáculo, podendo detonar espontaneamente a bomba a fim de iniciar uma reação em cadeia de explosões nos obstáculos que estiverem próximos à explosão, perdendo também uma vida neste caso, mas ganhando pontos que, quando acumulados acima de determinado valor, aumentam o número de vidas atuais. Alguns obstáculos ocasionalmente geram bônus quando destruídos, que podem ser coletados para se ganhar pontos extras [1].
EEClone, desenvolvido por Paul Gestwicki
O EEClone foi produzido intencionalmente com uma combinação de múltiplos padrões de projeto em sua estrutura, tornando-o mais facilmente extensível por quem tenha interesse em adicionar novos elementos e funcionalidades ao jogo. Possuindo os principais elementos comuns a outros jogos, a citar: animação, pontuação, música, efeitos sonoros e controle de colisão, o EEClone consegue manter um bom desempenho e tempo de resposta a controles com a sua estrutura montada tomando como base padrões de projeto clássicos.
Abaixo buscou-se explicitar a utilização dos padrões na estrutura do jogo em cada elemento deste, apresentando o problema no desenvolvimento do jogo cuja solução foi visada ao aplicar o padrão.
O padrão State permite a um objeto alterar seu comportamento baseado em uma mudança em seu estado interno, aparentando ter mudado de classe para elementos externos a ele.
A utilização deste padrão é recomendada quando um determinado objeto possui mais de um estado com diferentes comportamentos associados, havendo a necessidade de mudança de comportamento de acordo em tempo de execução.
No EEClone os elementos como o jogador, os obstáculos, as explosões e os bônus são todos considerados sprites pelo motor de jogo. Um sprite, segundo Gestwicki, é qualquer elemento de jogo que não faça parte do background. Cada sprite tem um estado, que pode incluir fatores como posição, velocidade, tamanho, imagem, entre outros, e um comportamento, que pode ser a resposta do elemento correspondente ao jogador quando este entra com comandos de movimentação, ou o tratamento de colisão para os sprites correspondentes aos inimigos.
Uma implementação tradicional sem o uso de padrões de projeto para este caso é a utilização de variáveis de controle para determinar o estado com uma posterior utilização de código condicional em todos os pontos que sejam dependentes de estado para determinar qual comportamento deve ser executado. No caso de adição de novos estados, esta abordagem faz com que cada um destes pontos dependentes de estado tenham que ser atualizados, dando mais margem para que erros sejam cometidos nesta adição ou que alguns destes pontos não sejam devidamente atualizados.
Neste caso, a substituição desta implementação tradicional pelo padrão State [2] remove este código condicional e encapsula cada estado em sua própria classe. Todas as mensagens enviadas a determinado sprite são repassadas para seu estado, que agora é um objeto, para serem tratadas, sendo de responsabilidade dos próprios estados as suas transições.
Em caso de necessidade de adição de um novo estado como, por exemplo, um novo estado de jogador correspondente a um novo bônus adicionado ao jogo, basta que seja criada uma nova classe contendo este novo estado e que seja atualizada a lógica de transição entre os estados.
No EEClone o padrão Facade [2] é utilizado para desacoplar a classe de apresentação das classes de lógica do jogo através de uma classe Facade que simplifica o acesso a estas últimas. A classe de apresentação comunica-se apenas com esta fachada, tornando possível que a apresentação seja alterada sem que seja necessário fazer modificações nas classes de lógica do jogo e vice-versa [1] .
O padrão Facade cria uma interface unificada para simplificar o acesso e utilização de um conjunto de classes ou subsistemas, potencialmente desacoplando estas classes ou subsistemas de outros que os utilizam
A utilização do padrão Observer no EEClone se dá no contexto de tratamento de entrada de comandos do usuário: um elemento KeyListener escuta por entradas e escreve a informação acerca das teclas pressionadas em um objeto compartilhado cujo estado é lido pela thread principal do jogo na fase de update. O objeto compartilhado neste caso é o subject, enquanto a thread principal do jogo faz o papel de observer. Nota-se que esta não é uma aplicação à risca do padrão Observer como definido Gamma et al [2], mas uma adaptação do mesmo a um contexto multithread sem utilização de sincronização [1] .
O padrão Observer define uma dependência de um para muitos entre objetos a fim de que quando um objeto do tipo subject mude de estado todos os objetos observers registrados naquele subject sejam notificados e atualizados automaticamente.
A animação do EEClone é tratada através do padrão Strategy: cada sprite possui um objeto Animation, que é a interface genérica do padrão Strategy neste caso, recebendo uma animação concreta nesta referência em tempo de execução. A renderização é então delegada do sprite para o objeto Animation. Esta aplicação do padrão Strategy no EEClone é combinada com a aplicação do padrão State apresentado anteriormente a fim de que cada estado tenha uma animação relacionada e apenas a animação do estado atual seja renderizada para aquele sprite [3].
O tratamento de colisão no EEClone se dá a partir dos tipos de sprites envolvidos na colisão, potencialmente gerando um efeito para cada um no momento em que esta ocorre. Uma implementação não orientada a objetos para este tipo de detecção de colisão utilizaria uma variável básica de controle, como um inteiro, para determinar os tipos de sprites, sendo utilizado então alguma estrutura condicional como um switch para determinar os tipos dos sprites na colisão em tempo de execução. Esta abordagem tende a dificultar a adição de novos elementos e a tornar o código menos legível.
Singleton: "só deve haver um"
A aplicação do padrão Visitor [2] no lugar desta abordagem tradicional ajuda a manter a lógica de tratamento de colisão para cada sprite em uma estrutura própria, deixando as classes de sprite menos infladas, além de facilitar no caso de necessidade futura de adição de novos tipos de colisão, bastando para isso a adição de um novo método de tratamento na interface genérica de Visitor, que deverá ser implementado nas classes anônimas internas às classes sprites nas quais aquele tipo de colisão faça sentido. Cada sprite possui por associação uma referência a um objeto Visitor que recebe um objeto concreto a partir de uma classe interna, podendo conter métodos vazios para as colisões que não façam sentido para aquele objeto sprite determinado. No momento da colisão cada objeto Visitor visita o outro sprite e realiza a sua tarefa para aquele tipo de sprite. O objeto Visitor do sprite do jogador, por exemplo, possuirá implementações para reduzir uma vida caso colida com um obstáculo ou aumentar o número de pontos caso colida com um sprite de bônus.
O EEClone utiliza este padrão no tratamento de reprodução de música, possuindo uma classe singleton [2] para este fim, que pode ser acessada por qualquer objeto do jogo a fim de iniciar, alterar ou parar a música que esteja tocando ao fundo do jogo, garantindo que em qualquer momento haja somente uma música sendo reproduzida.
Confira a Introdução da série Padrões de Projeto Aplicados ao Desenvolvimento de Jogos Digitais
Veja a página oficial do EECLONE com uma versão executável disponível (Dica do Hugo Rodrigues)
No que pese o EEClone fazer uso em toda a sua estrutura de padrões de projeto por conta do fim a que se destina, ou seja, o de ensinar padrões de projeto a alunos de ciências da computação e não exatamente por uma necessidade propriamente dita do projeto, ele é um bom exemplo de como é possível equilibrar a necessidade de desempenho e manutenibilidade em jogos digitais através da utilização de padrões.
[2] E. Gamma et al. Design patterns: elements of reusable object-oriented software. Addison – Wesley, 1995.
[3] PCGAMER. Analyst says digital sales made up 92 percent of PC game market in 2013. 2014. Disponível em: <http://www.pcgamer.com/analyst-says-digital-sales-made-up-92-percent-of-pc-game-market-in-2013>.
Só senti falta do link do código fonte, achei, ta aqui: http://www.cs.bsu.edu/homepages/pvg/games/eeclone/eeclone-0.6-src.jar
Isso aí, Hugo, depois que publicamos também sentimos falta, vamos adicionar ao post
[…] aproximando-se mais de uma abordagem mercadológica de desenvolvimento se comparado com o EEClone. O autor faz a ressalva de que os padrões selecionados e a utilização dos mesmos no projeto não […]