Процедурное поколение, игровые обновления и эффект бабочки

10

ПРИМЕЧАНИЕ: я спрашивал об этом в Stack Overflow несколько дней назад, но у меня было очень мало просмотров и никакого ответа. Решил, что я должен спросить на gamdev.stackexchange вместо этого.

Это общий вопрос / просьба о совете по поддержке системы процедурного генерирования посредством нескольких обновлений после выпуска, не нарушая ранее сгенерированный контент.

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

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

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

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

Итак, мой вопрос на самом деле - запрос общих советов, методов и шаблонов дизайна для решения этой проблемы с эффектом бабочки, особенно в контексте пост-релизных обновлений игры. (Надеюсь, это не слишком широкий вопрос.)

В настоящее время я работаю в Unity3D / C #, хотя это не зависит от языка.

ОБНОВИТЬ:

Спасибо за ответы.

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

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

значение NULL
источник
Я голосую, чтобы закрыть этот вопрос как не по теме, потому что он слишком широк.
Almo
Я понимаю, что это очень широко. Вы бы порекомендовали мне попробовать форум Gamedev или что-то еще? Нет никакого способа сделать вопрос более конкретным. Я надеялся, что смогу услышать от кого-то с большим опытом в этой области, с некоторыми хитростями, которые мне не приходили в голову.
ноль
2
Альмо ошибается. Это совсем не широкий. Это отличный вопрос, достаточно узкий, чтобы дать хорошие ответы. Я думаю, что многие из нас, процедурные люди, часто обдумывали это.
инженер

Ответы:

8

Я думаю, что вы рассмотрели основы здесь:

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

  • Отслеживание версии генератора, используемой в файле сохранения, и соответствующее реагирование. Какие "подходящие" средства могут быть ...

    • Сохранение истории всех предыдущих версий генераторов в вашем исполняемом файле и использование того, которое соответствует сохранению. Это затрудняет исправление ошибок, если игроки продолжают использовать старые сейвы.
    • Предупреждение проигрывателя о том, что этот файл сохранения относится к старой версии, и указание ссылки для доступа к этой версии в виде отдельного исполняемого файла. Подходит для игр с кампаниями продолжительностью от нескольких часов до нескольких дней, плохо для кампании, в которой вы будете участвовать неделями или дольше.
    • Сохраняйте только последние nверсии генератора в вашем исполняемом файле. Если файл сохранения использует одну из этих последних версий, (предложите) обновить файл сохранения до последней версии. При этом используется соответствующий генератор для распаковки любого устаревшего состояния в литералы (или в дельты из выходных данных нового генератора на том же семени, если они очень похожи). С этого момента любое новое состояние исходит от новейших генераторов. Игроки, которые долго не играют, могут остаться позади. И в худшем случае вы в конечном итоге сохраняете все игровое состояние в буквальном виде, и в этом случае вы можете также ...
  • Если вы планируете часто изменять логику генерации и не хотите нарушать совместимость с предыдущими версиями, не полагайтесь на детерминизм генератора: сохраните все свое состояние в файле сохранения. (то есть. «Ядерная бомба с орбиты. Это единственный способ быть уверенным»)

ДМГригорий
источник
Если вы создали правила генерации, то есть ли способ изменить поколение? IE, учитывая игровое состояние, можете ли вы вернуть это в семя? Если это возможно с вашими данными, вместо того, чтобы связать игрока с другой версией игры, вы можете предоставить утилиту обновления, которая генерирует мир из начального числа с более старой системой, а затем использует сгенерированное состояние для создания начального числа для нового генератора. Возможно, вам придется предупредить игроков об ожидании конверсии.
Джо
1
Это не возможно вообще. Вы даже не гарантированы, что существует источник для нового генератора, который дает тот же результат, что и старый. Обычно эти начальные числа содержат около 64 битов, но число возможных миров, которые ваша игра может поддерживать, скорее всего, будет больше 2 ^ 64, поэтому каждый генератор генерирует только часть из них. Смена генератора, скорее всего, приведет к новому подмножеству уровней, которые могут иметь небольшое пересечение или даже вообще не пересекаться с предыдущей генераторной установкой.
DMGregory
Трудно было выбрать «правильный» ответ. Я выбрал этот, потому что он был лаконичен и ясно изложил основные вопросы. Спасибо.
ноль
4

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

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

Числа: периодические последовательности / слоты Допустим, у вас есть один генератор чисел, который служит для всего. Он не присваивает значения, он просто выплевывает числа в последовательности - как любой PRNG. Учитывая одинаковое начальное число в течение двух прогонов, мы получаем одинаковые последовательности, да? Теперь вы немного подумаете и решите, что, возможно, в вашей игре будет 30 аспектов, которые будут регулярно снабжаться случайным значением. Здесь мы назначаем циклическую последовательность из 30 слотов, например, каждое первое число в последовательности - это грубый рельеф местности, каждое второе число - возмущения местности ... и т. Д. ... каждое 10-е число добавляет некоторую ошибку в состояние ИИ для реалистичности. Итак, ваш период 30.

После 10 у вас будет 20 свободных слотов, которые вы можете использовать для других аспектов в процессе разработки игры. Стоимость здесь, конечно, состоит в том, что вы должны сгенерировать числа для слотов 11-30, даже если они не используются в данный момент , то есть завершить период, чтобы вернуться к следующей последовательности 1-10. Это имеет стоимость процессора, хотя она должна быть незначительной (в зависимости от количества свободных слотов). Другим недостатком является то, что вы должны быть уверены, что ваш окончательный дизайн может соответствовать числу слотов, которые вы сделали доступными в самом начале процесса разработки ... и чем больше вы назначаете в начале, тем больше «пустых» слотов вам, возможно, придется пройти через каждый, чтобы все заработало.

Последствия этого:

  • У вас есть один генератор, производящий числа для всего
  • Изменение количества аспектов, для которых необходимо создать числа, не повлияет на детерминизм (при условии, что ваш период достаточно велик, чтобы учесть все аспекты)

Конечно, будет длительный период, в течение которого ваша игра будет недоступна для публики - так сказать, в альфа-версии, - так что вы сможете сократить, скажем, с 30 до 20 аспектов, не влияя ни на каких игроков, только на себя, если вы поняли, что вы присвоили путь слишком много слотов на старте. Это, конечно, сэкономит некоторые циклы процессора. Но имейте в виду, что хорошая хеш-функция (которую вы можете написать самостоятельно) должна быть молниеносной. Поэтому запуск дополнительных слотов не должен быть дорогостоящим.

инженер
источник
Привет. Это звучит похоже на некоторые вещи, которые я делаю. Я обычно генерирую кучу подсемей заранее, основываясь на исходном семени мира. Недавно я начал предварительно генерировать длинный массив шумов, и тогда каждый «слот» - это просто указатель на этот массив. Таким образом, каждая подсистема может просто взять нужные семена и работать в изоляции. Другой замечательный метод - использовать координаты x, y для создания начального числа для каждого местоположения. Я использую код из ответа Euphoric на этой странице стека: programmers.stackexchange.com/questions/161336/…
null
3

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

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

Я не знаю примеров игр, в которых хранится множество версий генераторов, потому что постоянство в играх PCG необычно, поэтому permadeath часто идет рука об руку с PCG. Однако в одной и той же игре есть множество примеров нескольких компьютерных игр одного типа. Например, в Unangband есть много отдельных генераторов для комнат подземелий, и по мере добавления новых старые работают по-прежнему. Это зависит от вашей реализации. Один из способов обеспечить его поддержку - использовать сценарии для реализации ваших генераторов, изолируя их от остального кода игры.

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

Я поддерживаю площадь около 30000 квадратных километров, вмещая около 1 миллиона зданий и других объектов, в дополнение к случайным размещениям разных вещей. Открытый симулятор ofc. Сохраненные данные составляют около 4 ГБ. Мне повезло, что у меня есть место для хранения, но оно не безгранично.

Случайный случайный, неконтролируемый. Но можно немного посадить в клетку:

  • Контролируйте его начало, конец и конец (как упоминалось в других сообщениях, начальный номер и количество сгенерированных чисел).
  • Ограничить его числовое пространство, например. генерировать целые числа только от 0 до 100.
  • Сместить его числовое пространство, добавив значение (например, 100 + [сгенерированные числа от 0 до 100] производят случайные числа от 100 до 200)
  • Масштабируйте его (например, умножьте на 0,1)
  • И применять различные клетки вокруг него. Это сокращение, слом части поколения. Например. если генерируется в 2-мерном пространстве, можно поместить прямоугольник поверх пар чисел и отбросить то, что снаружи. Или круг, или многоугольник. Если в 3D-пространстве можно принять, например, только триплеты, которые находятся внутри сферы, или какую-то другую форму (теперь мыслят визуально, но это не обязательно имеет отношение к реальной визуализации или позиционированию).

Вот и все. Клетки также потребляют данные, к сожалению.

На финском языке есть поговорка: хайота и халице. Переводит в Разделяй и властвуй .

Я быстро отказался от идеи точного определения мельчайших деталей. Рэндом хочет свободы, поэтому он получил свободу. Пусть бабочка летит - внутри клетки. Вместо этого я сосредоточился на том, чтобы иметь богатый способ определить (и сохранить !!) клетки. Неважно, что это за машины, если они синие или темно-синие (скучный работодатель однажды сказал :-)). «Синий или темно-синий» - это (очень маленькая) клетка здесь, по цветовой гамме.

Что является управляемым, для контроля и управления числовыми пространствами?

  • Булева сетка (биты маленькие!)
  • Угловые точки
  • Как выглядит древовидная структура (= для перехода к «клеткам с клетками»)

С точки зрения обслуживания и совместимости версий ... мы имеем
: если version = n, то
: elseif version = m затем ...
Да, база кода увеличивается :-).

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

Не совсем несовместимо с забавным «ядерным оружием с орбиты», предложенным Д.Г.Григорием, но, возможно, использовать маленькие и точные ядерные бомбы? :-)

Штормград
источник
Спасибо за Ваш ответ. Это звучит как впечатляюще большая процедурная область для поддержания. Я могу видеть, как для такой большой площади, даже когда у вас есть доступ к большому количеству хранилища, все еще невозможно просто хранить все. Звучит так, как будто версионные генераторы должны будут идти вперед. Так и будет :)
null
Из всех ответов я больше всего думаю об этом. Мне понравились твои слегка философские описания вещей. Я нахожу термин «клетка» очень полезным при объяснении идей, так что спасибо за это. Пусть бабочка летать ... в клетке :)
нуль
PS Мне действительно любопытно узнать, над какой игрой вы работаете. Вы можете поделиться этой информацией?
ноль
Можно добавить еще одну вещь о числовом пространстве: стоит всегда оставаться близко к нулю. Что ты знал наверняка уже. Близкое к нулю дает лучшую числовую точность и большую отдачу для наименьших бит. Позже вы всегда можете сместить целую кучу близких к нулю чисел, но для этого вам понадобится только одно число. Точно так же вы можете (я бы почти сказал, что ДОЛЖЕН) переместить удаленный расчет ближе к нулю со смещением. - Штормград
Штормград
Оценивая предыдущее, рассмотрим движение небольшого транспортного средства с шагом в 1 кадр 0,01 [метра, единицы]: вы не можете точно рассчитать 10000,1 + 0,01 с 32-разрядной точностью (единично), но вы МОЖЕТЕ рассчитать 0,1 + 0,01. Следовательно, если «действие» происходит далеко (за горами :-)), не ходите туда, вместо этого двигайте горы к себе (двигайтесь с 10000, тогда вы находитесь на 0,1 сейчас). Действительно также для складских помещений. Можно быть жадным с хранением числовых значений, близких друг к другу. Храните общую часть из них один раз, а варианты по отдельности - можете сэкономить биты! Вы нашли ссылку? ;-)
Штормград