Exemplo de Projeto Orientado a Dados

8

Não consigo encontrar uma boa explicação do Design Orientado a Dados para um jogo genérico de zumbi (é apenas um exemplo, um exemplo bastante comum).

Você poderia fazer um exemplo do Design Orientado a Dados para criar uma classe zumbi genérica? O seguinte é bom?

Classe da lista de zumbis:

class ZombieList {
    GLuint vbo; // generic zombie vertex model
    std::vector<color>;    // object default color
    std::vector<texture>;  // objects textures
    std::vector<vector3D>; // objects positions
public:
    unsigned int create(); // return object id
    void move(unsigned int objId, vector3D offset);
    void rotate(unsigned int objId, float angle);
    void setColor(unsigned int objId, color c);
    void setPosition(unsigned int objId, color c);
    void setTexture(unsigned int, unsigned int);
    ...
    void update(Player*); // move towards player, attack if near
}

Exemplo:

Player p;

Zombielist zl;
unsigned int first = zl.create();
zl.setPosition(first, vector3D(50, 50));
zl.setTexture(first, texture("zombie1.png"));
...

while (running) { // main loop
    ...
    zl.update(&p);
    zl.draw(); // draw every zombie
}

Ou seria a criação de um recipiente Mundial genérico que contém todas as ações de bite(zombieId, playerId)para moveTo(playerId, vector)para createPlayer()para shoot(playerId, vector)para face(radians)/face(vector); e contém:

std::vector<zombie>
std::vector<player>
...
std::vector<mapchunk>
...
std::vector<vbobufferid> player_run_animation;
...

ser um bom exemplo?

Qual é a maneira correta de organizar um jogo com o DOD?

Sapato
fonte

Respostas:

11

Não existe um "jogo com DOD". Primeiramente, essa palavra da moda é um pouco confusa, porque cada sistema é projetado para dados. Cada programa trabalha em um conjunto de dados e faz certas transformações nele. Impossível fazer isso sem orientar o design para os dados. Portanto, não é mutuamente exclusivo no design "normal", mas adiciona restrições no layout da memória e na maneira como a memória é acessada, respectivamente.

A idéia por trás do DOD é empacotar e agrupar dados pertencentes a uma funcionalidade mais próximos em um bloco de memória contínuo, a fim de ter menos falhas de cache, livrar-se de funções virtuais e vtables, paralelização mais fácil, nenhum (ou mínimo) acesso aleatório à memória e escrever código para processadores altamente otimizados, como as SPUs da Cell no PS3, com recursos limitados de memória, otimizando o acesso à memória e os DMAs para e da memória principal.

Isso não significa simplesmente mudar tudo de "Array-of-Structures" (AoS) para "Structure of Arrays" (SoA), como mostrado nos exemplos aqui. Também pode significar misturar e intercalar e empacotar os dados pertencentes a uma funcionalidade, como por exemplo "posição" e "velocidade" para evitar pular na memória para a integração da posição.

No entanto, os sistemas DOD puros são muito difíceis de implementar, pois cada acesso ao ponteiro é uma violação desse conceito puro , pois você não acessa mais um bloco de memória contínuo, mas faz acessos aleatórios à memória desreferenciando um ponteiro. Isso é particularmente importante para escrever código para a SPU ao mover, por exemplo, um sistema de partículas da CPU para a SPU, mas no desenvolvimento diário normal de jogos não é importante. É uma maneira de otimizar a sub-funcionalidade, não para escrever jogos com ela (ainda, como o artigo de Noels explica).

Mike Acton, da Insomniac Games, tem um monte de material intestinal relacionado a esse tópico, você pode encontrar algumas coisas dele aqui , além dos artigos de Noel , ambos altamente recomendados.

Maik Semder
fonte
Uma coisa que eu gostaria de acrescentar a esta resposta: DOD não é tudo sobre sistemas SoA. Enquanto as estruturas SoA tendem a funcionar melhor para o DOD, elas nem sempre se encaixam no conceito real do DOD. O conceito por trás do DOD é simplesmente a idéia de que você está projetando o código em torno dos dados, e não o contrário, que é o método usual.
Gurgadurgen
0

Eu tenho procurado um bom exemplo disso também, mas com recursos limitados na rede e ninguém para me dizer como fazê-lo corretamente, eu fiz isso com a seguinte implementação. (pode não ser o melhor, mas segue a ideia básica)

Object
   //Basic data
   Vector3* Position;
   Vector3* Rotation;
   Vector3* Scale;



Car : Object
    float* acceleration;
    Object* GetObjectData();
    //invoke the updateCars, to update all cars.
    void    UpdateCar() { UpdateCars(Postion, Rotation, Scale);

    //Update all your objects in a big loop.
    void    UpdateCars(vector3* Position, vector3* Rotation, Vector3* scale);

Portanto, a implementação é mais ou menos assim: você tem uma classe de objeto base, que contém todos os dados comuns. Quando sua classe de carro é construída, você especifica a quantidade de dados que deseja agrupar e, portanto, possui memória suficiente para todos os objetos.

a partir daí, você pode adicionar identificadores fora do curso ou o que for necessário para sua implementação. mas tentei isso em um jogo mais simples, e funcionou bem legal.

Também não está muito longe do seu design e, francamente, não conheço nenhuma maneira mais eficaz de fazer isso.

Tordin
fonte
Alguns problemas do Departamento de Defesa: 1. Perder escala, com certeza. Os cálculos relativos à posição e rotação quase sempre não são afetados pela escala, portanto, praticamente nunca se acostuma e ocupa apenas espaço em cache. 2. Eu também perderia a rotação e a substituiria pela velocidade. Um carro deve se mover reto, mas sua velocidade determinará sua direção. O motorista pressiona a pétala do gás, mas a física move o carro. 3. Não herda de classes para os dados se você não planeja usá-los em quase todos os cálculos juntos. 4. Mesmo no OOP, os carros não se atualizam. Use funções gratuitas.
Gurgadurgen
Este é mais um exemplo, não um guia definitivo. é claro que você precisa escolher o melhor ajuste para sua própria implementação. (Como é indicado)
Tordin
Seu exemplo é um exemplo de abstração padrão de POO e tira pouca ou nenhuma vantagem das estratégias do Departamento de Defesa. O Departamento de Defesa trata dos dados, não do modelo. O fato de você ter um objeto "carro" é uma revelação absoluta de que esse não é um exemplo muito do DoD. Um carro é bastante específico, e o Departamento de Defesa tende a fazer polimorfismo na composição de objetos e na existência, em vez de na herança. Portanto, por exemplo, você pode ter um objeto que contém informações necessárias para uma transformação específica e criar uma matriz desses objetos, em vez de um objeto com informações para várias transformações.
Gurgadurgen 15/05