Melhor Solução de Design para o problema

Olá,

tenho uma dúvida extremamente filosófica, mas considero relativamente importante e preciso decidir como vou seguir com meu proojeto :slight_smile: Por isso, gostaria da opinião de vocês para o seguinte problema:

Estou desenvolvendo em conjunto com uns colegas um RPG, que dispensa entrar em detalhes sobre o jogo em si.

Simplificando, a questão é assim:
Tenho uma interface Character que é implementada pelo personagens do jogo (sejam eles playables, enemies, friends, etc…). Esta interface define alguns métodos comuns (attack, talk, draw, etc.).
Os personagens possuem estado (Padrão State - fine, dead, etc.) e eis que surge a questão: suponha que eu tenha um personagem que seja infectado (poisoned) de alguma maneira -> o estado do personagem passa a ser de algum subtipo de PoisonState.
Tempo depois, o mesmo personagem é infectado novamente com algum outro tipo de veneno, passando a ter “dois poisons”.

Quando um personagem está infectado, sua força de ataque, velocidade e outros atributos são alterados.

Pois bem, como vcs implementariam estas questões relativas a estados e etc.?

Considerem isto:

-> É complicado ficar implementado estados poisoned combinados (BlindedAndAngry, BlindedAndLazy…). Existem n combinações de estados possíveis e a qualquer momento podemos incluir um novo tipo de veneno no jogo, obrigando-nos a criar mais m classes (BlindedAndNewPoisonedState, LazyAndNewPoisonedState…). E lembrem-se também que o personagem pode estar infectado de várias maneiras ao mesmo tempo.

-> Sim, pode-se usar o Padrão Decorator:
O estado atual do personagem é um Wrapper e podemos ir combinando mais e mais estados no personagem.
Porém, isto acarreta uma dificuldade de implementação, pois:
Se o meu personagem está infectado com vários poisons, preciso que a força do ataque dele seja influenciada pelo poisons envolvidos. Logo, um poison pode depender de QUAL é o outro poison para determinar a força do ataque… Eu teria que adaptar o padrão para fazer isso e não consigo enxergar uma maneira decente de fazer isso hehe…

Enfim, são divagações.
Não sei se me fiz claro e, caso não tenha me feito, pf me avisem que esclareço :slight_smile:

Gostaria de ver a opinião de vocês.

Abraço!

P.S.: não sei postei o tópico no lugar correto… fiquei na dúvida. Caso esteja no local errado, peço que os moderadores movam-no para o local correto :slight_smile:

Eu geralmente defino um conjunto de atributos, e coisas que modificam atributos (como venenos e powerups).
No caso o veneno, só irá temporariamente alterar o atributo “blind”. Aí simplesmente faço uma lista desses modificadores.

Estado eu deixo pra coisas mais comportamentais, como o “Angry”, “Watching”, etc.

Olá ViniGodoy,

obrigado pela resposta.

Os atributos que você disse seriam strings ou, que vá, constantes?

Por exemplo

public final static Integer BLIND = 1;
public final static Integer LAZY = 2;

//no personagem
List<Integer> poisons = new ArrayList<Integer>(); //ArrayList soh de exemplo - por ser qqr outra list ou set (para garantir q nenhum poison seja repetido)

void addPoison(Integer poison) { poisons.add(poison); }

//OUtra alternativa: Enums

É assim que você faz?

Se for, de fato é uma abordagem interessante, porém qdo vc decide adicionar um veneno novo que afete o personagem de maneira mais “incomum”, vc precisa alterar bastante coisas… e talvez vc tenha que usar diversos ifs… como:

for (Integer p : poisons) {
if (p == BLIND) { reducePowerAttack(); } else if (p == LAZY) { foo(); }
}

Não sei se foi isso que você quis dizer.
Se foi, pf não leve a mal minha resposta, estou apenas discutindo para verificar qual solução é melhor. Talvez a tua seja a melhor mesmo e eu que apenas não tenha entendido ela corretamente =D

E ViniGodoy, parabéns pelo PontoV. Excelente fonte de informações sobre game dev. Volta e meia dou uma olhada lá, muito bom!

Abraço!

Não… provavelmente eu modelaria isso numa classe, ou num enum.

Assim seria possível criar um método para aplicar os efeitos de maneira polimórfica, e eliminar totalmente o if.
Além disso, seria facilmente possível estender os efeitos através de scripts, bastando que alguém implementasse uma subclasse de “Effect”.

public interface Effect { void applyEffect(Character pc); void undoEffect(Character pc); }

Usar constantes estáticas dessa forma é muito, muito, muito, muito raro.

Claro isso é um rascunho. Afinal, você pode querer cobrir efeitos que são ou não cumulativos, que anulam uns aos outros, etc…
Isso é mais um indício que um efeito é uma classe completa, não um item de uma enumeração.

Muitos sistemas de RPG fazem com que os personagens tenham dois sets de atributos. Os atributos reais, e os modificados. Os modificados são calculados baseados na lista de efeitos, que são aplicados em ordem através daquele método “applyEffect”. O applyEffect simplesmente dispara um script, que realiza qualquer ação que o game designer queira para aquele efeito.

É claro, a mecânica disso não é tão simples, mas é mais flexível que a que você está propondo.

Quanto ao elogio, obrigado. A gente se esforça muito para manter a qualidade dos artigos e a constância no portal. Estamos pensando numas mudanças para o ano que vem e já temos confirmadas a entrada de alguns autores de peso. :wink:

Opa Vinícus,

ah tah, eu havia compreendido que você propunha algo daquela maneira que descrevi no post, com const. Confesso que achei bastante estranha huahua
Mas agora entendi o que você quis dizer.

De fato, esta tua solução de ter uma classe Effect (e não sendo um State como eu havia pensado inicialmente) que é aplicada sobre um Character e altera os atributos “modificáveis” do personagem é realmente interessante.

Acredito que vc me mostrou o caminho correto!

Há vários aspectos que não são contemplados por esta solução e que eu gostaria que estivesse presente, porém ela é a base para tudo! A partir dela posso expandir o modelo e seguir em frente =)

Vlws mesmo pela resposta =D

A hora que eu “enroscar” no modelo novamente, volto a postar neste tópico :wink:

abraço!

Tranquilo. Isso é uma aplicação direta do padrão Strategy.
Veja como vc pode criar suas classes filhas “hardcoded” de maneira fácil com enums do java:
http://www.guj.com.br/posts/list/55885.java

Dica: Procure comprar jogos como Nerverwinter Nights 1 ou 2 e Oblivion e estudar o sistema de mods deles, é muito esclarecedor.

Sim, agora que você falou percebi que ele é o Strategy, de fato vc está encapsulando que varia e definindo interfaces para o que está variando.
Eu não havia percebido porque normalmente um objeto atribui o que varia ao Character. Ilustrando:

Character c = new CharacterQualquer();
c.setEffect(objetoSubTipoDeEffect);

Neste caso é o próprio algoritmo que varia que está se “injetando” no Character:

Effect poison = new Blind();
poisoin.applyEffect(characterQualquer);

É o Strategy mesmo! Apenas eu que estava habituado a vê-lo por outro ângulo.

É por isto que acho bacana discutir… sempre acabo aprendendo algo novo =D

Vlws!
Abraço!