Давайте напишем ... MMO! Часть 18: Процедурные ландшафты

Опубликовано NowhereMan -

May 1, 2011

Ну, у меня до сих пор тонны проблем, больших и маленьких, в различных портах проекта. На данный момент я чувствую, что было ошибкой пытаться поддерживать несколько платформ, и переключиться на OpenGL с вариациями в шейдерном языке и поддержкой драйверов. Один комментатор две части назад назвал его "batshit insane". Портирование израсходовало много энтузиазма, который я испытывал по отношению к этому проекту.

Чтобы исправить это, вместо того, чтобы делать вещи из списка ToDo, я вернулся к работе над мировым дизайном. В частности, я хотел реализовать свое кольцо астероидов.

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

На практике это означает, что у меня есть функция, которая принимает на вход позиции (x, y, z). Она производит на выходе некоторое значение, которое я буду использовать для генерации астероида в этой точке. Эта техника очень общая. Если бы мы делали ландшафт, то ввод мог бы быть просто (x, y), а на выходе была бы высота рельефа. Вторая функция может вывести тип рельефа (земля, вода, снег и т.д.) в каждой точке.

Пока игрок движется, мы постоянно вызываем эту функцию, чтобы сгенерировать новую местность. Эта функция должна производить один и тот же вывод для каждого входа (x,y,z), чтобы ландшафт был одинаковым для всех игроков, и одинаковым для одного игрока, когда он возвращается в место, которое он уже видел. Я снова использую Simplex noise.

Рис. 1: Немного симплексного шума

Я написал простую программу для отображения моей шумовой функции. Слева вид сверху в (x, z). Правая сторона - это вид сбоку (x, y). Я добавил функции панорамирования и масштабирования с помощью мыши и поместил текущую шкалу в верхнее левое положение. В этом виде мы смотрим на область шириной 40,000,000 единиц. Входной диапазон больше, чем может выдержать симплексная функция, поэтому она повторяется, как вы видите. На практике это не имеет значения, так как масштаб настолько велик.

Далее я умножаю этот шум на две функции. Расстояние от центра подается на другую функцию шума Simplex, чтобы получить кольца. Расстояние y используется для ограничения глубины кольца.

Рис. 2: Кольцо шума

Увеличив это, мы сможем увидеть больше деталей в кольцах, и получить лучшее представление о толщине.

Рис. 3: Детали кольца

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

Рис. 4: Положение астероидов

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

Рис. 5: Локальная группа астероидов

Результат показан в видео, здесь. Внимательно смотрите, и вы можете видеть, как астероиды появляются на расстоянии. Я покрасил их в синий цвет, потому что серые астероиды было труднее выделить на фоне звезд.

{"preview_thumbnail":"/index.php/sites/default/files/styles/video_embed_wysiwyg_preview/public/video_thumbnails/wLjgTBIxgbg.jpg?itok=eLtsT2Bh","video_url":"https://youtu.be/wLjgTBIxgbg","settings":{"responsive":1,"width":"854","height":"480","autoplay":0},"settings_summary":["Embedded Video (Адаптивный)."]}

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

Следующий шаг

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

В финальной игре есть набор объектов, которые он отображает, исходя из расстояния, от ближнего до дальнего:

  1. Аватары, объекты и здания, сделанные из кубов.
  2. Близлежащая местность, которая должна быть модифицируемой, чтобы можно было копать фундамент для своих зданий и т.д. Это будут полигоны, чтобы придать ему более естественный вид, чем кубовый ландшафт.
  3. Далекие здания, суммированные точками, а не кубами.
  4. Далекая местность, нарисованная в виде процедурных форм с низким разрешением. Любые пользовательские правки будут отсутствовать в этой версии.
  5. Близлежащие астероиды, процедурно сгенерированные с ограниченными деталями. Тем не менее, каждый близлежащий астероид должен быть разным. Я также могу сгенерировать текстуру, основываясь на текущем состоянии построения на этом астероиде. Текстура будет обновляться в фоне, а не в реальном времени.
  6. Небо, состоящее из кольца, солнца, туманности и звезд.

Осталось еще многое сделать, чтобы реализовать все это и сделать так, чтобы все отображалось гладко. Помимо кода самих объектов, есть еще несколько тонких проблем.

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

Даже в этом случае расстояния до местных астероидов велики (100 000 единиц и более). Чтобы избежать этого, я могу уменьшить координаты и размеры астероидов, чтобы удержать их в разумном диапазоне. Помните, что мы все еще используем Z-буфер для того, чтобы держать удаленные объекты в неведении. Есть ограничение на размер значения, которое я могу поместить в Z-буфер. Создание большого координатного пространства (большое значение для расстояния до задней плоскости) уменьшает разрешение буфера.

Дизайн мира

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

Рис. 6: Точное кольцо

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

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

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

Рис. 7: Интересный баг

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

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