TileTool — модуль для обучения детей основам разработки игр
Последнее время из каждого чайника напористо лезет реклама обучения языку Python. И это не удивительно, поскольку Python уверенно занял лидирующее положение в качестве первого языка для изучения программирования. А его простота, обилие вспомогательных модулей и скорость написания кода сделали его лидером для обучения детей программированию, полностью вытеснив, популярные лет 20 назад QBasic и Pascal.
Обучаем играючи…
Последние тренды в обучении детей сводятся к игровым механикам. Чтобы заинтересовать ребенка программированием — преврати обучение в игру или предложи научиться игры создавать. Из-за этой логики одним из самых популярных модулей для Python, используемых в обучении стал Pygame, позволяющий создавать простые и не очень игры, без глубокого погружения в тонкости реализации, что очень удобно для детей в процессе начального освоения программирования.
О чем статья?
В этой статье я хотел бы поделиться с хабравцеми и маленькими хабрятами одним очень удобным дополнением для Pygame, найденным на просторах интернета буквально пару дней назад, но покорившем мое сердце. А все от того, что модуль, о котором пойдет речь, упрощает процесс создания 2D игрушек, делая его простым, наглядным, быстрым и увлекательным.
Маленький ликбез о плиточных играх
Огромное количество плоских 2D игрушек относятся к так называемым «плиточным» играм, в которых основой построения игрового мира служат минимальные элементы — плитки, чаще всего квадратные с нанесенной на них текстурой.
К плиточным играм относятся и Сапёр и легендарный Марио и практически все стратегии на развитие территорий и пошаговые стратегии.
В основе построения мира лежит поле, составленное из отдельных квадратиков, каждый из которых может быть закрашен своим цветом или заполнен какой то текстурой.
Задача программиста нарисовать этот квадратный мир с помощью кода и научить персонажа в этом мире жить. Именно за простое создание игрового мира и отвечает модуль под названием TileTool.
TileTool — может всё или почти всё?
Устанавливаем модуль в командной строке
pip install tiletool
И через минуту мы готовы приступать.
У модуля на данный момент имеется три режима работы, то есть три варианта сборки мира.
Генератор прямоугольников
Создание мира из *.txt или *.csv файлов
Создание мира из файлов *.tmx, созданных в визуальном редакторе плиточных уровней Tiled (https://www.mapeditor.org/ — сайт редактора)
Для начала необходимо создать минимальный каркас проекта на Pygame, выглядит он так:
import pygame #Импортируем модуль Pygame
pygame.init() #Инициализируем инструментарий Pygame
window=pygame.display.set_mode((700, 640)) #Создаем окно шириной 700 и высотой 640
while True: #Бесконечный цикл, в котором будет "жить" игра
for i in pygame.event.get(): #Перебираем все события, происходящие в окне
if i.type == pygame.QUIT: #Если событие совпадаем с "закрытием окна", то есть нажатием на креcтикстик
pygame.quit() #Выходим из Pygame
pygame.display.update() #Обновляем экран
Данный код создает игровое окно и запускает цикл в котором будут происходить все события игры, а также делает возможным закрытие окна.
Теперь перейдем к созданию мира и сделаем это тремя разными способами.
Способ №1. Генератор.
TileTool позволяет создать прямоугольную область в виде контура или с заполнением клетками. Для этого до бесконечного цикла создаем объект на базе класса TileTool.
#Импортируем модуль TileTool
import tiletool
#Cоздаем первый объект TileTool с шириной в 7 и высотой 5 тайлов
#в координатах x=120 и y=120 с включенной заливкой
#Необязательный параметр fill принимая значение 1, заполняет весь создаваемый прямоугольник плитками,мии
#значение fill=0, оставляет внутреннее пространство пустым
Box=tiletool.TileTool((7,5),120,120,fill=1)
#К созданному объекту Box применяем метод fill_dict, связывая таким образом
#элементы созданной матрицы с графическими файлами тайлов
Box.fill_dict((1,0),('platform2.png','Empty'))
Тут требуется некоторое пояснение. В результате создания объекта, формируется двумерная матрица или таблица, такого вида:
в ней »1» стоят там, где позднее будут подставлены плитки, а »0» там, где останется пустое пространство.
Метод fill_dict собственно и нужен для того, чтобы соединить графические файлы с матрицей, заменив единички на файл с картинкой, а вместо нуликов оставить пустое пространство.
Box.fill_dict((1,0),('platform2.png','Empty'))
# (1, 0) - кортеж с возможными значениями матрицы
# ('platform2.png','Empty') - соответствующий кортеж замен
# цифра 1 из первого котрежа заменится на изображение из файла platform2.png,
# цифра 0 заменится на "пустоту"
Всё, прямоугольник создан и заполнен тайлами. Осталось его отобразить в нашем окне.
while True:
#Внутри бесконечного цикла к объекту Box применяем метод blit_tiles, в который передаем имя нашего окна
Box.blit_tiles(window)
Одна из приятных фишек модуля заключается в том, что созданный прямоугольник, или, как многие уже догадались платформу, можно заставить двигаться буквально одной командой.
#Добавим в бесконечный цикл строчку:
Box.moving_tile((300,300),1,500,'HORIZONTAL',window)
#(300, 300) - левая или верхняя граница при движении платформы
#1 - скорость движения
#500 - значение правой или нижней границы движения платформы
#'HORIZONTAL' или 'VERTICAL' - направление движения горизонтально или вертикально
#window - имя окна
#И наша платформа будет двигаться горизонтально от точки (300,300) до (500,300) с минимальной скоростью 1.
#Если это слишком быстро, можно настроить частоту кадров с помощью метода tick модуля Pygame
#А чтобы движущийся объект не оставлял за собой след,
#над строкой с движением добавим заливку окна черным цветом
window.fill((0,0,0))
В результате полный код примера с генератором платформ будет выглядеть так:
import pygame
import tiletool
pygame.init()
window=pygame.display.set_mode((700, 640)) #creating the window(создаем окно)
Box=tiletool.TileTool((7,5),120,120,fill=0) #Создаем платформу
Box.fill_dict((1,0),('platform.png','Empty')) #Связываем графические файл и платформой. Файл platform.png должен содержать изображения тайла и лежать в папке с проектом
while True:
window.fill((0,0,0)) #Заливаем окно черным для очистки следа от движущегося объекта
Box.blit_tiles(window) #Отображаем платформу на экране
Box.moving_tile((300,300),1,500,'HORIZONTAL',window) #Заставляем платформу двигаться
for i in pygame.event.get():
if i.type == pygame.QUIT:
pygame.quit()
pygame.display.update()
Всего четыре строчки кода, а у нас уже есть движущаяся платформа, которая ко всему прочему, является наследницей класса pygame.sprite.Sprite, что позволяет добавлять ее в группы и проверять коллизии.
Способ 2. Создание мира из *.txt или *.csv файлов
Многие визуальные редакторы миров умеют экспортировать созданные слои, карты в формат csv или txt. Некоторые разработчики пользуются Excel’ем для рисования уровней. О вкусах не спорят. Тем не менее, TileTool прекрасно работает с подобными форматами. Внешне нарисованный в txt мир выглядит как то так:
По сути своей это такая же матрица, что создавалась Генератором в первом примере. Отличие лишь в том, что задача модуля не создать, а прочитать готовую матрицу из файла и связать ее с текстурами тайлов.
#Импортируем модули
import pygame
import tiletool
pygame.init() #Инициализируем инструментарий Pygame
window=pygame.display.set_mode((800, 800)) #Создаем игровое окно
#Cоздаем объект на основе csv-файла(файла с разделителем) в координатах(100,100)
Box1=tiletool.TileTool('test.csv',100,100)
#Связываем ключи из файла с картинками, для 0 передаем "empty"
Box1.fill_dict((42,21,0),('platform.png','platform2.png','empty'))
#Cоздаем объект на основе txt-файла с разделителем в координатах(0,0)
Box2=tiletool.TileTool('temp.txt',0,0)
#Cвязываем ключи из файла с картинками, для -1 передаем "empty"
Box2.fill_dict((10,-1),('platform.png','empty'))
while True:
window.fill((0,0,0))
#Размещаем оба объекта в окне
Box1.blit_tiles(window)
Box2.blit_tiles(window)
#Первый объект заставляем циклично двигаться
Box1.moving_tile((300,500),1,500,'VERTICAL',window)
for i in pygame.event.get():
if i.type == pygame.QUIT:
pygame.quit()
pygame.display.update()
Небольшое замечание по строчке
Box1.fill_dict ((42,21,0),('platform.png','platform2.png','empty'))
В файлах txt и csv в открытом виде видны значения, используемые в построении миров. В методе fill_dict в первом кортеже необходимо перечислить все значения используемые в файлах, а во втором кортеже сопоставить значениям имена графических файлов, а чтобы оставить «пустоту», достаточно передать 'empty'.
Как и в первом примере — всего пара строк и из текстовых файлов получаются готовые платформы.
Способ 3. Создание мира из файлов *.tmx (редактор миров Tiled)
Не скрою, это мой любимый способ, поскольку визуальный редактор уровней Tiled в разы упрощает и ускоряет процесс создания больших и сложных уровней для двумерных игрушек. Умеет работать как с плоскими картами, так и с изометрией, что дает возможности создавать псевдо 3D игры.
Редактор «плиточных» уровней Tiled
Проект Tiled содержит в себе файлы *.tsx, *.tmx, графические файлы тайлов.
Одно из основных преимуществ редактора заключается в возможности создания многослойных уровней, что позволяет разделить фоновое изображение, передний, средний планы, добиваясь эффекта параллакса в движении, а кроме того, в одном файле нарисовать и статические элементы и на отдельных слоях платформы, которым уже в дальнейшем с помощью TileTool можно задать режимы движения.
И так, к практике:
import tiletool
import pygame
window=pygame.display.set_mode((800, 800))
#Создаем два объекта из одного файла desert1.tmx,
#отличаться объекты будут номером слоя, за который будут отвечать.
#Так, Box1 становится хозяином слоя №1 и мы может независимо управлять этим слоем
#Box2 становится владельцем слоя №2, который мы позднее заставим двигаться.
Box1=tiletool.TileTool('desert1.tmx',0,0,layer=1)
Box2=tiletool.TileTool('desert1.tmx',0,0,layer=2)
#Нам не нужно вызывать метод fill_dict, так как вся информация о изображениях,
#привязанных к ключам, указана в .tsx файле, относящемуся к данному .tmx файлу
while True:
window.fill((0,0,0))
Box1.blit_tiles(window)
Box2.blit_tiles(window)
Box2.moving_tile((0,0),1,200,'HoriZONTAL',window)
for i in pygame.event.get():
if i.type == pygame.QUIT:
pygame.quit()
pygame.display.update() #Oбновляем экран
И снова, всего три строчки и полчаса сэкономленного времени, которые можно потратить не на отрисовку игрового мира, а на работу с коллизиями и игровой механикой.
Способ 4 (бонусный):
У модуля TileTool есть еще один приятный метод, позволяющий в пару строк создавать персонажа и нацеливать на него виртуальную камеру, которая будет следить за ним, создавая иллюзию движения. Выглядит это так:
А реализуется примерно так:
import tiletool
import pygame
window=pygame.display.set_mode((700, 640))
#Cоздаем объект класса TileTool на основе первого слоя .tmx файла.
b1=tiletool.TileTool('desert.tmx',0,0,layer=1)
#Загружаем изображение и конвертируем его в подходящий формат, теперь в переменной filename находится файла с изображением нашего героя.
filename=pygame.image.load('platform.png').convert_alpha()
#Cоздаем объект класса platforms_objects в координатах x=100,y=200,
#за ним будет следовать камера. Также передаем в метод изображение героя.
f1=tiletool.platforms_objects(100,20,filename)
#Cледующие 2 строчки ограничивают частоту кадров в секунду
clock = pygame.time.Clock()
fps = 300
#Kамера будет следить за объектом f1 в координатах (0-1000) по x и (0-1000) по y.
#При выходе из этих координат объект выйдет за пределы экрана.
camera_obj=tiletool.camera(1000,1000,f1)
while True:
window.fill((0,0,0)) #Заливаем окно черным для очистки следа от движущегося объекта.
clock.tick(fps) #Настраиваем скорость обновления экрана.
#Oтображаем объект в окне. Необходимо передать объект класса camera во все вызовы blit_tiles.
b1.blit_tiles(window,camera_obj)
#Oтображаем объект platforms_objects в окне.
#Следует передать вызов метода drawCam,
#как в примере вместо свойства rect объекта,
#чтобы объект мог отображаться в координатах, нужных камере.
window.blit(f1.image,camera_obj.drawCam(f1))
for i in pygame.event.get():
if i.type == pygame.QUIT:
pygame.quit()
# Эти сторочки отвечают за движение объекта, используйте стрелки для движения.
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
f1.rect.x-=1
elif keys[pygame.K_RIGHT]:
f1.rect.x+=1
elif keys[pygame.K_DOWN]:
f1.rect.y+=1
elif keys[pygame.K_UP]:
f1.rect.y-=1
pygame.display.update() # Oбновляем экран.
Сравнительно недавно я в учебных целях реализовывал один уровень игры Марио, на что у меня ушло около 6 часов чистого времени. Познакомившись с модулем TileTool я решил переписать свой проект с использованием предоставившихся возможностей. Времени ушло 2.5 часа, а код сократился практически вдвое!
Автор модуля заявляет, что в следующих версиях появится поддержка изометрии, что даст возможность упрощать работу над псевдо 3D играми, что было бы очень кстати.
А в качестве заключения, хочу лишний раз подчеркнуть, что наличие огромного числа модулей для Python’a позволяет в разы сократить время прототипирования и разработки практически любых программ. И что тоже немаловажно, дает возможность не углубляться в изучение всего подряд, позволяя сосредоточится на одном узком направлении. На примере TileTool, я не хочу заморачиваться с «рисованием миров» и тратить на это свое время, мне интереснее физика движения, игровая механика, реалистичные коллизии, и на отработке этих элементов я хотел бы сосредоточиться. Те же, кому наоборот интереснее генерировать миры, могут сосредоточиться на изучении этой темы, а для физики движения использовать сторонние модули. Как говорится о вкусах не спорят!
Благодарю за терпение в данном лонгриде. Обзор получился довольно общим, и позволяет в общих чертах познакомиться с модулем, упрощающем жизнь создателей плиточных игрушек. Если стиль изложения и данный модуль вам понравились, с радостью поделюсь со всеми парой подробных и пошаговых статей-мануалов, написанных лично, о том как я с помощью модуля TileTool реализовал игры Марио и Сапёр. А также, если интересно, могу выложить мануал по редактору Tiled.