Я делаю игру с процедурно сгенерированным миром, созданным в начале игры, состоящим из нескольких областей, представленных сетками (скажем, 8x8, 9x6, размеры в идеале были бы произвольными). Эти области должны быть связаны друг с другом через список зависимостей.
Соединение существует, когда по крайней мере 3 пространства этой сетки открыты между этими двумя областями. В средней ячейке этой 3 области пространственного соединения находится дверной проем между областями:
Я пытался найти способ соединить их, но он становится все более сложным, чем больше областей вы должны рассмотреть одновременно.
Я попробовал некоторые бумажные прототипы, и, хотя это очень простой процесс, когда я выполняю это визуально, я не нашел хорошего набора математических выражений, который позволял бы мне размещать комнаты с той же эффективностью с помощью кода.
Вот «простой» пример, с которым я сейчас борюсь:
- Область 'a' должна быть связана с 'b' и 'c'
- Область 'b' должна быть связана с 'a' и 'd'
- Область 'c' должна быть связана с 'a' и 'd'
- Область 'd' должна быть связана с 'b' и 'c'
Для простоты рассмотрим, как мы размещаем комнаты в порядке их появления в списке (я пробовал другие). Поэтому я рассматриваю это как ваш стандартный процедурный алгоритм Dungeon Generation.
Мы ставим «а» в любом месте на доске, так как это первая область. Затем мы выбираем случайную стену и, поскольку с этой стеной ничего не связано, мы можем разместить там «b»:
Теперь нам нужно разместить «c», но «a» уже находится на доске и имеет занятую стену, поэтому мы решили разместить ее на другой стене. Но не все места размещения подойдут, потому что появляется «d», и его тоже нужно соединить с «b» и «c»:
Я пробовал возможное ограничение, что 2 комнаты с одинаковым набором зависимостей не могут находиться на противоположных стенах, но даже это не гарантирует успеха:
А в других случаях, когда области имеют разные размеры, может оказаться, что они находятся на противоположных стенах:
Кроме того, игнорирование используемой стены является ошибочным допущением, поскольку оно исключает допустимые решения:
Я пытался найти исследование других алгоритмов процедурного генерирования или аналогичных им, таких как алгоритмы оптимальной упаковки прямоугольников и компоновки графиков, но обычно эти алгоритмы не учитывают все ограничения этой проблемы и их трудно совместить друг с другом.
Я думал о множестве подходов, включая размещение области и возврата, пока не будет найдено подходящее размещение, но они кажутся очень зависимыми от проб и ошибок и дорогостоящими с точки зрения вычислений. Но, учитывая обширные исследования последних двух проблем, которые я упомянул, это может быть единственным / лучшим решением?
Я просто хотел посмотреть, сталкивался ли кто-то с подобными проблемами в прошлом или хочет помочь мне разобраться в этом и дать несколько советов о том, с чего мне начать с алгоритма. Или, если это не удастся, мне придется ослабить установленные мной ограничения.
источник
Ответы:
Это крутая проблема. Я считаю, что это может быть решено с помощью планирования действий в пространстве размещения помещений.
Определение государства мира следующим образом :
Определите ограничение как:
Где «смежный», как вы описали (разделяя как минимум 3 соседей)
Constraint называется недействительным всякий раз , когда две комнаты не смежные, и существуют обе комнаты.
Определите состояние, чтобы быть действительным, когда:
Определить действие как размещение комнаты, учитывая текущее состояние. Действие является действительным , когда результирующее состояние от действия является действительным. Поэтому мы можем сгенерировать список действий для каждого состояния:
Теперь, что вы остались является графиком , где Штаты являются узлами, а также действия ссылки. Цель состоит в том, чтобы найти государство, которое и действует, и все номера были размещены. Мы можем найти правильное размещение, выполнив произвольный поиск в графе, возможно, используя поиск в глубину. Поиск будет выглядеть примерно так:
Теперь качество создаваемого подземелья будет зависеть от порядка, в котором рассматриваются комнаты и действия. Вы можете получить интересные и разные результаты, вероятно, просто случайным образом переставляя действия, которые вы выполняете на каждом этапе, таким образом, выполняя случайный обход графика состояния-действия. Эффективность поиска будет во многом зависеть от того, насколько быстро вы сможете отклонить недействительные состояния. Это может помочь генерировать действительные состояния из ограничений всякий раз, когда вы хотите найти допустимые действия.
источник
Ваши приоритеты поколения находятся в конфликте. При создании уровней вашей первой целью должна быть сеть планарных (не перекрывающихся), связанных точек , независимо от масштаба. Затем приступайте к созданию комнат из точек в этой сети. Вообще говоря, создание форм комнаты - это ошибка. Сначала создайте соединение, а затем посмотрите, какие формы помещений могут быть размещены в нем.
Общий алгоритм
Создайте квантованную сетку пола достаточного размера, чтобы поддерживать свой уровень, используя двумерный массив или изображение.
Разбросайте точки случайным образом по этому пустому пространству. Вы можете использовать простую случайную проверку каждой плитки, чтобы увидеть, получает ли она точку, или использовать стандартное / гауссово распределение для разброса точек. Назначьте уникальный цвет / числовое значение для каждой точки. Это идентификаторы. (PS Если после этого шага вы чувствуете, что вам нужно увеличить свое пространство, обязательно сделайте.)
Для каждой такой сгенерированной точки последовательно увеличивайте круг или прямоугольник границ на один шаг (обычно со скоростью 0,5-1,0 ячеек / пикселей на шаг) в
x
иy
. Вы можете либо вырастить все границы параллельно, начиная с нулевого размера на одном и том же шаге, или вы можете начать выращивать их в разное время и с разными скоростями, отдавая предпочтение размеру тех, которые начинаются раньше (представьте, что сеянцы растут, где некоторые начать поздно). Под «ростом» я подразумеваю заполнение вновь увеличенных границ цветом / идентификатором, уникальным для начальной точки этих границ. Метафора для этого - держать маркеры на обратной стороне листа бумаги и наблюдать, как чернильные пятна разного цвета растут, пока они не встретятся.В какой-то момент границы некоторой точки и другой точки будут сталкиваться на этапе роста. Это точка, в которой вы должны прекратить увеличивать границы для этих двух точек - по крайней мере, в едином смысле, описанном в шаге 3.
Как только вы увеличите границы всех точек настолько, насколько это возможно, и остановите все процессы роста, у вас будет карта, которая должна быть в значительной степени, но не полностью заполнена. Теперь вы можете упаковать это пустое пространство, которое я буду считать белым, как если бы оно раскрашивалось на листе бумаги.
Пост-процесс Заполнение пространства
Для заполнения оставшихся пустых / пробелов на шаге 5 можно использовать различные методы:
возмущение
В качестве последнего шага, чтобы все выглядело более органично, вы могли бы по-разному воздействовать на краевые ячейки областей. Только убедитесь, что не заблокировали важные маршруты движения.
Теория, ради интереса
Это похоже на подход, использованный в диаграммах Вороного / триангуляции Делоне , за исключением того, что в приведенном выше примере вы явно не создаете ребра - вместо этого, когда сталкиваются ограничивающие области, рост прекращается. Вы заметите, что диаграммы Вороного заполняют пространство; это потому, что они не прекращают рост только при прикосновении, а скорее при некоторой номинальной степени совпадения. Вы можете попробовать подобное.
источник