Подтвердить что ты не робот

Эффективный способ хранения и извлечения сложных объектов на С++?

В настоящее время я работаю над небольшой симуляцией подземелий. Игра довольно подробно, и я планирую со временем + 200 000 экземпляров класса, представляющего "монстра". Они содержат перки, навыки и историю этого монстра. Такие вещи, как количество зелий, которые он использовал, где он живет, каковы его маршруты патрулирования и т.д.

Я начал реализовывать это с помощью SQLite и использовал простую таблицу под названием "монстры", в которой были все данные. Это позволило мне использовать SQL-запросы для поиска монстров, необходимых для расчета моделирования для каждого кадра. Например: найти всех монстров, которые патрулировали точку А, или найти всех монстров, которые использовали Potion X и т.д. К сожалению, запрос SQLite несколько раз на каждый кадр быстро замедлял игру. Несмотря на то, что это 2D-игра, мне нужны драгоценные миллисекунды для моделирования расчетов.

Кроме того, в будущем мне понадобится JOIN для создания графиков: мне нужно знать, атаковал ли монстр другой монстр или монстр является частью команды другого монстра. Это еще больше замедлит работу.

Есть ли у кого-нибудь предложения о том, как подойти к этому?

Мои данные похожи на что-то вроде этого:

http://farm3.static.flickr.com/2450/3614560130_aaafa37387.jpg?v= 0

4b9b3361

Ответ 1

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

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

Хотя это может звучать "правильно", чтобы удерживать все детали монстра в одной записи (и, может быть, один объект, который отражает его в самом коде), это редко бывает хорошим решением. У вас должно быть четкое разделение между атрибутами объекта, смоделированными в вашем "мире", с теми атрибутами, которые смоделированы в программном обеспечении. Например, местоположение монстра, вероятно, очень часто изменяется, а его имя - нет. Поскольку вы сохраняете все данные в одной таблице, любое изменение местоположения выполняется в той же таблице, которая содержит все другие атрибуты, что делает ее очень тяжелым действием.

Вместо этого вы должны:

  • Отделить данные от разных таблиц
  • Выберите хорошие индексы для каждой таблицы
  • При необходимости используйте соединения (основанные, например, на ID монстра)

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

Если ваши запросы много полагаются на "географические" местоположения, и вы все еще хотите обрабатывать их с помощью реляционной БД, вы можете рассмотреть другие типы БД, которые имеют лучшую поддержку в пространственных запросах.

НТН!

Ответ 2

Если вы не используете систему компонентов сущностей, рассмотрите возможность переноса вашей игры на ее использование. Каждый бит связанных данных может храниться в автономном компоненте, а сам объект является непрозрачным идентификатором, который идентифицирует компонент. При использовании ECS вместо того, чтобы иметь игровой объект с кучей данных, зависающих от него, вы меняете отношения. Все компоненты определенного типа существуют в большом пуле вместе, а ваш объект - это просто идентификатор, который указывает, какой компонент в этом пуле им нужен. Это позволяет вам обновлять пакетные компоненты. Если у каждого монстра есть инвентарь, у вас есть инвентарь, все ваши компоненты Inventory могут храниться более или менее смежно в памяти. Это означает, что при их обработке вы обладаете высокой степенью кэширования, что может быть значительным повышением производительности.

Возможно также, что вы просто пытаетесь сделать слишком много каждого кадра. С помощью системы компонентов сущности вы можете дросселировать определенные подсистемы для каждого кадра X или каждые X секунд, если вам нужно. Может быть, компонент AI должен запускаться раз в секунду, чтобы подумать о том, что делать дальше, но им нужно постоянно двигаться, чтобы обновлять позицию в каждом кадре. Или, может быть, сборка одной из диаграмм и графиков занимает слишком много времени для завершения в одном кадре, поэтому вы рассчитали каждый второй кадр или разделили обработку на два кадра, чтобы вы перебирали половину ваших объектов на кадре, а остальные на втором Рамка. Там есть много способов разделить его.

Вот еще некоторые сведения о компонентах, если вы их не видели раньше

Ответ 3

Как я понял, главная проблема для вас - найти оптимизированный способ хранения ваших монстров. Например, вы можете использовать некоторые древовидные структуры данных для поиска действительно необходимых монстров в плоскости. Одной из этих структур данных является дерево BSP (дерево двоичного поиска). Это краткое описание https://en.wikipedia.org/wiki/Binary_space_partitioning. Структура графического представления Qt использует этот подход. Для получения дополнительной информации об этом вы можете увидеть на http://doc.qt.io/qt-4.8/graphicsview.html

Ответ 4

Не было принятого ответа, поэтому я постараюсь ответить на ваш вопрос. Но я буду честен с вами; -)

Во-первых: вы не указали слишком много деталей, это делает очень подходящий ответ трудным, если не невозможным. Для меня не ясно, сколько данных вы хотите обработать в какое время. Вы упоминаете фреймы, но это кадр, как в кадрах в секунду, или он больше похож на то, что я бы назвал "тиканием мира"? Если вы хотите запустить игру со скоростью > 30 кадров в секунду и обновить состояние всего мира 30 раз в секунду: Нет, я полагаю, вы можете забыть об этом (мы сделали паническую симуляцию примерно с 1000 узлов/лиц как часть лекции CUDA. И хотя этот способ был более простым, чем ваш симулятор, он едва мог работать в режиме реального времени на GTX780, поэтому я предполагаю, что опытный разработчик CUDA, скорее всего, достигнет предела с 10 000 узлов на этом аппаратное обеспечение - и вы хотите иметь > 20x узлы с более сложным AI/симулятором, чем "убежать от огня к следующему видимому выходу и затоптать других людей, если ваш уровень паники слишком высок + простое столкновение двух стенок" ).

Но если по кадру вы подразумеваете тикинг мирового моделирования, тогда да, это может быть выполнимо.

Теперь вам снова не хватает каких-либо деталей из ваших вопросов: планируете ли вы разработать выделенный MMO-сервер с монстрами и 200 тысячами игроков, или это локальный хост-одиночный игрок? Или что-то между ними (сетевая мультиплеерная RPG, скажем, максимум 16 игроков)?

Если у вас есть только несколько игроков (я так думаю, так как вы сказали 2D, и нет большой разницы между одним игроком или четырьмя): Не выполняйте все симуляции в полной мере сразу, Для полного погружения достаточно иметь детальное моделирование в районе игрока (ов). Как в ручке и бумаге: Как мастер игры (GM) у вас обычно есть только несколько ключевых событий, происходящих по всему миру, и вы составляете все остальное, когда вы идете/имеете какой-то грубый контур, что происходит в другом месте, но нет точных деталей. Если вы хороший ГМ, который достаточно убедителен для игроков, потому что кто-то заботится о том, что о нынешней позиции охранника в некотором тронном зале в 50 милях отсюда?

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

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

Кроме того, вы должны знать о своей архитектуре. Например, вы упомянули, что хотите знать, что монстр сделал зелье Trink X. Конечно, это просто записать в SQL, но вы не получите удовольствие от производительности. Или, по крайней мере, я не думаю, что хочу, и что после одной базовой лекции базы данных и "пусть писать высокопроизводительный SQL-сервер" - расширенная лекция [полное раскрытие: я плохо разбираюсь в высокопроизводительных SQL-запросах, так как я Обычно используется SQL. Плюс: кому нужна полная ACID для игры? Хорошо, для простоты вы можете поместить вещи, которые вам действительно не нужны, часто в базу данных SQL (высота монстра, вес, тексты вкусов,...?) Или ECS или какой-либо метод, который вы считаете лучшим. Но все, что вы хотите прикоснуться каждые несколько секунд, может войти в память. Я имею в виду, если вы храните 1kByte за монстра, чем вы на ~ 200MByte памяти для 200k монстров.

В любом случае, вернемся к вопросу, "какие монстры сделали trink potion X?": Почему вы хотите это знать? Использовать эффекты? Чтобы проверить, есть ли изнашивание эффектов? Я бы использовал очередь событий для этого: монстр пьет зелье силы → обновить инвентарь, дать ему бонус STR, вычислить тайм-аут и поставить в очередь событие "бонус STR износ за". Это возможно даже быстрее, чем обработка 200 Мбайт памяти, поскольку вы делаете "что нужно делать", за отметку - и не "проверяете все на все возможные условия, каждый тик".

Кроме того, подумайте, будьте осторожны с вашими алгоритмами: у вас есть человек, который знает отношения людей Y, аннотированные с помощью "public/private"? В зависимости от того, что вы хотите сделать на этом графике, вы можете столкнуться с NP-hard проблемами. Например, у вас может быть плохое время, если вы случайно попытаетесь решить производную от проблемы clique.

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

Ответ 5

Я бы не запрашивал базу данных SQL в каждом кадре, но вместо кеш монстры, которые, по вашему мнению, наиболее вероятны в расчетах.

Сколько монстров я должен кэшировать?

Если есть кто-то, кто знает, это вы! Тем не менее, я думаю, что только экспериментируя, вы найдете сладкое пятно для размера вашего кеша. Вы ищете реализации кеша и получите вдохновение, например .


Зачем использовать SQL на первом месте? Рассмотрите возможность записи на диск.


Ваше изображение предлагает график, так почему бы не сохранить то, что вам нужно в виде графика? Как вы предположили, Библиотека Boost Graph (BGL) может пригодиться! Также проверьте это: https://stackoverflow.com/questions/2751826/which-c-graph-library-should-i-use

Ответ 6

Есть два способа ограничить то, что происходит, и их можно использовать вместе.

  • Планирование событий.
  • Ограничение данных на видимый экран.

Планирование событий

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

Ограничение данных на видимый экран

sqlite имеет дерево R * для поддержки выбора только тех элементов в географическом регионе.

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

Насколько я могу судить, World of Warcraft имеет уравнение времени для позиции монстра....

MyXY = getPosition( me, currentTime );

Это было бы естественное состояние монстра, когда оно находится в стабильном "не рассматриваемом состоянии". Это означает, что действия для монстра вообще не нужно моделировать, когда они не видны.