как можно написать код к игре марио в hub на pygame
Пишем игру на Python
Прежде чем мы начнём программировать что-то полезное на Python, давайте закодим что-нибудь интересное. Например, свою игру, где нужно не дать шарику упасть, типа Арканоида. Вы, скорее всего, играли в детстве во что-то подобное, поэтому освоиться будет просто.
Логика игры
Есть игровое поле — простой прямоугольник с твёрдыми границами. Когда шарик касается стенки или потолка, он отскакивает в другую сторону. Если он упадёт на пол — вы проиграли. Чтобы этого не случилось, внизу вдоль пола летает платформа, а вы ей управляете с помощью стрелок. Ваша задача — подставлять платформу под шарик как можно дольше. За каждое удачное спасение шарика вы получаете одно очко.
Алгоритм
Чтобы реализовать такую логику игры, нужно предусмотреть такие сценарии поведения:
Хитрость в том, что всё это происходит параллельно и независимо друг от друга. То есть пока шарик летает, мы вполне можем двигать платформу, а можем и оставить её на месте. И когда шарик отскакивает от стен, это тоже не мешает другим объектам двигаться и взаимодействовать между собой.
Получается, что нам нужно определить три класса — платформу, сам шарик и счёт, и определить, как они реагируют на действия друг друга. Поле нам самим определять не нужно — для этого есть уже готовая библиотека. А потом в этих классах мы пропишем методы — они как раз и будут отвечать за поведение наших объектов.
По коням, пишем на Python
Для этого проекта вам потребуется установить и запустить среду Python. Как это сделать — читайте в нашей статье.
Начало программы
Чтобы у нас появилась графика в игре, используем библиотеку Tkinter. Она входит в набор стандартных библиотек Python и позволяет рисовать простейшие объекты — линии, прямоугольники, круги и красить их в разные цвета. Такой простой Paint, только для Python.
Чтобы создать окно, где будет видна графика, используют класс Tk(). Он просто делает окно, но без содержимого. Чтобы появилось содержимое, создают холст — видимую часть окна. Именно на нём мы будем рисовать нашу игру. За холст отвечает класс Canvas(), поэтому нам нужно будет создать свой объект из этого класса и дальше уже работать с этим объектом.
Если мы принудительно не ограничим скорость платформы, то она будет перемещаться мгновенно, ведь компьютер считает очень быстро и моментально передвинет её к другому краю. Поэтому мы будем искусственно ограничивать время движения, а для этого нам понадобится модуль Time — он тоже стандартный.
Последнее, что нам глобально нужно, — задавать случайным образом начальное положение шарика и платформы, чтобы было интереснее играть. За это отвечает модуль Random — он помогает генерировать случайные числа и перемешивать данные.
Запишем всё это в виде кода на Python:
Мы подключили все нужные библиотеки, сделали и настроили игровое поле. Теперь займёмся классами.
Шарик
Сначала проговорим словами, что нам нужно от шарика. Он должен уметь:
Платформа
Сделаем то же самое для платформы — сначала опишем её поведение словами, а потом переведём в код. Итак, вот что должна уметь платформа:
А вот как это будет в виде кода:
Можно было не выделять счёт в отдельный класс и каждый раз обрабатывать вручную. Но здесь реально проще сделать класс, задать нужные методы, чтобы они сами потом разобрались, что и когда делать.
От счёта нам нужно только одно (кроме конструктора) — чтобы он правильно реагировал на касание платформы, увеличивал число очков и выводил их на экран:
У нас всё готово для того, чтобы написать саму игру. Мы уже провели необходимую подготовку всех элементов, и нам остаётся только создать конкретные объекты шарика, платформы и счёта и сказать им, в каком порядке мы будем что делать.
Смысл игры в том, чтобы не уронить шарик. Пока этого не произошло — всё движется, но как только шарик упал — нужно показать сообщение о конце игры и остановить программу.
Посмотрите, как лаконично выглядит код непосредственно самой игры:
Что дальше
На основе этого кода вы можете сделать свою модификацию игры:
Mario Game In Python With Source Code
Mario Game In Python With Source Code
The Mario Game In Python is written in Python, This Mario Game Code In Python is design in Graphical User Interface (GUI) that uses PyGame library. Talking about the gameplay, it’s a single player game, where the player (Mario) has to dodge fireballs coming out from the dragon. Each level comes with more difficulties, the area gets smaller and smaller as soon as there’s an increment in level. In this Super Mario Python Tutorial you can learn on How To Make Super Mario Game In Python.
A Mario Game Program In Python simple and clean GUI is provided for easy gameplay. The gameplay design is so simple that the user won’t find it difficult to use and understand. Different images are used in the development of this game project, the gaming environment is just like the Mario game.
Watch the video here to see the full running super mario game in python.
Anyway if you want level up your knowledge in programming especially games in python, try this new article I’ve made for you Code For Game in Python: Python Game Projects With Source Code
This Super Mario In Python also includes a downloadable Mario Game In Python source code for free, just find the downloadable source code below and click to start downloading.
To start creating a Mario Game In Python, make sure that you have PyCharm IDE installed in your computer.
By the way if you are new to python programming and you don’t know what would be the the Python IDE to use, I have here a list of Best Python IDE for Windows, Linux, Mac OS that will suit for you. I also have here How to Download and Install Latest Version of Python on Windows.
Steps on how to create a Mario Game In Python
You are free to copy the code given below and download the full source code below.
Извлекаем уровни из Super Mario Bros с помощью Python
Введение
Для нового проекта мне понадобилось извлечь данные уровней из классической видеоигры 1985 года Super Mario Bros (SMB). Если конкретнее, то я хотел извлечь фоновую графику каждого уровня игры без интерфейса, подвижных спрайтов и т.п.
Разумеется, я просто мог склеить изображения из игры и, возможно, автоматизировать процесс с помощью техник машинного зрения. Но мне показался более интересным описанный ниже метод, позволяющий исследовать те элементы уровней, которые нельзя получить с помощью скриншотов.
На первом этапе проекта мы изучим язык ассемблера 6502 и написанный на Python эмулятор. Полный исходный код выложен здесь.
Анализ исходного кода
Реверс-инжиниринг любой программы намного проще, если есть её исходный код, а у нас имеются исходники SMB в виде 17 тысяч строк ассемблерного кода 6502 (процессора NES), опубликованных doppelganger. Поскольку Nintendo так и не выпустила официального релиза исходников, код был создан дизассемблированием машинного кода SMB, с мучительной расшифровкой значения каждой части, добавлением комментариев и осмысленных символьных названий.
Выполнив быстрый поиск по файлу, я нашёл нечто, похожее на нужные нам данные уровней:
Если вы незнакомы с ассемблером, то я объясню: всё это просто означает «вставить такой набор байтов в скомпилированную программу, а потом позволить другим частям программы ссылаться на него с помощью символа L_GroundArea6 ». Можно воспринимать этот фрагмент как массив, в котором каждый элемент является байтом.
Сокращённый граф вызовов для AreaParserCore
Процедура выполняет запись в MetatileBuffer : раздел памяти длиной 13 байтов, представляющий собой один столбец блоков в уровне, каждый байт которого обозначает отдельный блок. Метатайл (metatile) — это блок 16×16, из которого составляются фоны игры SMB:
Уровень с прямоугольниками, описанными вокруг метатайлов
Они называются метатайлами, потому что каждый состоит из четырёх тайлов размером 8×8 пикселей, но подробнее об этом ниже.
То, что декодер работает с заранее заданными объектами, объясняет маленький размер уровня: данные уровня должны ссылаться только на типы объектов и их расположение, например «расположить трубу в точке (20, 16), ряд блоков в точке (10, 5), …». Однако это означает, что для превращения сырых данных уровня в метатайлы требуется много кода.
Портирование такого объёма кода для создания собственного распаковщика уровней заняло бы слишком много времени, поэтому давайте попробуем другой подход.
py65emu
Если бы у нас был интерфейс между Python и языком ассемблера 6502, мы могли бы вызывать подпроцедуру AreaParserCore для каждого столбца уровня, а затем использовать более понятный Python для преобразования информации блоков в нужное изображение.
Тут на сцене появляется py65emu — лаконичный эмулятор 6502 с интерфейсом Python. Вот как в py65emu настраивается та же конфигурация памяти, что и в NES:
Стоит заметить, что это всего лишь эмулятор процессора NES: он не эмулирует другие аппаратные части, такие как PPU (Picture Processing Unit), поэтому его нельзя использовать для эмуляции всей игры. Однако его должно быть достаточно для вызова подпроцедуры парсинга, потому что она не использует никаких других аппаратных устройств, кроме ЦП и памяти.
Но перед этим нам нужно скомпилировать листинг на ассемблере в машинный код.
Как указано в исходном коде, ассемблер компилируется с помощью x816. x816 — это ассемблер 6502 под MS-DOS, используемый сообществом разработчиков самодельных игр (homebrew) для NES и ROM-хакеров. Он замечательно работает в DOSBox.
Наряду с ROM программы, который необходим для py65emu, ассемблер x816 создаёт символьный файл, привязывающий символы к расположению их в памяти в адресном пространстве ЦП. Вот фрагмент файла:
Для удобства я написал парсер символьного файла, который сопоставляет названия символов и адреса:
Подпроцедуры
Как сказано в представленном выше плане, мы хотим научиться вызывать подпроцедуру AreaParserCore из Python.
Чтобы понять механику подпроцедуры, давайте изучим короткую подпроцедуру и соответствующий ей вызов:
Замечательное свойство подпроцедур заключается в том, что можно создавать встроенные вызовы, то есть вызовы подпроцедур внутри подпроцедур. Адреса возврата будут добавляться в стек и извлекаться в правильном порядке, таким же образом, как это происходит с вызовами функций в языках высокого уровня.
Вот код для выполнения подпроцедуры из Python:
Однако мы кое о чём забыли: как передавать входные значения для этой подпроцедуры? Нам нужно сообщить процедуре, какой уровень мы хотим отрендерить и какой столбец нам нужно парсить.
Valgrind для NES?
На самом деле написать простую версию memcheck для py65emu очень легко:
Вот какие результаты даёт MMU в обёртке при вызове execute_subroutine(sym_file[‘AREAPARSERCORE’]) :
Uninitialized read! 0x0728 (BACKLOADINGFLAG):
Uninitialized read! 0x0742 (BACKGROUNDSCENERY):
Uninitialized read! 0x0741 (FOREGROUNDSCENERY):
Uninitialized read! 0x074e (AREATYPE):
Uninitialized read! 0x075f (WORLDNUMBER):
Uninitialized read! 0x0743 (CLOUDTYPEOVERRIDE):
Uninitialized read! 0x0727 (TERRAINCONTROL):
Uninitialized read! 0x0743 (CLOUDTYPEOVERRIDE):
Uninitialized read! 0x074e (AREATYPE):
.
Очевидно, что metatile_data явно соответствует информации фона.
Графика метатайлов
(Чтобы посмотреть конечный результат, можно сразу перейти к разделу «Соединяем всё вместе».)
Теперь давайте разберёмся, как превратить полученные числа метатайлов в настоящие изображения. Описанные ниже этапы придуманы благодаря анализу исходников и чтению документации с потрясающей Nesdev Wiki.
Чтобы понять, как рендерить каждый метатайл, нам сначала нужно поговорить о цветовых палитрах NES. PPU консоли NES способен в общем рендерить 64 разных цвета, однако чёрный цвет несколько раз дублируется (подробности см. в Nesdev):
Каждый уровень Mario может использовать для фона только 10 из этих 64 цветов, разделённых на на 4 четырёхцветных палитры; первый цвет всегда одинаков. Вот четыре палитры для World 1-1:
Давайте теперь рассмотрим пример номера метатайла, представленный в двоичном виде. Вот номер метатайла тайла камней с трещинами, который является землёй уровня World 1-1:
Индекс палитры говорит нам, какой палитрой пользоваться при рендеринге метатайла (в нашем случае палитрой 1). Индекс палитры также является индексом двух следующих массивов:
.db >Palette0_MTiles, >Palette1_MTiles, >Palette2_MTiles, >Palette3_MTiles
Сочетание этих двух массивов даёт нам 16-битный адрес, который в нашем примере указывает на Palette1_Mtiles :
Четыре записи этой строки на самом деле являются идентификаторами тайлов: каждый метатайл состоит из четырёх тайлов размером 8×8 пикселей, выстроенных в следующем порядке — верхний левый, нижний левый, верхний правый и нижний правый. Эти идентификаторы передаются непосредственно в PPU консоли NES. Идентификатор ссылается на 16 байтов данных в CHR-ROM консоли, а каждая запись начинается с адреса 0x1000 + 16 * :
0x1000 + 16 * 0xb4: 0b01111111 0x1000 + 16 * 0xb5: 0b11011110
0x1001 + 16 * 0xb4: 0b10000000 0x1001 + 16 * 0xb5: 0b01100001
0x1002 + 16 * 0xb4: 0b10000000 0x1002 + 16 * 0xb5: 0b01100001
0x1003 + 16 * 0xb4: 0b10000000 0x1003 + 16 * 0xb5: 0b01100001
0x1004 + 16 * 0xb4: 0b10000000 0x1004 + 16 * 0xb5: 0b01110001
0x1005 + 16 * 0xb4: 0b10000000 0x1005 + 16 * 0xb5: 0b01011110
0x1006 + 16 * 0xb4: 0b10000000 0x1006 + 16 * 0xb5: 0b01111111
0x1007 + 16 * 0xb4: 0b10000000 0x1007 + 16 * 0xb5: 0b01100001
0x1008 + 16 * 0xb4: 0b10000000 0x1008 + 16 * 0xb5: 0b01100001
0x1009 + 16 * 0xb4: 0b01111111 0x1009 + 16 * 0xb5: 0b11011111
0x100a + 16 * 0xb4: 0b01111111 0x100a + 16 * 0xb5: 0b11011111
0x100b + 16 * 0xb4: 0b01111111 0x100b + 16 * 0xb5: 0b11011111
0x100c + 16 * 0xb4: 0b01111111 0x100c + 16 * 0xb5: 0b11011111
0x100d + 16 * 0xb4: 0b01111111 0x100d + 16 * 0xb5: 0b11111111
0x100e + 16 * 0xb4: 0b01111111 0x100e + 16 * 0xb5: 0b11000001
0x100f + 16 * 0xb4: 0b01111111 0x100f + 16 * 0xb5: 0b11011111
0x1000 + 16 * 0xb6: 0b10000000 0x1000 + 16 * 0xb7: 0b01100001
0x1001 + 16 * 0xb6: 0b10000000 0x1001 + 16 * 0xb7: 0b01100001
0x1002 + 16 * 0xb6: 0b11000000 0x1002 + 16 * 0xb7: 0b11000001
0x1003 + 16 * 0xb6: 0b11110000 0x1003 + 16 * 0xb7: 0b11000001
0x1004 + 16 * 0xb6: 0b10111111 0x1004 + 16 * 0xb7: 0b10000001
0x1005 + 16 * 0xb6: 0b10001111 0x1005 + 16 * 0xb7: 0b10000001
0x1006 + 16 * 0xb6: 0b10000001 0x1006 + 16 * 0xb7: 0b10000011
0x1007 + 16 * 0xb6: 0b01111110 0x1007 + 16 * 0xb7: 0b11111110
0x1008 + 16 * 0xb6: 0b01111111 0x1008 + 16 * 0xb7: 0b11011111
0x1009 + 16 * 0xb6: 0b01111111 0x1009 + 16 * 0xb7: 0b11011111
0x100a + 16 * 0xb6: 0b11111111 0x100a + 16 * 0xb7: 0b10111111
0x100b + 16 * 0xb6: 0b00111111 0x100b + 16 * 0xb7: 0b10111111
0x100c + 16 * 0xb6: 0b01001111 0x100c + 16 * 0xb7: 0b01111111
0x100d + 16 * 0xb6: 0b01110001 0x100d + 16 * 0xb7: 0b01111111
0x100e + 16 * 0xb6: 0b01111111 0x100e + 16 * 0xb7: 0b01111111
0x100f + 16 * 0xb6: 0b11111111 0x100f + 16 * 0xb7: 0b01111111
CHR-ROM — это фрагмент памяти read-only, к которому может получать доступ только PPU. Он отделён от PRG-ROM, в котором хранится код программы. Поэтому приведённые выше данные отсутствуют в исходном коде и их нужно получать из дампа ROM игры.
16 байта для каждого тайла составляют 2-битный тайл размером 8×8: первый бит — это первые 8 байтов, а второй — вторые 8 байтов:
21111111 13211112
12222222 23122223
12222222 23122223
12222222 23122223
12222222 23132223
12222222 23233332
12222222 23111113
12222222 23122223
12222222 23122223
12222222 23122223
33222222 31222223
11332222 31222223
12113333 12222223
12221113 12222223
12222223 12222233
23333332 13333332
Выполняем привязку этих данных к палитре 1:
…и объединяем куски:
Наконец мы получили отрендеренный тайл.
Соединяем всё вместе
Повторив эту процедуру для каждого метатайла, мы получим полностью отрендеренный уровень.
Пишем платформер на python, используя pygame. Часть 2 подчасть 2. Редактор уровней
Привет, друзья! Сегодня мы наконец-то доделаем нашего мариобоя. Начало тут и тут. Вот только мы не будем изобретать свой велосипед в виде редактора уровней, а воспользуемся готовым мощным инструментом. За знакомство с которым я благодарен господам(товарищам) sourcerer и Tarvitz
Почему так?
Создаём неприятности и преграды нашему герою
Тут можете рисовать что угодно и как угодно, тайлы с этого слоя ни как не влияют на героя или игровой процесс, разве что на эстетический вид игры 🙂
Блоки, по которым можно бегать
На этом слое располагаются тайлы, которые в игре создают объекты класса Platform
Смертельно опасные блоки
Все тайлы, независимо от внешнего вида, будь то шипы или кирпичная стена, создают в игре объекты класса BlockDie
Монстры
Это слой объектов, а значит, он не отображает в игре тайлы и каждый объект, добавленный на него, должен обладать какими — нибудь свойствами.
Объекты класса Monster, чей конструктор имеет следующий вид
Обязательно должны иметь такие свойства, как: left, maxLeft, up, maxUp — заполняемые вручную и x, y — передающиеся по расположению объекта.
Объект персонажа должен иметь имя Player
Объект принцессы должен иметь имя Princess
Вид слоя:
Телепорты
Как узнать конечные координаты?
Легко! Навести курсор мыши на то место, куда хотите, чтобы герой телепортировался и посмотреть слева снизу координаты места
Карту в игру
НемногоИзменим основной файл игры для того, чтобы открыть в ней выше созданную карту.
Для начала скачиваем необходимые библиотеки от сюда и кидаем их в папку с исходными кодами игры.
И импортируем их
Далее, очищаем процедуру loadLevel(), мы её перепишем.
Что мы тут видим?
Начнем с того, что теперь процедура принимает входной параметр name, который используется для загрузки карты уровня. Это сделали для того, чтобы сделать переход между уровнями.
Далее идёт загрузка и преобразование карты, и по тому же принципу, что мы парсили массив с картой, парсим слои с тайлами. Обратите внимание, что теперь созданные объекты классов Platform и BlockDie не помещаются в группу entities, а значит, мы их не будет отображать т.е. они будут существовать, но не отображаться. Вместо них мы будет отображать тайлы со слоёв карты.
Продолжим
Теперь займемся процедурой main
Добавим визуализатор(рендерер) слоёв карты
Для чего он — увидим чуть ниже
Изменим вызов процедуры loadLevel
И далее, весь код будет в этом цикле
В блоке вывода изображений на экран добавим работу визуализатора
Обратите внимание, что renderer выводит свои изображения по центру экрана, внутри передвигающегося фокуса камеры, для этого нам нужно было добавить процедуру в класс Camera
Уберем то, что перенесли в процедуру loadlevel, добавим немного нового и получим следующий вид:
Что тут интересного?
кроме того, что мы разобрали в этой и предыдущей частях статьи, тут имеется событие, присходящее, когда наш герой касается принцессы. В этот момент мы выводим сообщение о его неудаче и, даём время прочитать его и идет на следующую итерацию, загружая следующий уровень.
Вот и всё. Вот так, легко и быстро мы переделали игру для загрузки уровней из tmx файлов.
Создание игр на Python 3 и Pygame: Часть 1
Многие разработчики приходят в разработку ПО, потому что хотят создавать игры. Не все могут стать профессиональными разработчиками игр, но любой может создавать собственные игры из интереса (а может быть, и с выгодой). В этом туториале, состоящем из пяти частей, я расскажу вам, как создавать двухмерные однопользовательские игры с помощью Python 3 и замечательного фреймворка PyGame.
Мы создадим версию классической игры Breakout. Освоив этот туториал, вы будете чётко понимать, что необходимо для создания игры, познакомитесь с возможностями Pygame и напишете собственный пример игры.
Мы реализуем следующие функции и возможности:
Краткое введение в программирование игр
Главное в играх — перемещение пикселей на экране и издаваемый шум. Почти во всех видеоиграх есть эти элементы. В этой статье мы не будем рассматривать клиент-серверные и многопользовательские игры, для которых требуется много сетевого программирования.
Основной цикл
Основной цикл (main loop) игры выполняется и обновляет экран через фиксированные интервалы времени. Они называются частотой кадров и определяют плавность перемещения. Обычно игры обновляют экран 30-60 раз в секунду. Если частота будет меньше, то покажется, что объекты на экране дёргаются.
Внутри основного цикла есть три основных операции: обработка событий, обновление состояния игры и отрисовка текущего состояния на экране.
Обработка событий
События в игре состоят из всего, что происходит за пределами управления кода игры, но относится к выполнению игры. Например, если в Breakout игрок нажимает клавишу «стрелка влево», то игре нужно переместить ракетку влево. Стандартными событиями являются нажатия (и отжатия) клавиш, движение мыши, нажатия кнопок мыши (особенно в меню) и события таймера (например, действие спецэффекта может длиться 10 секунд).
Обновление состояния
Сердце любой игры — это её состояние: всё то, что она отслеживает и отрисовывает на экране. В случае Breakout к состоянию относятся положение всех кирпичей, позиция и скорость мяча, положение ракетки, а также жизни и очки.
Существует также вспомогательное состояние, позволяющее управлять игрой:
Отрисовка
Игре нужно отображать своё состояние на экране, в том числе отрисовывать геометрические фигуры, изображения и текст.
Игровая физика
В большинстве игр симулируется физическое окружение. В Breakout мяч отскакивает от объектов и имеет очень приблизительную систему физики твёрдого тела (если это можно так назвать).
В более сложных играх могут использоваться более изощрённые и реалистичные физические системы (особенно в 3D-играх). Стоит также отметить, что в некоторых играх, например, в карточных, физики почти нет, и это совершенно нормально.
ИИ (искусственный интеллект)
Например, враги преследуют игрока и знают о его местоположении. В Breakout нет никакого ИИ. Игрок сражается с холодными и твёрдыми кирпичами. Однако ИИ в играх часто очень прост и всего лишь следует простым (или сложным) правилам, обеспечивающим псевдоразумные результаты.
Воспроизведение звука
Воспроизведение звука — ещё один важный аспект игр. В общем случае существует два типа звука: фоновая музыка и звуковые эффекты. В Breakout я реализую только звуковые эффекты, которые воспроизводятся при различных событиях.
Фоновая музыка — это просто музыка, постоянно играющая на фоне. В некоторых играх она не используется, а в некоторых меняется на каждом уровне.
Жизни, очки и уровни
В большинстве игр игрок имеет определённое количество жизней, и когда они заканчиваются, игра завершается. Также в играх часто присутствуют очки, позволяющие понять, насколько хорошо мы играем, и дающие мотивацию к самосовершенствованию или просто хвастаться друзьям своими рекордами. Во многих играх есть уровни, которые или совершенно отличаются, или постепенно увеличивают сложность.
Знакомство с Pygame
Прежде чем приступить к реализации игры, давайте немного узнаем о Pygame, который возьмёт на себя большую часть работы.
Что такое Pygame?
Pygame — это фреймворк языка Python для программирования игр. Он создан поверх SDL и обладает всем необходимым:
Установка Pygame
Это станет серьёзным препятствием при запуске игры. В конце концов мне пришлось запускать её в Windows внутри VirtualBox VM. Надеюсь, ко времени прочтения этой статьи проблема будет решена.
Архитектура игры
Играм нужно управлять кучей информации и выполнять почти одинаковые операции со множеством объектов. Breakout — это небольшая игра, однако попытка управлять всем в одном файле может оказаться слишком утомительной. Поэтому я решил создать файловую структуру и архитектуру, которая подойдёт и для гораздо более крупных игр.
Структура папок и файлов
Pipfile и Pipfile.lock — это современный способ управления зависимостями в Python. Папка images содержит изображения, используемые игрой (в нашей версии будет только фоновое изображение), а в папке sound_effects directory лежат короткие звуковые клипы, используемые (как можно догадаться) в качестве звуковых эффектов.
Файлы ball.py, paddle.py и brick.py содержат код, относящийся к каждому из этих объектов Breakout. Подробнее я рассмотрю их в следующих частях туториала. Файл text_object.py содержит код отображения текста на экране, а в файле background.py содержится игровая логика Breakout.
Однако существует несколько модулей, создающих произвольный «скелет» общего назначения. Определённые в них классы можно будет использовать в других играх на основе Pygame.
Класс GameObject
GameObject представляет собой визуальный объект, знающий о том, как себя рендерить, сохранять свои границы и перемещаться. В Pygame есть и класс Sprite, исполняющий похожую роль, но в этом туториале я хочу показать вам, как всё работает на низком уровне, а не полагаться слишком активно на готовую магию. Вот как выглядит класс GameObject:
Класс Game
Класс Game — это ядро игры. Он выполняется в основном цикле. В нём есть множество полезных возможностей. Давайте разберём его метод за методом.
Метод __init__() инициализирует сам Pygame, систему шрифтов и звуковой микшер. Три разных вызова нужны, так как не во всякой игре на Pygame используются все компоненты, поэтому можно контролировать подсистемы, которые мы используем, и инициализировать только нужные с соответствующими параметрами. Метод создаёт фоновое изображение, основную поверхность (на которой всё отрисовывается) и игровой таймер с правильной частотой кадров.
Элемент self.objects хранит все игровые объекты, которые должны рендериться и обновляться. Различные обработчики управляют списками функций-обработчиков, которые должны выполняться при определённых событиях.
Методы update() и draw() очень просты. Они обходят все управляемые игровые объекты и вызывают соответствующие им методы. Если два объекта накладываются друг на друга на экране, то порядок списка объектов определяет, какой из них будет рендериться первым, а остальные будут частично или полностью его перекрывать.
Метод handle_events() слушает события, генерируемые Pygame, такие как события клавиш и мыши. Для каждого события он вызывает все функции-обработчики, которые должны обрабатывать события соответствующих типов.
Затем он обновляет экран, то есть записывает на физический дисплей всё содержимое, которое было отрендерено на текущей итерации. И последнее, но не менее важное — он вызывает метод clock.tick() для управления тем, когда будет вызвана следующая итерация.
Заключение
В этой части мы изучили основы программирования игр и все компоненты, участвующие в создании игр. Также мы рассмотрели сам Pygame и узнали, как его установить. Наконец, мы погрузились в архитектуру игры и изучили структуру папок, классы GameObject и Game.