Pull to refresh

Битовый способ отображения тайловых карт

Game development
Translation
Original author: Sam Driver


Техника автоматического выбора нужного тайла из тайловой карты.

Сначала эта статья была ответом на вопрос на TIGSource, но мне показалось, что стоит его немного расширить и опубликовать отдельно.

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

Тайлы, учитывающие своих соседей


Тайлы в Super Mario не учитывают своих соседей: каменный блок всегда выглядит одинаково, и как отдельный фрагмент, и как часть стены.



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

Однобитная карта


Представим, что какими-нибудь хитрыми техниками мы создали схему уровня платформера, состоящую только из каменных блоков и «воздуха» между ними. Уровень можно представить как однобитное изображение, в котором состояние каждого пикселя определяется единственным битом (1 — каменный блок, 0 — «воздух»). Вот увеличенный пример части такого уровня с добавленными линиями сетки:



Набор тайлов (тайлсет)


Тайлсет — это набор графических изображений, которые можно использовать для заполнения карты. Тайлсет Mario довольно ограничен, он состоит из нескольких типов блоков и «декораций», но наш набор будет содержать множество изображений для каждого типа тайлов:



Оцениваем соседей


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



Значение для каждой точки находится исследованием её соседей и прибавлением значений для тех из них, в которых есть камень. Например, если у исследуемой точки сосед сверху тоже заполнен камнем, то ей присваивается значение 1. Если заполнены камнем соседи сверху и снизу, то точке присваивается значение 1 + 4, то есть 5.

Можно заметить, что присваиваемые значения направлений такие же, что и значения в позициях в двоичных числах, и это неудивительно: оба типа значений — это способы представления возможных комбинаций четырёх позиций, каждая из которых может быть во «включенном» или «выключенном» состояниях (камень или «воздух»).

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



Сложение тайлов


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



Отлично!

Двигаемся дальше


Часть первая: избавляемся от «воздуха»


Приведённый выше пример хорошо работает для висящих в воздухе платформ, но на самом деле не полностью обрабатывает два типа тайлов.

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

Мы можем использовать точно такую же систему оценки соседей, как и прежде, но нам потребуется способ, позволяющий при изучении точки определить, что в ней находится — трава или вода. Это реализовать очень просто — достаточно только добавить ещё одно значение самой точке, по тому же шаблону «2 в степени n» из других значений:



Давайте решим, что в случае наличия воды мы будем прибавлять к значению точки, а при наличии травы — не будем. То есть тайл травы, окружённый со всех сторон травой, будет иметь значение 0. Тайл травы с водой сверху и справа, имеет значение 1 + 8 = 9. Тайл воды, окружённый со всех сторон травой, будет иметь значение 16. Тайл воды, окружённый со всех сторон водой, имеет значение 1 + 2 + 4 + 8 + 16 = 31

Часть вторая: добавляем вариативности


Как обеспечить обработку других типов рельефа?

Допустим, в игре с видом сверху есть три типа рельефа: вода, трава и лес. Мы уже обрабатываем границы воды и травы, теперь нам нужно научиться обрабатывать границы воды и леса, а также травы и леса.

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



В двоичной системе использовался шаблон «2 в степени n», в новой же будем применяться шаблон «3 в степени n».

В троичной системе каждое положение имеет три возможных состояния: трава, вода, лес, или 0, 1, 2. Когда в текущей точке находится трава, мы игнорируем значение (умножаем его на 0). Когда в точке находится вода, мы прибавляем заданное значение (умножаем его на 1). В случае леса мы прибавляем удвоенное значение (умножаем его на 2).

То есть в случае тайла леса сверху и справа от которого находится вода, снизу лес, а слева трава: 81 * 3 + 1 * 2 + 3 * 1 + 9 * 3 + 27 * 0 = 275

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

Разумеется, таким же способом систему можно расширить на большее количество типов рельефа, но количество тайловых изображений намного увеличится. Поэтому я рекомендую наложить ограничения на то, какие тайлы могут соседствовать друг с другом. Например, если тайлы леса и воды никогда не могут граничить друг с другом, то в вышеуказанном примере понадобится на несколько сотен тайловых изображений меньше.
Tags:tilemapтайлытайловая графикаtileset
Hubs: Game development
Total votes 58: ↑58 and ↓0+58
Views14K