Пример ориентированного на данные дизайна

8

Кажется, я не могу найти хорошее объяснение Data Oriented Design для обычной игры зомби (это всего лишь пример, довольно распространенный пример).

Не могли бы вы привести пример Data Oriented Design для создания универсального класса зомби? Это хорошо?

Класс списка зомби:

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
}

Пример:

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
}

Или создать общий Всемирный контейнер , который содержит все действия от bite(zombieId, playerId)до moveTo(playerId, vector)до createPlayer()до shoot(playerId, vector)до face(radians)/face(vector); и содержит:

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

быть хорошим примером?

Как правильно организовать игру с DOD?

башмак
источник

Ответы:

11

Не существует такой вещи, как «игра с DOD». Во-первых, это модное слово немного нечетко, потому что каждая система ориентирована на данные. Каждая программа работает с набором данных и вносит в него определенные преобразования. Это невозможно сделать, не ориентируя дизайн на данные. Таким образом, он не является взаимоисключающим с «нормальным» дизайном, но добавляет ограничения в структуру памяти и способ доступа к памяти соответственно.

Идея DOD состоит в том, чтобы упаковать и сгруппировать данные, относящиеся к одной функции, ближе друг к другу в непрерывный блок памяти, чтобы уменьшить количество кеш-памяти, избавиться от виртуальных функций и виртуальных таблиц, упростить распараллеливание, отсутствие (или минимальное) случайного доступа к памяти и писать код для высокооптимизированных процессоров, таких как SPU Cell в PS3 с его ограниченными ресурсами памяти, оптимизируя доступ к памяти и DMA в и из основной памяти.

Это не просто означает изменить все с «массива структур» (AoS) на «структуру массивов» (SoA), как показано в примерах здесь. Это также может означать смешивание и для чередования, и для объединения и упаковки данных, принадлежащих одной функциональности, например, «положение» и «скорость», чтобы избежать скачков в памяти для интеграции положения.

Однако системы с чистым DOD очень сложно реализовать, поскольку каждый доступ к указателю является нарушением этой чистой концепции, поскольку вы больше не обращаетесь к непрерывному блоку памяти, а делаете произвольный доступ к памяти, разыменовывая указатель. Это особенно важно для написания кода для SPU при перемещении, например, системы частиц из CPU в SPU, но при обычной ежедневной разработке игр это не важно. Это способ оптимизировать подфункцию, а не писать игры с ней (пока, как объясняется в статье Noels).

Майк Актон из Insomniac Games имеет много intesting материала по этой теме, вы можете найти некоторые его вещи здесь , а также статей Ноэля , и настоятельно рекомендуется.

Майк Земдер
источник
К этому ответу я хотел бы добавить одну вещь: DOD - это не ВСЕ о системах SoA. Хотя структуры SoA, как правило, лучше всего работают для DOD, они не всегда соответствуют реальной концепции DOD. Концепция DOD - это просто идея, что вы разрабатываете код вокруг данных, а не наоборот, что является обычным методом.
Гургадурген
0

Я искал хороший пример этого, но с ограниченными ресурсами в сети, и никто не сказал мне, как это сделать правильно, я сделал это со следующей реализацией. (это может быть не самым лучшим, но оно следует основной идее)

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);

Таким образом, реализация более или менее похожа на это: у вас есть базовый объектный класс, который содержит все общие данные. Когда ваш автомобильный класс создается, вы указываете, какой объем данных вы хотите объединить, и поэтому у вас будет достаточно памяти для всех объектов.

оттуда вы можете добавить идентификаторы или что-то, что когда-либо кажется необходимым для вашей реализации. но я попробовал это на более простой игре, и она работала довольно аккуратно.

Это не так далеко от вашего дизайна, и, честно говоря, я не знаю более эффективного способа сделать это.

Tordin
источник
Несколько вопросов DOD: 1. Потерять масштаб, конечно. На вычисления относительно положения и поворота практически всегда не влияет масштаб, поэтому он почти никогда не будет использоваться, а просто займет место в кеше. 2. Я бы также потерял вращение и заменил бы его на скорость. Автомобиль предназначен для движения прямо, но его скорость в конечном итоге будет определять его направление. Водитель нажимает на газовый лепесток, но физика двигает машину. 3. Не наследуйте от классов данные, если вы не планируете использовать их практически во всех вычислениях вместе. 4. Даже в ООП автомобили не обновляют друг друга. Используйте бесплатные функции.
Гургадурген
Это скорее пример, а не окончательное руководство. Вы, конечно, должны выбрать лучший вариант для своей реализации. (как говорится)
Тордин
Ваш пример является примером стандартной абстракции ООП и практически не использует стратегии DoD. DoD о данных, а не о модели. Тот факт, что у вас даже есть «автомобильный» объект, - это пустяк, который не является примером DoD. Автомобиль довольно специфичен, и DoD имеет тенденцию делать композицию и полиморфизм объектов на основе существования, а не наследование. Так, например, у вас может быть объект, который содержит информацию, необходимую для конкретного преобразования, и создать массив из этих объектов вместо объекта с информацией для нескольких преобразований.
Гургадурген