Engine & platform

Процедурные шаблоны, которые можно использовать с Tilemaps, часть 1

ETHAN BRUINS / UNITY TECHNOLOGIES Contributor
May 29, 2018|10 Мин
Процедурные шаблоны, которые можно использовать с Tilemaps, часть 1
Эта веб-страница была переведена с помощью машинного перевода для вашего удобства. Мы не можем гарантировать точность или надежность переведенного контента. Если у вас есть вопросы о точности переведенного контента, обращайтесь к официальной английской версии веб-страницы.

Многие создатели используют процедурную генерацию, чтобы внести разнообразие в свою игру. Среди них можно отметить такие игры, как Minecraft, а в последнее время - Enter the Gungeon и Descenders. В этом посте рассказывается о некоторых алгоритмах, которые можно использовать с помощью Tilemap, представленной как 2D-функция в Unity 2017.2, и RuleTile.

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

О чем эта запись в блоге?

Мы рассмотрим некоторые из наиболее распространенных методов создания процедурного мира, а также несколько собственных вариантов, которые я создал. Вот пример того, что вы сможете создать после прочтения этой статьи. Три алгоритма работают вместе, чтобы создать одну карту, используя Tilemap и RuleTile:

Процедурный мир - введение
Процедурный мир - введение

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

Полезно знать, прежде чем читать дальше:

Мы различаем, что является плиткой, а что нет, с помощью двоичного кода. 1 - включено, 0 - выключено.

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

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

Я часто использую Mathf.FloorToInt(), потому что система координат Tilemap начинается слева внизу, а использование Mathf.FloorToInt() позволяет округлять числа до целого числа.

Весь код, представленный в этой статье, написан на C#.

Создать массив

GenerateArray создает новый массив int заданного размера. Мы также можем сказать, каким должен быть массив - полным или пустым (1 или 0). Вот код:

Неизвестный тип блока "codeBlock", укажите для него сериализатор в свойстве `serializers.types`.

Карта рендера

Эта функция используется для рендеринга нашей карты на карту плиток. Мы циклически проходим по ширине и высоте карты, размещая плитки только в том случае, если в массиве есть 1 в проверяемом месте.

Неизвестный тип блока "codeBlock", укажите для него сериализатор в свойстве `serializers.types`.

Обновить карту

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

Неизвестный тип блока "codeBlock", укажите для него сериализатор в свойстве `serializers.types`.

Шум Перлина

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

Простой

Эта генерация представляет собой простейшую форму внедрения шумов Перлина в генерацию уровней. Мы можем использовать функцию Unity для Perlin Noise, чтобы помочь нам, поэтому нет необходимости в сложном программировании. Мы также собираемся убедиться, что у нас есть целые числа для нашего тайлмапа, используя функцию Mathf.FloorToInt().

Неизвестный тип блока "codeBlock", укажите для него сериализатор в свойстве `serializers.types`.

Вот как это выглядит при рендеринге на карту плиток:

Создание карт
Создание карт
Сглаженный

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

Неизвестный тип блока "codeBlock", укажите для него сериализатор в свойстве `serializers.types`.

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

Неизвестный тип блока "codeBlock", укажите для него сериализатор в свойстве `serializers.types`.

Сглаживание происходит на следующих этапах:

- Получение текущей и последней позиции

- Получите разницу между двумя позициями, ключевая информация, которая нам нужна, - это разница по оси y

- Далее определяем, на сколько нужно изменить хит, для этого делим разность y на интервальную переменную.

Теперь мы можем приступить к установке позиций. Мы будем стремиться к нулю

Когда мы достигнем 0 на оси y, мы добавим изменение высоты к текущей высоте и повторим процесс для следующей позиции x

После того как мы выполнили все позиции между последней и текущей, переходим к следующей точке

Если интервал меньше единицы, мы просто используем предыдущую функцию, чтобы сделать работу за нас.

Неизвестный тип блока "codeBlock", укажите для него сериализатор в свойстве `serializers.types`.

Давайте посмотрим, как это выглядит в визуализации:

Разглаживание
Разглаживание
Случайная прогулка
Случайная прогулка Топ

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

Неизвестный тип блока "codeBlock", укажите для него сериализатор в свойстве `serializers.types`.

Это поколение дает нам более плавную высоту по сравнению с поколением шума Perlin.

Случайная прогулка Топ сглаженный

Это поколение дает нам более плавную высоту по сравнению с поколением шума Perlin.

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

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

Теперь мы знаем, что нам нужно добавить. Давайте посмотрим на функцию:

Неизвестный тип блока "codeBlock", укажите для него сериализатор в свойстве `serializers.types`.

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

Подбрасывание монеты
Заключение

Надеюсь, это вдохновило вас на то, чтобы начать использовать процедурные генерации в своих проектах. Если вы хотите узнать больше о процедурной генерации карт, загляните на сайт Procedural Generation Wiki или Roguebasin.com- это отличные ресурсы.

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

Если вы сделали что-то крутое с использованием процедурной генерации, не стесняйтесь написать мне в Twitter или оставить комментарий ниже!

Процедурная генерация 2D на выставке Unite Berlin

Хотите узнать о нем больше и получить живую демонстрацию? Я также рассказываю о процедурных паттернах для использования с тайлмапами на Unite Berlin, в мини-театре экспо-холла 20 июня. Я буду рядом после выступления, если вы захотите пообщаться лично!