Como organizar um mecanismo de jogo em C ++? Meu uso da herança é uma boa idéia?


11

Sou iniciante no desenvolvimento e programação de jogos.

Estou tentando aprender algum princípio na construção de um mecanismo de jogo.
Eu quero criar um jogo simples, estou no ponto em que estou tentando implementar o mecanismo do jogo.

Então pensei que meu mecanismo de jogo deveria controlar essas coisas:

- Moving the objects in the scene
- Checking the collisions
- Adjusting movements based on collisions
- Passing the polygons to the rendering engine

Eu projetei meus objetos assim:

class GlObject{
private:
    idEnum ObjId;
    //other identifiers
public:
    void move_obj(); //the movements are the same for all the objects (nextpos = pos + vel)
    void rotate_obj(); //rotations are the same for every objects too
    virtual Polygon generate_mesh() = 0; // the polygons are different
}

e eu tenho 4 objetos diferentes no meu jogo: avião, obstáculo, jogador, bala e os projetei assim:

class Player : public GlObject{ 
private:
    std::string name;
public:
    Player();
    Bullet fire() const; //this method is unique to the class player
    void generate_mesh();
}

Agora, no mecanismo de jogo, quero ter uma lista geral de objetos, onde posso verificar, por exemplo, colisão, mover objetos etc., mas também quero que o mecanismo de jogo use os comandos do usuário para controlar o jogador ...

Isso é uma boa ideia?

class GameEngine{
private:
    std::vector<GlObject*> objects; //an array containg all the object present
    Player* hPlayer; //hPlayer is to be read as human player, and is a pointer to hold the reference to an object inside the array
public:
    GameEngine();
    //other stuff
}

o construtor GameEngine será assim:

GameEngine::GameEngine(){
    hPlayer = new Player;
    objects.push_back(hPlayer);
}

O fato de eu estar usando um ponteiro é porque preciso chamar o fire()que é exclusivo para o objeto Player.

Então, minha pergunta é: é uma boa ideia? Meu uso da herança está errado aqui?



Eu os vi depois de postar, e na verdade composição é uma ótima idéia! Eu sempre tenho medo de acabar usando c com classes em vez de ter um design c ++ real!
precisa saber é o seguinte

Eu acho que existem muitas outras razões para usar a composição do que a herança ... Minha resposta fornece alguns exemplos sobre as vantagens de flexibilidade que ela oferece. Para um bom exemplo de implementação, verifique os artigos que eu vinculei.
coiote

Respostas:


12

Depende do que você quer dizer com 'bom'. Certamente fará o trabalho. Tem muitas partes de cima e de baixo. Você não os encontrará até que seu mecanismo seja muito mais complexo. Veja esta pergunta no SO , para discussão sobre o assunto.

De qualquer maneira, existem muitas opiniões, mas se o código do mecanismo ainda estiver no estágio inicial, será difícil explicar por que isso pode ser ruim.

Em geral, Scott Meyers tem grandes coisas a dizer sobre herança. Se você ainda está nessa fase de desenvolvimento, leia este livro (C ++ eficaz) antes de continuar. É um livro fantástico e é um requisito para qualquer codificador que trabalha em nossa empresa. Mas, para resumir o conselho: se você estiver escrevendo uma classe e pensando em usar herança, seja absolutamente claro que o relacionamento entre a classe e seu ancestral é um relacionamento 'é um'. Ou seja, todo B é um A. Não use apenas a herança como uma maneira fácil de fornecer acesso à funcionalidade fornecida por A, pois isso levará a graves problemas arquiteturais e existem outras maneiras de fazê-lo.

Simplificando; para um mecanismo de qualquer tamanho, você descobrirá rapidamente que os objetos raramente caem em uma estrutura hierárquica organizada para a qual a herança funciona. Use-o quando for apropriado, mas não caia na armadilha de usá-lo para tudo - aprenda outras maneiras de inter-relacionar objetos.


Eu apóio a opinião sobre herança. Minha regra pessoal é: não faça isso, a menos que você precise, ou fazê-lo de qualquer outra maneira seria estúpido. Em outras palavras, herança é uma má ideia em 99% dos casos (pelo menos :)). Além disso, pode ser bom separar a lógica do jogo (girar / mover) da renderização (generate_mesh ()). Outra coisa é fire () - considere ter uma lista de ações e uma função genérica de ação (my_action) - dessa forma, você não terá um bazilhão de funções diferentes espalhadas por todas as classes. Em qualquer um dos casos, mantenha-o simples e refatorar-se impiedosamente à medida que encontrar problemas.
Gilead

8

Para os dados lógicos e as funções dos objetos (entidades) do jogo, recomendo fortemente o uso da composição sobre a herança. Um bom começo seria a abordagem baseada em componentes. Está bem descrito neste artigo sobre Cowboy Programming, que descreve como essa arquitetura pode ser implementada e como beneficiou a franquia Tony Hawk.

Também este artigo sobre sistemas de entidades me inspirou quando o li pela primeira vez.

A herança é uma ótima ferramenta para o polimorfismo. Exceto em projetos muito simples, você deve evitar usá-lo como uma maneira de estruturar as entidades e a lógica do jogo. Se você optar pela herança, certamente terá que replicar e manter cópias do mesmo código para implementar funções que não podem ser herdadas de um pai comum. E você provavelmente começará a poluir suas classes com lógicas e dados mais comumente usados ​​para evitar a replicação de muito código.

Os componentes são muito úteis em várias situações. Você pode conseguir muito mais usando-os:

Flexibilidade com seus tipos de entidade : Há momentos no desenvolvimento de jogos em que algumas entidades evoluem repentinamente e enquanto o criador do jogo toma a decisão do desenvolvedor terá que implementá-lo. São necessárias apenas algumas etapas para adicionar um comportamento existente sem replicar o código. Os sistemas baseados em componentes permitem que mais alterações sejam feitas por não programadores. Por exemplo, cada tipo de entidade, em vez de ser representado por uma classe, pode ser representado por um arquivo de descrição que contém todos os nomes e parâmetros de componentes necessários para configurar o tipo. Isso permite que não programadores façam e testem alterações sem recompilar o jogo. Simplesmente editar os arquivos de descrição será suficiente para adicionar, remover ou alterar os componentes usados ​​por cada tipo de entidade.

Flexibilidade com instâncias particulares e cenários específicos : há casos em que você precisa que seu jogador seja capaz de equipar armas ou itens; você pode adicionar um componente de inventário ao seu jogador. durante o desenvolvimento do seu jogo, você precisará de outras entidades para fazer um inventário (inimigos, por exemplo). E em um ponto, você tem em seu cenário uma situação em que uma árvore deve conter algum tipo de item oculto. Com os componentes, você pode adicionar um componente de inventário até às entidades cujo tipo não foi projetado originalmente para ter um sem nenhum esforço adicional de programação.

Flexibilidade no tempo de execução : os componentes oferecem uma maneira muito eficaz de alterar o comportamento das entidades no tempo de execução. Você pode adicionar e remover componentes em tempo de execução e, assim, criar ou remover comportamentos e propriedades quando necessário. Eles também permitem que você separe tipos diferentes de dados em grupos que podem (algumas vezes) ser computados separadamente em paralelo em várias GPUs.

Flexibilidade no tempo : os componentes podem ser usados ​​para manter a compatibilidade entre as versões. Por exemplo, quando são feitas alterações entre os lançamentos de um jogo, você pode (nem sempre) simplesmente adicionar novos componentes que implementam os novos comportamentos e mudanças. Em seguida, o jogo instanciará os componentes certos anexados às suas entidades, dependendo do arquivo salvo salvo, conectado por pares ou servidor ao qual o jogador se une. Isso é extremamente útil em cenários em que você não pode controlar as datas de lançamento da nova versão em todas as plataformas (Steam, Mac App Store, iPhone, Android ...).

Para uma melhor compreensão, dê uma olhada nas perguntas marcadas com [componente-baseado] e [sistema-entidade] .


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.