| сохранено
Собственный Tilemap движок в черновиках Из песочницы
В данной статье я научу Вас создавать уровни практически для игр любого жанра и сделать их разработку значительно проще. Мы создадим tile map движок, который Вы сможете использовать в своим проектах. Мы будет использовать Haxe и OpenFL, но процесс создания схож для многих языков.
Немного о том, что будет описано в статье
Конечно, Вы можете найти определение тайловой графики в википедии, но, чтобы понять, что это, достаточно усвоить несколько вещей
В нашей статье плитки будут иметь прямоугольную форму. Существует несколько крутых фишек, которые Вы получаете, используя тайловую графику. Самое крутое — нет необходимости рисовать огромные изображения для каждого уровня. Пятьдесят изображений с разрешением 1280x768px для 50-ти уровневой игры против одного изображения с сотней плиток имеют огромные различия. Другое преимущество состоит в том, что размещать предметы, используя тайловую графику, становится заметно проще.
Первое, что нужно для построения движка — набор плиток. У Вас есть два варианта: использовать уже готовые tiles или сделать свои собственные. Если Вы решите использовать готовы изображение, то их можно легко найти по всему интернетом. Минусом является то, что эта графика не была сделана специально для вашей игры. С другой стороны, если Вы просто экспериментируете, то данный вариант вполне подойдет.
Думаю, что с поиском плиток возникнуть проблем не должно, поэтому я не буду подробно на этом останавливаться.
Существует множество замечательных инструментов, позволяющий легко создавать свои изображения. Многие разработчики для своей работы используют данные инструменты
Это — самые популярные инструменты для создания пиксель-арта. Если Вам нужно что-то более мощное, то GIMP идеально подойдет.
Как только Вы выбрали программу, Вы можете начать экспериментировать со своими собственными тайлами. Поскольку данная статья покажет, как создать собственный движок тайловой графику, то я не буду подробно останавливаться на самих плитках.
Когда у нас имеется все необходимое, мы может погрузиться непосредственно в само программирование.
Давайте начнем с простейшей задачи, отображение одной плитки на экране. Убедитесь, что все Ваши изображения имеют один размер и сохранены в разных файлах (о хранении спрайтов в одном фале мы поговорим позже).
Как только все Ваши плитки находятся в папке с ресурсами проекта, Вы можете написать простой
Сейчас, все, что мы делаем — помещаем плитку на экран. Единственная вещь, которую делает класс — импортирование изображение из папки с ресурсами и добавление в качестве дочернего объекта. То, как будет выглядеть данный класс очень сильно зависит от языка программирования, который Вы используете.
Теперь, когда у нас есть класс
Класс
Когда запускается игра, будет вызвана функция
Следующий шаг — придумать метод отображения всех плиток. Самый простой метод — заполнить массив числами, каждое из которых соответствует какой-то плитке. Затем, Вы просто перебираете все элементы массива и отображаете их.
У нас есть выбор: использовать обычный массив или матрицу. Если Вы не знакомы с матрицами, то просто знайте, что это массив, который содержит в себе еще массивы. Большинство языков программирования представляют это, как nameOfArray[x][y].
X и Y мы используем, как координаты на экране. Может быть нам удастся использоваться эти X и Y для отображения наших плиток? Итак, давайте взглянем на матрицу
Заметим, что нулевой элемент в данном массиве — массив из пяти чисел. Это значит, что сначала вы получаете элемент y, а затем x. Если Вы попытаетесь взять элемент [1][0], то Вы получите доступ к шестой плитке.
Если Вы не понимаете, как работают матрицы, не беспокойтесь. В данной статье я буду использовать обычный массив, чтобы упростить нашу задачу.
Пример Выше показывает, что использовать обычный массив — гораздо проще. Мы можем отобразить конкретный элемент, вычислив координаты, используя простую формулу.
Теперь, давайте напишем код, создающий наш массив. Заполним его 1. Цифра один будет означать ID нашей первой плитки.
Для хранения массива нам нужно создать переменную внутри класса
Может выглядеть немного странно, поэтому я поясню.
Имя переменной — map, а тип — Array (массив). означает, что массив содержит числа.
Теперь, добавим немного кода в наш конструктор нашего класса, чтобы инициализировать нашу карту
Этот фрагмент создает пустой массив, который скоро мы заполним. Но сначала, давайте объявим несколько переменных, которые помогут нам с математикой
Эти переменные являются
Здесь мы задаем значение 10 для переменной w и 6 для h, как я уже сказал. Дальше, мы должны пройтись циклом по массиву, чтобы уместить 10 * 6 чисел.

Теперь у нас есть простенькая карта, но нам же нужно расположить плитки правильно, так? Для этого вернемся обратно в класс
Когда мы вызываем функцию
Единственная вещь, которую осталось сделать — описать в классе
Да! Все правильно. Экран заполнен плитками. Давайте разберемся, что происходит выше
Давайте обсудим формулу, о которой я упоминал выше.
Мы вычисляем

Для
Наконец, я бы хотел сказать чуть-чуть о прокрутке уровня. Обычно у Вас не получится создать уровень, который полностью будет помещаться на экране. Ваши карты будут заметно больше, чем экран, поэтому нет необходимости рисовать ту часть карты, которую пользователь не увидит. Это можно поправить при помощи все той же математики. Вы должны считать, какие плитки будут видны на экране, а какие — нет.
Например: Экран размером 500х500, а плитки — 100х100, а Ваш мир — 1000х1000. Вам нужно делать простую проверку перед отрисовкой плитки.
Все, что нам осталось — создать разные типы плиток и отобразить что-нибудь красивое.
Окей, теперь у нас есть карта, полная одинаковых элементы. Не плохо было бы иметь более одного типа элементов, а это значит, что нам придется изменить наш конструктор в классе
Теперь у нас есть шесть разных типов плиток. Мне нужна конструкция
Вернемся в конструктор класса
Единственное изменение состоит в том, что теперь мы передаем конструктору тип блока. Если бы Вы попробовали запустить программу без данной правки, до выполнение окончилось бы ошибкой.
Сейчас все стало на свои места, но нужно придумать дизайн карты вместо того, чтобы заполнять ее случайными блоками. Первое, удалите цикл в конструкторе
Если отформатировать массив, как сделано у меня, то можно легко увидеть примерный облик уровня. Но никто не запрещает Вам забить массив просто в одну строку.
При запуске программы Вы увидите несколько ошибок. Проблема заключается в том, что наша карта содержит нулевой тип блока, который не имеет изображения и наш класс просто не знает, что с этим делать. Исправим это недоразумение
Эта короткая проверка избавит нас от надоедливой ошибки с нулевым указателем. Последнее изменение затронет функцию
Благодаря этим двум простым условиям Вы теперь можете запустить игру и увидеть простой уровень. Тайлы, которые умеют id 0 мы воспринимаем, как пустое место. Добавьте какой-нибудь фон. чтобы это выглядело более привлекательно.

Вы только что создали tale-based движок. Теперь Вы знаете, что такое тайловая графика и как ей пользоваться. Вы научились создавать и редактировать уровни. Но не останавливайтесь на этом, ведь столько всего еще можно улучшить в нашем движке.
Немного о том, что будет описано в статье
- Что такое tile-based игра?
- Создание или поиск собственных «плиток»
- Написание кода для отображения уровня
- Редактирование уровней
Что такое tile-based игра?
Конечно, Вы можете найти определение тайловой графики в википедии, но, чтобы понять, что это, достаточно усвоить несколько вещей
- Tile — плитка — маленькой изображение, обычно прямоугольной формы, которое выступает в роли кусочка пазла при построении больших изображений
- Карта — группа плиток, объединенных вместе
- Tile-based отсылает нас к методу созданий уровней в играх. Код размещает плитки в заранее определенных местах
В нашей статье плитки будут иметь прямоугольную форму. Существует несколько крутых фишек, которые Вы получаете, используя тайловую графику. Самое крутое — нет необходимости рисовать огромные изображения для каждого уровня. Пятьдесят изображений с разрешением 1280x768px для 50-ти уровневой игры против одного изображения с сотней плиток имеют огромные различия. Другое преимущество состоит в том, что размещать предметы, используя тайловую графику, становится заметно проще.
Создание или поиск плиток
Первое, что нужно для построения движка — набор плиток. У Вас есть два варианта: использовать уже готовые tiles или сделать свои собственные. Если Вы решите использовать готовы изображение, то их можно легко найти по всему интернетом. Минусом является то, что эта графика не была сделана специально для вашей игры. С другой стороны, если Вы просто экспериментируете, то данный вариант вполне подойдет.
Думаю, что с поиском плиток возникнуть проблем не должно, поэтому я не буду подробно на этом останавливаться.
Создание плиток
Существует множество замечательных инструментов, позволяющий легко создавать свои изображения. Многие разработчики для своей работы используют данные инструменты
Это — самые популярные инструменты для создания пиксель-арта. Если Вам нужно что-то более мощное, то GIMP идеально подойдет.
Как только Вы выбрали программу, Вы можете начать экспериментировать со своими собственными тайлами. Поскольку данная статья покажет, как создать собственный движок тайловой графику, то я не буду подробно останавливаться на самих плитках.
Написание кода
Когда у нас имеется все необходимое, мы может погрузиться непосредственно в само программирование.
Отображение одной плитки на экране
Давайте начнем с простейшей задачи, отображение одной плитки на экране. Убедитесь, что все Ваши изображения имеют один размер и сохранены в разных файлах (о хранении спрайтов в одном фале мы поговорим позже).
Как только все Ваши плитки находятся в папке с ресурсами проекта, Вы можете написать простой
Tile
класс. Вот пример на Haxeimport flash.display.Sprite;
import flash.display.Bitmap;
import openfl.Assets;
class Tile extends Sprite {
private var image:Bitmap;
public function new() {
super();
image = new Bitmap(Assets.getBitmapData("assets/grassLeftBlock.png"));
addChild(image);
}
}
Сейчас, все, что мы делаем — помещаем плитку на экран. Единственная вещь, которую делает класс — импортирование изображение из папки с ресурсами и добавление в качестве дочернего объекта. То, как будет выглядеть данный класс очень сильно зависит от языка программирования, который Вы используете.
Теперь, когда у нас есть класс
Tile
, нам нужно создать экземпляр Tile
и добавить его в нашем главном классе.import flash.display.Sprite;
import flash.events.Event;
import flash.Lib;
class Main extends Sprite {
public function new() {
super();
var tile = new Tile();
addChild(tile);
}
public static function main() {
Lib.current.addChild(new Main());
}
}
Класс
Main
создает новый объект Tile
при вызове конструктора и добавляет его в список отображения.Когда запускается игра, будет вызвана функция
main()
и новый объект типа Main
будет добавлен на сцену. Обратите внимание, что Ваша новая плитка появится в левом верхнем углу экрана.Использование массивов для отображения всех плиток
Следующий шаг — придумать метод отображения всех плиток. Самый простой метод — заполнить массив числами, каждое из которых соответствует какой-то плитке. Затем, Вы просто перебираете все элементы массива и отображаете их.
У нас есть выбор: использовать обычный массив или матрицу. Если Вы не знакомы с матрицами, то просто знайте, что это массив, который содержит в себе еще массивы. Большинство языков программирования представляют это, как nameOfArray[x][y].
X и Y мы используем, как координаты на экране. Может быть нам удастся использоваться эти X и Y для отображения наших плиток? Итак, давайте взглянем на матрицу
private var exampleArr = [ [0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
Заметим, что нулевой элемент в данном массиве — массив из пяти чисел. Это значит, что сначала вы получаете элемент y, а затем x. Если Вы попытаетесь взять элемент [1][0], то Вы получите доступ к шестой плитке.
Если Вы не понимаете, как работают матрицы, не беспокойтесь. В данной статье я буду использовать обычный массив, чтобы упростить нашу задачу.
private var exampleArr = [ 0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0 ];
Пример Выше показывает, что использовать обычный массив — гораздо проще. Мы можем отобразить конкретный элемент, вычислив координаты, используя простую формулу.
Теперь, давайте напишем код, создающий наш массив. Заполним его 1. Цифра один будет означать ID нашей первой плитки.
Для хранения массива нам нужно создать переменную внутри класса
Main
private var map:Array<Int>;
Может выглядеть немного странно, поэтому я поясню.
Имя переменной — map, а тип — Array (массив). означает, что массив содержит числа.
Теперь, добавим немного кода в наш конструктор нашего класса, чтобы инициализировать нашу карту
map = new Array<Int>();
Этот фрагмент создает пустой массив, который скоро мы заполним. Но сначала, давайте объявим несколько переменных, которые помогут нам с математикой
public static var TILE_WIDTH = 60;
public static var TILE_HEIGHT = 60;
public static var SCREEN_WIDTH = 600;
public static var SCREEN_HEIGHT = 360;
Эти переменные являются
public static
для того, чтобы дать нам доступ к ним из любого места нашей программы. Вы могли заметить, что исходя из данных чисел, мы будем решать, сколько ячеек хранить в массиве.var w = Std.int(SCREEN_WIDTH / TILE_WIDTH);
var h = Std.int(SCREEN_HEIGHT / TILE_HEIGHT);
for (i in 0...w * h) {
map[i] = 1
}
Здесь мы задаем значение 10 для переменной w и 6 для h, как я уже сказал. Дальше, мы должны пройтись циклом по массиву, чтобы уместить 10 * 6 чисел.

Теперь у нас есть простенькая карта, но нам же нужно расположить плитки правильно, так? Для этого вернемся обратно в класс
Tile
и создадим функцию, позволяющую нам это сделатьpublic function setLoc(x:Int, y:Int) {
image.x = x * Main.TILE_WIDTH;
image.y = y * Main.TILE_HEIGHT;
}
Когда мы вызываем функцию
setLoc()
, мы передаем координаты x и y. Функция берет эти значения и переводит их в координаты в пикселях, умножая на TILE_WIDTH
и TILE_HEIGHT
Единственная вещь, которую осталось сделать — описать в классе
Main
процесс создания и позиционирования плиток на картеfor (i in 0...map.length) {
var tile = new Tile();
var x = i % w;
var y = Math.floor(i / w);
tile.setLoc(x, y);
addChild(tile);
}
Да! Все правильно. Экран заполнен плитками. Давайте разберемся, что происходит выше
Формула
Давайте обсудим формулу, о которой я упоминал выше.
Мы вычисляем
x
, присваивая ему остаток от деления i
на w
. Это делается для того, чтобы вернуть x
значение 0 в начале каждой линии.
Для
y
мы берем floor()
от i / w.Наконец, я бы хотел сказать чуть-чуть о прокрутке уровня. Обычно у Вас не получится создать уровень, который полностью будет помещаться на экране. Ваши карты будут заметно больше, чем экран, поэтому нет необходимости рисовать ту часть карты, которую пользователь не увидит. Это можно поправить при помощи все той же математики. Вы должны считать, какие плитки будут видны на экране, а какие — нет.
Например: Экран размером 500х500, а плитки — 100х100, а Ваш мир — 1000х1000. Вам нужно делать простую проверку перед отрисовкой плитки.
Все, что нам осталось — создать разные типы плиток и отобразить что-нибудь красивое.
Разные типы плиток
Окей, теперь у нас есть карта, полная одинаковых элементы. Не плохо было бы иметь более одного типа элементов, а это значит, что нам придется изменить наш конструктор в классе
Tile
public function new(id:Int) {
super();
switch(id) {
case 1:
image = new Bitmap(Assets.getBitmapData("assets/grassLeftBlock.png"));
case 2:
image = new Bitmap(Assets.getBitmapData("assets/grassCenterBlock.png"));
case 3:
image = new Bitmap(Assets.getBitmapData("assets/grassRightBlock.png"));
case 4:
image = new Bitmap(Assets.getBitmapData("assets/goldBlock.png"));
case 5:
image = new Bitmap(Assets.getBitmapData("assets/globe.png"));
case 6:
image = new Bitmap(Assets.getBitmapData("assets/mushroom.png"));
}
addChild(image);
}
Теперь у нас есть шесть разных типов плиток. Мне нужна конструкция
switch
, чтобы выбирать, какое изображение нужно отобразить. Вы могли заметить, что теперь конструктор принимает в качестве параметра число, означающее тип плитки.Вернемся в конструктор класса
Main
и отредактируем наш циклfor (i in 0...map.length) {
var tile = new Tile(map[i]);
var x = i % w;
var y = Math.floor(i / w);
tile.setLoc(x, y);
addChild(tile);
}
Единственное изменение состоит в том, что теперь мы передаем конструктору тип блока. Если бы Вы попробовали запустить программу без данной правки, до выполнение окончилось бы ошибкой.
Сейчас все стало на свои места, но нужно придумать дизайн карты вместо того, чтобы заполнять ее случайными блоками. Первое, удалите цикл в конструкторе
Main
, заполняющий массив единицами. А затем создайте Вашу карту вручнуюmap = [ 0, 4, 0, 0, 0, 0, 0, 5, 0, 0,
0, 0, 0, 0, 0, 1, 2, 2, 3, 0,
0, 0, 0, 6, 0, 0, 0, 0, 0, 0,
1, 2, 2, 3, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 2, 2, 3 ];
Если отформатировать массив, как сделано у меня, то можно легко увидеть примерный облик уровня. Но никто не запрещает Вам забить массив просто в одну строку.
При запуске программы Вы увидите несколько ошибок. Проблема заключается в том, что наша карта содержит нулевой тип блока, который не имеет изображения и наш класс просто не знает, что с этим делать. Исправим это недоразумение
if (image != null) addChild(image);
Эта короткая проверка избавит нас от надоедливой ошибки с нулевым указателем. Последнее изменение затронет функцию
setLoc()
. Мы пытаемся использовать переменные x
и y
, которые не были инициализированыpublic function setLoc(x:Int, y:Int) {
if (image != null) {
image.x = x * Main.TILE_WIDTH;
image.y = y * Main.TILE_HEIGHT;
}
}
Благодаря этим двум простым условиям Вы теперь можете запустить игру и увидеть простой уровень. Тайлы, которые умеют id 0 мы воспринимаем, как пустое место. Добавьте какой-нибудь фон. чтобы это выглядело более привлекательно.

Заключение
Вы только что создали tale-based движок. Теперь Вы знаете, что такое тайловая графика и как ей пользоваться. Вы научились создавать и редактировать уровни. Но не останавливайтесь на этом, ведь столько всего еще можно улучшить в нашем движке.
769
комментарии (2)