Як створити міську вуличну мережу?


16

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

Оскільки це середньовічний світ, я не хочу плану сітки, як у багатьох сучасних містах. Я б в ідеалі віддав перевагу псевдовипадковій генерації великих проспектів і менших вулиць, де можна було б заблукати, але з все-таки певною логікою - не повним лабіринту.
Щось, що було б схоже на природно вирощене місто.

Для того, щоб зробити це просто, скажімо, мої міста будуть розташовані на рівній та стабільній місцевості, без проблем із перетином річки чи рельєфу. Я міг би спробувати інтегрувати це до рішення після.

Я не вирішив точного розміру чи розміщення для моїх міст, тому, якщо у вас є рішення, яке би працювало лише з містами точної форми (квадрат, коло, прямокутник тощо), я б прийняв це.


2
Можливо, ви захочете подивитися на генератор процедурного міста за допомогою програмного забезпечення Introversion, яке вони створили для Subversion. У той час як сама гра була скасована, з їх генератора є багато кадрів.
Філіп

Чи є у вас приклад того, що ви хочете (приклад із реального життя з цільового періоду часу, приклад з іншої гри, ескізу тощо)? Існує багато варіантів між "не сіткою" і "не повним лабіритом".
Пікалек

@Pikalek Я не даю більшої точності, бо не маю цього. Я не шукаю чогось дуже конкретного, будь-який приклад покоління, який не генерує ні лабіринт, ні план сітки, не міг би мене задовольнити.
Арактхор

Відповіді:


21

Хорошим місцем для початку процедурного генерування міста є процедурне моделювання міст Парафії та Мюллера . У їхньому документі представлена ​​система L, в якій правила щодо щільності населення та структури доріг (прямокутна сітка, радіальна та найменша зміна висоти) поєднуються і потім фіксуються для задоволення місцевих обмежень, таких як водні фронти та естетика доріг. Незважаючи на те, що результати цієї системи є вражаючими, її критикують як надмірно складну . Альтернативне рішення Барретта переглядається в блозі « Запчастини Руджіча» про запасні частини наступним чином:

  • вести перелік "запропонованих" доріг
  • оцінити їх у певному порядку
  • якщо вони прийнятні (з незначними змінами або без них)
  • зберігайте кожну прийняту дорогу, "пропонуючи" ще кілька відгалужень від неї

Цей підхід усуває більшість символів перезапису домашнього господарства, успадкованих у L-системі Парафія та Мюллера. Демонстрацію такого підходу ви можете побачити тут .

Перевага такого підходу полягає в тому, що це агностик форми міста - ви можете додавати контурні обмеження за потребою, тому форму вашого міста можна визначати відповідно до потреб вашого дизайну гри, а не алгоритму. Залежно від розміру вашого міста, це може бути досить добре. Ось результат наведеної вище демонстрації з обмеженням сегмента 100: введіть тут опис зображення Але якщо вам потрібно щось велике, у вас можуть виникнути проблеми; ось результат з обмеженням сегмента 500: введіть тут опис зображення

Частково ви можете скоригувати це, змінивши правила розгалуження дороги, уникаючи кутів на 90 градусів тощо. Якщо ваша компонування все ще надто регулярна, ось моя корекція:

Перетворіть міську сітку на графік, де кожна вулиця - край, а кожне перехрестя - вузол. Далі, використовуйте алгоритм, який ви віддаєте перевагу, щоб перетворити графік у лабіринт . Ось останній приклад, перетворений на лабіринт: введіть тут опис зображення

Тепер вихід має протилежну проблему, це занадто лабіринт. Але зараз ми можемо застосувати пару прийомів із таємних робіт генератора підземелля Джейміса Бака . По-перше, збільшуйте розрідженість, видаляючи коридори в глухий кут. Далі збільшуйте підключення, додаючи в дороги, які створюють петлі (тобто вводять цикли до графіка). Ось приклад результату: введіть тут опис зображення

Примітка: можна досягти того ж кінцевого результату безпосередньо з попередньої стадії орієнтованої на сітку (до створення лабіринту), лише застосувавши видалення краю до міської сітки. Проблема такого підходу полягає в тому, що ви повинні переконатися, що видалення краю не розділяє місто, тим самим роблячи частини недоступними.


6

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

Я припускаю, що ви шукаєте природно-хаотичне поселення.

Для цього я б спробував такий підхід:

  • Почніть з головної дороги, що йде від одного кінця до іншого (і в ідеалі з'єднує деякі інші населені пункти. Якщо ви хочете, створіть третю дорогу, щоб ви отримали перехрестя, на якому почати своє поселення.
  • Розташуйте кілька будинків уздовж дороги (лише з одного боку).
  • Тепер розгорніть цю дорогу вздовж будинків і додайте головну орієнтир з іншого боку (як правило, церкву, але це також може бути якийсь млин чи подібне). Це буде ваш центр / ринок.
  • Тепер виберіть дві позиції за межами площі з будинками та створіть нову дорогу, що огороджує будинки.
  • За бажанням створіть кілька менших союзників між будинками, що з'єднують стару і нову дорогу.
  • Тепер повторіть, поки ви не будете задоволені своїм "ядром":
    • Додайте ще кілька будинків.
    • Додайте ще одну дорогу, що їх огороджує.
    • Додайте назад алеї, що з'єднують дороги.
  • Як тільки ви задоволені цим, ви закінчите. Якщо це має бути місто, оточіть його стінами і повторіть останні кроки ще кілька разів, додавши додаткові будинки поза стінами.

3

Перш за все, існує безліч способів зробити процедурне покоління, і жоден з них зовсім не простий, я буду робити такий підхід до того, як ви могли б змусити його працювати.

Чи буде псевдокод в JS, так як це легше зрозуміти.

1º визначте точку входу, оскільки ви хочете побудувати середньовічне місто, ми розпочнемо з площі, тому скажімо, що у вашого міста буде 300 квадратних одиниць, а площа буде посередині нього (представлена ​​символом X).

       300
________________
|               |
|               |
|               | 300
|       X       |
|               |
|               |
|_______________|

const square = [ 150, 150 ];

2º зараз ми будемо проспекти, їх буде випадкове число, вони будуть прямими і почнуть із середнього квадрата або з інших проспектів

let avenues = [] // will contain start and end [[sx,sy],[ex,ey]]
const n_avenues = RANDOM(4, 8); // number of avenues
const n_av_from_square = RANDOM(0, avenues); // starting in the square

for av in av_from_square
  avenues.push(square, [RANDOM(0, 200) + 100, RANDOM(0, 200) + 100])
  // we want avenues to have, at least 100 units length, thats why we randomize just te last 200 units of the whole town size

Це повинно дати вам площу та пару головних вулиць

       300
________________
|   \\          |
|    \\         |
|     \\        | 300
|       X=====  |
|               |
|               |
|_______________|

Тепер ми повинні встановити алеї, які не починаються на головній площі, вони перетинатимуть інші проспекти

for av in (n_avenues - av_from_square){
  const av_to_intersect = avenues[RANDOM(0,avenues.length)];

  //check av_to... and get a perpendicular vector (explained bellow)
  av[0] = [ av_to_intersect[0][1], - av_to_intersect[0][0] ];
  av[1] = [ av_to_intersect[1][1], - av_to_intersect[1][0] ];

}

Для отримання перпендикулярних векторів вам потрібно поміняти місцями шнури x, y та заперечити нові y:

провела == x: noswiped.y, y: -1 * (noswiped.x)

Зараз у вас повинно бути щось подібне до цього, чи не схоже на місто? : P

       300
________________
|   \\  //      |
|    \\//  ||   |
|     \\   ||   | 300
|    //\X=====  |
|   //     ||   |
|          ||   |
|_______________|

3º тепер вам потрібно лише з’єднати проспекти з короткими вулицями, ви також можете породити випадкові площі по всьому місту і зробити те ж, що вище для всіх, або просто породити невеликі вулички з деяких відокремлених площ.

Пам’ятайте, чим коротші ваші вулиці, тим хаотично виглядає місто.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.