Criar um sistema baseado em comportamento / componente para jogos

11

fundo

Faço o desenvolvimento de jogos como um hobby e estou procurando uma maneira melhor de projetá-los. Atualmente, estou usando uma abordagem padrão de POO (desenvolvo empresas há 8 anos, por isso é necessária a cada ano). Tomemos, por exemplo, um "vilão"

public class Baddie:AnimatedSprite //(or StaticSprite if needed, which inherit Sprite)
{
    //sprite base will have things like what texture to use, 
    //what the current position is, the base Update/Draw/GetInput methods, etc..
    //an AnimatedSprite contains helpers to animated the player while 
    //a StaticSprite is just one that will draw whatever texture is there
}

O problema

Digamos que eu estou fazendo um jogo de plataformas 2D e preciso do vilão para poder pular. Normalmente, o que faço é adicionar o código apropriado nos métodos Update / GetInput. Então, se eu precisar fazer o jogador engatinhar, pular, subir, etc ... o código vai para lá.

Se eu não tomar cuidado, esses métodos ficam desordenados, então acabo criando pares de métodos como este

CheckForJumpAction(Input input) e DoJump()

CheckforDuckAction(Input input) e DoDuck()

então GetInput parece

public void DoInput(Input input)
{
    CheckForJumpAction(input);
    CheckForDuckAction(input);
}

e atualização parece

public void Update()
{
    DoJump();
    DoDuck();
}

Se eu for criar outro jogo em que o jogador precise pular e pular, geralmente entro em um jogo que tem a funcionalidade e o copio. Bagunçado, eu sei. É por isso que estou procurando algo melhor.

Solução?

Eu realmente gosto de como o Blend tem comportamentos que posso anexar a um elemento. Eu tenho pensado em usar o mesmo conceito nos meus jogos. Então, vamos olhar para os mesmos exemplos.

Eu criaria um objeto Behavior básico

public class Behavior
{
    public void Update()
    Public void GetInput()
}

E eu posso criar comportamentos usando isso. JumpBehavior:BehavioreDuckBehavior:Behavior

Posso adicionar uma coleção de comportamentos à base do Sprite e adicionar o que preciso para cada entidade.

public class Baddie:AnimatedSprite
{
    public Baddie()
    {
        this.behaviors = new Behavior[2];
        this.behaviors[0] = new JumpBehavior();
        //etc...
    }

    public void Update()
    {
        //behaviors.update
    }

    public GetInput()
    {
        //behaviors.getinput
    }
}

Então agora, se eu quisesse usar o Jump and Duck em muitos jogos, posso apenas trazer os comportamentos. Eu poderia até criar uma biblioteca para os comuns.

Funciona?

O que não consigo descobrir é como compartilhar o estado entre eles. Olhando Jump e Duck, ambos afetam não apenas a parte atual da textura que está sendo desenhada, mas também o estado do jogador. (O salto aplicará uma quantidade decrescente de força para cima ao longo do tempo, enquanto o duck apenas interromperá o movimento, mudará a textura e o tamanho da colisão do bandido.

Como posso amarrar isso para que funcione? Devo criar propriedades de dependência entre os comportamentos? Devo que cada comportamento conheça o pai e o modifique diretamente? Uma coisa que pensei foi poder passar um delegado em cada comportamento para ser executado quando ele é acionado.

Estou certo de que estou analisando mais problemas, mas o objetivo é poder reutilizar facilmente esses comportamentos entre jogos e entidades no mesmo jogo.

Então eu entrego a você. Importa-se de explicar como / se isso pode ser feito? Você tem uma ideia melhor? Sou todo ouvidos.


fonte
Isso pode ser melhor perguntado em gamedev.stackexchange.com ?
rcapote
Eu gosto de onde você está indo, mas não sei como resolver os problemas que você mencionou. Muito interessante e tem implicações além do desenvolvimento de jogos.
Edward Strange
@rcapote não é este site para quadro de comunicações / discussões?
2
@ Joe - não, não. Nem mencione "discussão" aqui ou você será modificado para não existir. Este site é apenas para o tipo de interação Pergunta-> Resposta-> Concluído.
Edward Strange
Bem, sistemas de entidade baseados em componentes é algo que começou a se destacar na comunidade de desenvolvedores de jogos ultimamente, então imaginei que você poderia receber melhor atenção lá, mas provavelmente obterá boas respostas aqui de qualquer maneira. Aqui é uma discussão interessante sobre gamedev.net sobre este problema que você pode achar interessantes: gamedev.net/topic/...
rcapote

Respostas:

1

Dê uma olhada nesta apresentação. Parece muito próximo ao tipo de padrão que você está procurando. Esse padrão suporta comportamentos e propriedades anexáveis. Eu não acho que a apresentação mencione isso, mas você também pode criar eventos anexáveis. Essa ideia é semelhante às propriedades de dependência usadas no WPF.

Nieldy
fonte
9

Para mim, isso soa como um caso quase manual para o uso do Padrão de Estratégia . Nesse padrão, seus comportamentos genéricos podem ser definidos em interfaces que permitem trocar diferentes implementações de comportamentos em tempo de execução (pense em como uma ligação pode afetar a capacidade de pular ou correr do seu personagem).

Para um exemplo (artificial):

// The basic definition of the jump behavior
public interface IJumpBehavior {
    void Jump();
}

Agora você pode implementar vários tipos de saltos que seu personagem pode usar, sem necessariamente conhecer os detalhes sobre cada salto específico:

// This is the jump the character may have when first starting
public class NormalJump : IJumpBehavior {

     public void Jump() {
         Console.WriteLine("I am jumping, and being pretty boring about it!");
     }

}

// This is the jump of a character who has consumed a power-up
public class SuperJump : IJumpBehavior {
    public void Jump() { 
         Console.WriteLine("I am all hopped up on star power, now my jumps can do damage!");
     }
}

Todos os caracteres que você deseja ter a capacidade de pular agora podem conter uma referência a um IJumpable e podem começar a consumir suas várias implementações

public class HeroCharacter {

    // By default this hero can perform normal jump.
    private IJumpBehavior _jumpBehavior = new NormalJump();

    public void Jump() {
        _jumpBehvaior.Jump();
    }

    // If you want to change the hero's IJumpable at runtime
    public void SetJump(IJumpBehavior jumpBehavior) {
      _jumpBehavior = jumpBehavior;
    }

}

Então seu código pode ser algo como:

HeroCharacter myHero = new HeroCharacer();

// Outputs: "I am jumping, and being pretty boring about it!"
myHero.Jump()

// After consuming a power-up
myHero.SetJump(new SuperJump());

// Outputs: "I am all hopped up on star power, now my jumps can do damage!"
myHero.Jump();

Agora você pode facilmente reutilizar esses comportamentos, ou até estendê-los mais tarde, se desejar adicionar mais tipos de saltos ou criar mais jogos criados em uma única plataforma de rolagem lateral.

mclark1129
fonte
Embora não seja exatamente o que estou procurando, isso é muito próximo e gosta. Vou brincar com isso para ter certeza. obrigado!
1

Dê uma olhada na arquitetura DCI, uma visão interessante da programação OO que pode ajudar.

A implementação de uma arquitetura de estilo DCI em C # requer o uso de classes de domínio simples e objetos de Contexto (comportamentos) com Métodos de Extensão e Interfaces para permitir que as classes de domínio colaborem sob diferentes funções. As interfaces são usadas para marcar as funções necessárias para um cenário. As classes de domínio implementam as interfaces (funções) que se aplicam ao comportamento pretendido. A colaboração entre as funções ocorre nos objetos de contexto (comportamento).

Você pode criar uma biblioteca de comportamentos e funções comuns que podem ser compartilhados entre objetos de domínio de projetos separados.

Veja DCI em C # para exemplos de código em C #.

Ed James
fonte
0

Que tal passar um objeto de contexto para um comportamento que forneça métodos para alterar o estado dos sprites / objetos afetados por um comportamento? Agora, se um objeto tem vários comportamentos, cada um deles é chamado em loop, dando a chance de mudar o contexto. Se uma certa modificação de uma propriedade excluir / restringir a modificação de outras propriedades, o objeto de contexto pode fornecer métodos para definir algum tipo de sinalizador para indicar esse fato, ou pode simplesmente negar modificações indesejadas.

Jonny Dee
fonte