Полноценный 2D-платформер на Python в 2023? Мой опыт

Говорят, что на Python легко и просто создавать платформеры. Правда ли?

Предисловие

Все мы были когда-то детьми. И те, кто вырос в 90-ые, наверняка играли в приставку Денди (в США она называлась NES — Nintendo Entertainment System). Среди всех игр была одна игра, которая мне особенно запомнилась, так как была не похожа на все остальные. Это игра The Addams Family (1992 год, студия Ocean). В игре была какая-то своя атмосфера. И мне захотелось создать что-то похожее.

Придумываем сюжет и геймплей

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

Рисуем ассеты

Перед тем как приступить к созданию ассетов (разных картинок, которые потом можно будет анимировать), надо разобраться в каком разрешении будет работать игра. У всех разные мониторы с разным разрешением. И не у всех даже FullHD. Поэтому выбираем 720p — то есть разрешение 1280×720 пикселей. Для такого разрешения органично будут смотреться плитки земли размером 64×64 пикселя. Берем любой векторный графический редактор и начинаем рисовать.

Рисуем ассеты поместья

Рисуем ассеты поместья

Рисовал как мог, вспоминая как у всех ребят в школе по рисованию было »5», и только у меня одного »4». Вы могли заметить, что это все статические объекты, которые не имеют анимации. Ну, а как же с анимированными объектами? Все просто, анимированные объекты — это те же отдельные картинки, у которых изменяется положение некоторых пикселей. Вот пример анимации на примере главного героя игры:

Анимация главного героя

Анимация главного героя

Собственно программирование

Инструмент для создания игр мы будем выбирать на основании того языка программирования, который более-менее знаем. В моем случае, это Python. Выбираем какую-нибудь современную версию языка — я выбрал Python 3.11.4. Для создания игр на Python существует библиотека Pygame. Причем это не графический движок типа Unity. Здесь нет графического интерфейса, а есть только набор разных классов, в которых есть свои атрибуты и методы. Эта библиотека служит для осуществления манипуляции графическими объектами на основе событий (events).

Заполняем словарики

Игру начинаем даже не с файла main.py, , а с файла settings.py, куда будут прописаны основные параметры и настройки игры. Помимо названия игры, длины и ширины окна игры, мы должны прописать доступы к ассетам. В связи с тем, что данных много, а в Python есть такой тип данных как словарь (dict), в основе которого лежит хеш-таблица, мы выбираем именно словарь и заполняем. Фрагмент такого словаря:

12: {
        'style': 'enemy',
        'type': 'tile',
        'menu': 'enemy',
        'menu_surf': 'images/menu/bug.png',
        'preview': 'images/preview/bug.png',
        'graphics': 'images/enemies/cementry/bug'
    },

С чего начинается родина — с main.py

Наконец-то поработаем с main.py. Конечно, будем использовать нашу любимую концепцию ООП. Есть такая штука как принципы SOLID, которым следует придерживаться при разработке программы. Некоторые из них я собираюсь нарушить, но вот один я никак нарушить не могу — буковка «S» — Single Resposibility Principle. Суть его в том, что каждый класс должен выполнять строго обозначенную функцию и быть ограниченным своей задачей. Не надо создавать классы, которые делают все сразу. Поэтому main.py будет класс Main, в котором есть главный цикл игры — бесконечный цикл с условием While, но все события будут обрабатываться в других отдельных классах, а в Main мы будем создавать экземпляры этих других классов.

Режим редакторы игры

Как думаете, что объединяет такие игры как Heroes of Might and Magic 3, The Elder Scrolls III: Morrowind и Max Payne. Их объединяет, что на диске с ними шел встроенный редактор уровней. Это мощнейший инструмент в разработке игры. Беда в том, что в Pygame ничего подобного нет, а значит мы его сами создадим. Здесь лучше найти какой-нибудь туториал. Я лично нашел на YouTube видео немецкого программиста Christian Koch «Creating a Mario Maker style game in Python» (канал ClearCode). На основе этого видео и создаем редактор уровней. Но нам надо как-нибудь сохранять те произведения искусства, которые мы создали. И здесь у нас есть выбор: либо сохранять как текстовый формат передачи данных (например, JSON), или же сохранять в бинарном виде, используя библиотеку pickle. Я решил, что для уровней у нас будут бинарные файлы, а для сохранений в процессе игры у нас будет JSON.

Сохраняем нарисованный уровень как бинарный файл

Сохраняем нарисованный уровень как бинарный файл

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

Коллизии и анимации спрайтов

Спрайт в Pygame —  это графическое изображение, которое представляет собой одну единицу анимации. Главное здесь — это коллизии и анимации. С коллизиями все просто — у классов Pygame есть встроенные методы для работы с коллизиями, например, collidepoint. Для анимаций в соответствующем классе мы создадим свой метод и используем тернарный оператор:

def animate(self, dt):
        self.frame_index += ANIMATION_SPEED * dt
        self.frame_index = 0 if self.frame_index >= len(
            self.animation_frames) else self.frame_index
        self.image = self.animation_frames[int(self.frame_index)]

Мы просто перебираем картинки в списке, и вызываем их одну за другой с помощью переменной self.frame_index. Тернарый оператор применяется, чтобы из бежать ошибки IndexError: list index out of range.

Обратите внимание на константу ANIMATION_SPEED. Дело в том, что у нас разлочен FPS в игре и скорость анимации никак не зависит от FPS.

Главное меню игры

Не помню какой обзорщик из YouTube 10-ых годов говорил, что главное меню — это как вешалка в театре. Для меня очень удобным было меню из игры The Elder Scrolls V: Skyrim — ничего лишнего. Нечто похожее и мы сделаем с учетом, что я не умею рисовать и игру мы делаем на Pygame. В главном меню мы должны сказать игроку как управлять персонажем в игре и создать возможности поменять язык, перейти в полноэкранный режим, уменьшить или увеличить громкость музыки и звуков, и перейти в режим редактора.

Музыкальная пауза

Если для звуков полно бесплатных ресурсов, где можно скачать free sounds, то музыку я решил сам написать для игры. Все рождается из импровизации. Записываем все на листочек, а потом ноты перебиваем в любой музыкальный midi-редактор, который поддерживает нотный стан.

Вот пример трека Garden из моей игры:

03debca9c4c70f1d8c918cb036e3f7bf.png

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

Итоги

Самое главное, что я понял, создание игры — это хорошая возможность прокачать ваши hard skills. Процесс создания игры заставляет вас решать множество задач. Библиотека Pygame — это один из самых лучших способов понять концепцию ООП в Python. Я, когда неправильно прописал условия для звуков и музыки, понял, как именно загружаются в память компьютера экземпляры классов, как в них инициализируются атрибуты и как работают методы. Также вы повторите весь базовый синтаксис Python — работу со всеми типами данных, включая файлы, и со стандартными библиотеками типа math, random, configparser, json и pickle.

Полный код игры доступен на моем GitHub

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

И что же получилось на выходе

© Habrahabr.ru