Это может быть легко для игр с четко определенной областью действия, но вопрос об играх-песочницах, где игроку разрешено создавать и создавать что угодно .
Возможные техники:
- Используйте пулы памяти с верхним пределом.
- Периодически удаляйте ненужные объекты.
- Выделите дополнительный объем памяти в начале, чтобы потом ее можно было освободить как механизм восстановления. Я бы сказал, около 2-4 МБ.
Скорее всего, это произойдет на мобильных / консольных платформах, где память обычно ограничена в отличие от вашего 16 ГБ ПК. Я предполагаю, что у вас есть полный контроль над распределением / освобождением памяти и не участвует сбор мусора. Вот почему я отмечаю это как C ++.
Обратите внимание, что я не говорю об эффективном пункте C ++ 7 «Будьте готовы к условиям нехватки памяти» , хотя это и уместно, я хотел бы получить ответ, более связанный с разработкой игры, где вы обычно имеете больший контроль над тем, что происходит.
Резюмируя вопрос, как вы готовитесь к нехватке памяти для игр-песочниц, когда вы ориентируетесь на платформу с ограниченной памятью консоль / мобильный?
источник
Ответы:
Как правило, вы не справляетесь с нехваткой памяти. Единственный разумный вариант в программном обеспечении, таком большом и сложном, как игра, - просто сбить / утвердить / завершить работу распределителя памяти как можно скорее (особенно в отладочных сборках). Условия нехватки памяти тестируются и обрабатываются в некоторых основных системных или серверных программах в некоторых случаях, но обычно в других местах.
Когда у вас есть верхний предел памяти, вы просто гарантируете, что вам никогда не понадобится больше, чем этот объем памяти. Например, вы можете сохранить максимальное количество разрешенных NPC за один раз и просто прекратить порождать новых несущественных NPC, как только эта крышка будет достигнута. Для важных NPC вы можете либо заменить их второстепенными, либо иметь отдельный пул / колпачок для важных NPC, которые ваши дизайнеры знают для разработки (например, если у вас есть только 3 основных NPC, дизайнеры не будут помещать более 3 в область / кусок - хорошие инструменты помогут дизайнерам сделать это правильно, и тестирование, конечно, необходимо).
Действительно хорошая потоковая система также важна, особенно для игр с песочницей. Вам не нужно хранить все NPC и предметы в памяти. По мере того, как вы перемещаетесь по кусочкам мира, новые куски будут поступать внутрь, а старые куски вытекать. Как правило, они включают в себя NPC и предметы, а также ландшафт. Принимая во внимание эту систему, необходимо установить ограничения на дизайн и конструирование лимитов предметов, зная, что не более X старых чанков будут храниться и активно загружаться Y новых чанков будут загружаться, поэтому в игре должно быть место, чтобы сохранить все данные X + Y + 1 кусков в памяти.
Некоторые игры пытаются справиться с ситуациями нехватки памяти с помощью двухпроходного подхода. Помните, что в большинстве игр содержится много технически ненужных кэшированных данных (скажем, старых блоков, упомянутых выше), и распределение памяти может выглядеть примерно так:
Это последний шаг для решения непредвиденных ситуаций в выпуске, но во время отладки и тестирования вы, вероятно, должны сразу же аварийно завершить работу. Вы не хотите полагаться на такого рода вещи (особенно потому, что сброс кэшей может иметь некоторые серьезные последствия для производительности).
Вы можете также рассмотреть возможность выгрузки копий некоторых данных в высоком разрешении, например, вы можете сбросить текстуру уровней mipmap с более высоким разрешением, если вам не хватает памяти GPU (или любой памяти в архитектуре с разделяемой памятью). Это обычно требует много архитектурных работ, чтобы это того стоило.
Обратите внимание, что некоторые очень неограниченные игры-песочницы могут быть просто легко разбиты, даже на ПК (помните, что обычные 32-битные приложения имеют ограничение в 2-3 ГБ адресного пространства, даже если у вас есть компьютер с 128 ГБ ОЗУ; Битовая ОС и аппаратное обеспечение позволяют одновременно запускать больше 32-разрядных приложений, но ничего не могут сделать, чтобы 32-разрядный двоичный файл имел большее адресное пространство). В конце концов, у вас либо очень гибкий игровой мир, для которого в каждом случае потребуется неограниченное пространство памяти, либо у вас очень ограниченный и контролируемый мир, который всегда отлично работает в ограниченной памяти (или что-то среднее).
источник
Приложение обычно тестируется на целевой платформе с наихудшими сценариями, и вы всегда будете готовы к платформе, на которую вы нацелены. В идеале приложение никогда не должно аварийно завершать работу, но, кроме оптимизации под конкретные устройства, существует небольшой выбор, когда вы сталкиваетесь с предупреждением о нехватке памяти.
Лучше всего иметь заранее распределенные пулы, и игра с самого начала использует всю необходимую память. Если в вашей игре максимум 100 юнитов, то есть пул на 100 юнитов и все. Если 100 единиц превышают требования к памяти для одного целевого устройства, то можно оптимизировать единицу, чтобы использовать меньше памяти или изменить конструкцию до максимум 90 единиц. Не должно быть случаев, когда можно строить неограниченное количество вещей, всегда должен быть предел. Это было бы очень плохо для игры в песочнице
new
для каждого экземпляра, потому что вы никогда не сможете предсказать использование памяти, а сбой намного хуже, чем ограничение.Кроме того, дизайн игры должен всегда иметь в виду устройства с наименьшим целевым назначением, потому что если вы основываете свой дизайн на «неограниченных» вещах, то будет намного сложнее решить проблемы с памятью или изменить дизайн в дальнейшем.
источник
Что ж, вы можете выделить около 16 МБ (просто чтобы быть уверенным на 100%) при запуске или даже во
.bss
время компиляции и использовать «безопасный распределитель» с такой сигнатуройinline __attribute__((force_inline)) void* alloc(size_t size)
(__attribute__((force_inline))
это GCC /mingw-w64
атрибут, который вызывает встраивание критических участков кода) даже если оптимизации отключены, даже если они должны быть включены для игр) вместо того,malloc
чтобы пытаться,void* result = malloc(size)
и если это не удается, отбросьте кеш, освободите свободную память (или скажите другому коду использовать эту.bss
вещь, но это выходит за рамки этого ответа) и сбросить несохраненные данные (сохранить мир на диск, если вы используете концепцию чанков в стиле Minecraft, назовите что-нибудь подобноеsaveAllModifiedChunks()
). Тогда, еслиmalloc(16777216)
(выделение этих 16 МиБ снова) не удается (снова замените на аналог для.bss
), завершите игру и покажитеMessageBox(NULL, "*game name* couldn't continue because of lack of free memory, but your world was safely saved. Try closing background applications and restarting the game", "*Game name*: out of memory", MB_ICONERROR)
или альтернатива конкретной платформы. Собираем все вместе:Вы можете использовать аналогичное решение,
std::set_new_handler(myHandler)
гдеmyHandler
вызываетсяvoid myHandler(void)
, когда происходитnew
сбой:источник