Вся информация анонимизирована.
Содержание:
- Вступление
- Запуск
- Что происходит после запуска
- Описание программы для пользователя
- Как создать свою карту
- Конфигурация ПК
- Описание некоторых алгоритмов проекта
- Описание реализации некоторых алгоритмов проекта
- Заключение
Перед нами стояла цель разработать программный продукт с использованием
библиотеки pygame, который позволит пользователю окунуться в бесконечный мир
танковых боев, почувствовать себя настоящим танкистом, управляя собственным танком.
2) Реализовать работоспособный код клиента игры и код меню игры.
3) Реализовать изменение настроект игры пользователем.
Для того чтобы более точно определить последовательности действий, ко-торые может осуществлять система в ответ на внешние воздействия пользователя была составлена диаграмма вариантов использования. Диаграмма вариантов использования представлена на рисунке 2.2.
Рисунок 2.2 — Диаграмма вариантов использования.Ниже будут приведены описания некоторых алгоритомов, а также описание их реализации.
Однако ознакомиться с более подробным описанием нашего проекта можно в пояснительный записке к проекту (см. файл PZ_dlya_irgry.docx).
- Pygame
- pytmx
Все необходимые библиотеки описаны в requirements.txt.
Чтобы запустить игру необходимо запустить файл main_menu.py из папки modules.
Имеется возможность отдельно запустить клиент игры (без меню) - запустить файл client.py из папки modules. В этом случае по-умолчанию запустится первый уровень игры на 1 человека.
Можно изменить уровень и тип игры из самого кода. Для этого необходимо изменить первые две цифры в следующей строке:
client = Client(1, 1, screen)
в файл client.py, где первый параметр это тип игры - (1 - игра на одного, 2 - игра на двоих) и второй параметр это уровень игры (всего доступно 20 различных уровней, если указан уровень другой, то программа рандомно выберет один из 20 уровней).
- Загружаются настройки игры из json объекта.
- Загружается вся музыка игры.
- Создаются все объекты игры: загружается карта, картинки игровых объектов, создается менеджер ботов, объекты танков игроков, а также окна меню выхода и паузы.
- Запускается основной игрвой цикл, в котором происходит перехват событий (нажатие клавиш на клавиатуре и пр.), обновление игрвого мира и его отрисовка. Кроме этого, в основном цикле можно перехватывать событие выхода в главное меню.
- Далее уже клиент игры отвечает за обработку нажатых клавиш, обновление объекта игры, воспроизведение музыки и отрисовку игровых объектов.
- Сам объект игры заставляет обновляться остальные игровые объекты, отвечает за передачу названий звуковых треков в объект клиента, проверяет состояние игры, и прочие действия при загрузке объектов.
- Остальные объекты лишь обновляют свое состояние и изменяют его в зависимости от таймеров либо от действий игрока.
- Каждое окно меню представляет из себя функцию.
- Главная функция запускает стартовый экран, при переключении на другой экран, функция стартового экрана завершается и запускается функция другого окна.
- При переключении на другой экран, запускается функция соответствующего окна, а функция активного завершается.
При запуске программы отрывается главное меню. Изображения главного меню представлено на рисунке 5.1.

Рисунок 5.1 — Изображение главного меню игры.
В главном меню расположены интуитивно понятные кнопки, отвечаю-щие, соответственно за начало игры, изменение настроек, ознакомление с пра-вилами, выход из игры.
Прежде чем начать игры рекомендуем ознакомиться с правилами игры. Изображение окна с правилами игры представлено на рисунке 5.2.

Рисунок 5.2 — Изображение окна с правилами игры.
После ознакомления с правилами рекомендуем посетить настройки, что-бы настроить предпочитаемый уровень громкости музыки и звуков, а также ознакомиться с кнопками управления. Изображения окна настроек представле-но на рисунке 5.3.

Рисунок 5.3 — Изображения окна настроек.
Далее можно перейти к выбору типа игры. Окно выбора типа игры представлено на рисунке 5.4.

Рисунок 5.4 — Изображение окна выбора типа игры.
После выбора типа игры откроется окно, с возможность выбрать 1 из 20 доступных уровней. В правом углу представлена карта выбранного уровня. Нажав кнопку «Играть» запуститься выбранный уровень. Изображение окна с выбором уровня игры представлено на рисунке 5.4.

Рисунок 5.4 — Изображение окна выбора уровня игры.
После нажатия кнопки «играть» откроется главное игровое окно и запу-стится выбранный уровень. Изображение главного игрового окна представлено на рисунке 5.5.

Рисунок 5.5 — Изображение главного игрового окна.
В данном окне можно наблюдать количество оставшихся ботов (врагов) в правом верхнем углу (изображение представлено на рисунке 5.5.1). Ниже отображены никнеймы игроков и их количество жизней (изображение представлены на рисунке 5.5.2). Ниже отображается выбранный игровой уровень (изображение представлено на рисунке 5.5.3). На игровой карте можно наблюдать танки игроков, орла и танки противников (изображения представлены на рисунках 5.5.4, 5.5.5, 5.5.6 соответственно).

Рисунок 5.5.1 – Изображение оставшихся ботов (врагов).

Рисунок 5.5.2 – Изображение никнеймов игроков и их количество жизней.

Рисунок 5.5.3 – Изображение выбранного игрового уровня.

Рисунок 5.5.4 – Изображение танков игроков.

Рисунок 5.5.5 – Изображение орла, которого необходимо защитить.

Рисунок 5.5.6 – Изображение танков врага.
Следует заметить, что в игре представлено 4 вида танков врагов, 4 вида танков игроков, а также 7 видов бонусов. Изображение бонусов представлено на рисунке 5.6. Изображение типов танков представлено на рисунке 5.6.1.

Рисунок 5.6. – Изображение бонусов.

Рисунок 5.6.1. – Изображение типов танков (слева игрока, справа бота). Сверху вниз в порядке увеличения уровня танка.
Типы танков отличаются друг от друга скоростями передвижения, коли-чеством жизней и скоростью полета пули.
Перечислю бонусы, в том порядке, в котором они находятся на картинке, и поясню, что каждый из бонусов делает:
- Бонус шлем - дает игроку, подобравшему этот бонус, щит – неуязвимость на определенное время.
- Бонус часы - заставляет всех ботов замереть на определенное время.
- Бонус лопата – превращает стены около орла в непробиваемые металличе-ские стены на определенное время.
- Бонус звезда – улучшает танк (меняет тип на следующий).
- Бонус граната – уничтожает всех вражеских танков без начисления оков.
- Бонус танк – увеличивает количество жизней у игрока, подобравшего этот бонус.
- Бонус пистолет – дает танку игрока максимальную прокачку и увеличивает количество жизней на 2 единицы. Следует отметить, что если бонус не подобрать, то спустя определенное время он начнет мигать и чуть позже исчезнет полностью.
Если уничтожены танки игроков, или уничтожен орел, или уничтожены все враги, то появляется завершающее окно, на котором отображается резуль-тат игры, количество уничтоженных на этом уровне танков и количество зара-ботанных очков за уничтожение танков и подбор бонусов. Изображение завершающего окна представлено на рисунке 5.7.

Рисунок 5.7. - Изображение завершающего окна.
На данном окне также представлены 2 кнопки, которые позволяют выйти в главное меню или же перезапустить уровень или запустить следующий уровень (зависит от результата игры).
Также игру можно поставить на паузу в любой момент, нажав кнопку «P» на клавиатуре. Изображение окна паузы представлено на рисунке 5.8.

Рисунок 5.8. - Изображение окна паузы.
В игре предусмотрена возможность «быстрого выхода» в меню. Нажав кнопку Escape (Esc) на клавиатуре, запустится окно паузы, в котором также бу-дет отображено окно с кнопками отмены действия и подтверждения.
Нажав «отмена», игра возобновится. Нажав «В меню» игра завершится и откроется главное игровое меню. Изображение меню «быстрого выхода» представлено на рисунке 5.9.

Рисунок 5.9. - Изображение меню «быстрого выхода».
Для выхода из программы следует нажать кнопку выхода в главном меню игры.
-
Необходимо установить приложение Tiled Map Editor. Вот офф сайт
-
Создать новую карту со следующими настройками:
- Ориентация - Ортогональная
- Формат слоя тайлов - Base64 (zlib сжатие)
- Порядок отрисовки тайлов - Справа снизу
- Размер тайлов:
- Ширина и Высота - 80 точек
- Размер карты. Может быть любым, но рекомендуемые:
- Ширина и Высота - 13 тайлов
-
Добавить (если еще не добавлен) новый набор тайлов.
-
Игровая карта обязана содержать следующие слои и объекты:
- Слои: ground (земля), spawn_players (клетки для спавна игрока), spawn_bots (клетки для спавна бота)
- Объекты: eagle (орел)
Необходимо отметить, что клеток для спавна игроков ОБЯЗАТЕЛЬНО должно быть 2 штуки.
-
Также карта может содержать следующие слои и объекты:
- Слои: trees (деревья)
- Объекты: walls (стены)
-
Новую карту необходимо сохранить под названием map{номер карты}.tmx в папке путь_к_корню_проекта\data\maps. Пример: map21.tmx
-
Теперь, поиграть на новой карте можно, запустив код из файла client.py, предварительно изменив строку
client = Client(1, 1, screen), где вместо 2-го параметра необходимо указать номер нового уровня.
P.S. Если вдруг что-то стало непонятно по созданию уровня в Tiled Map Editor, то в нем можно открыть одну из доступных карт и ознакомиться с тем, какие слои содержат какие элементы, как нужно правильно подписать и расположить объекты и тд.
На приведенной ниже конфигурации был разработан клиент игры.Также работа кода была протестирована на более чем 3 разных машинах, и никаких критических ошибок не было обнаружено.
Алгоритм создания и загрузки игровых карт: 1) В программе Tiled Map Editor создается карта игры. Игровая карта обязана содержать следующие слои и объекты: • Слои: ground (земля), spawn_players (клетки для спавна игрока), spawn_bots (клетки для спавна бота) • Объекты: eagle (орел) Также карта может содержать следующие слои и объекты: • Слои: trees (деревья) • Объекты: walls (стены) 2) Загрузка карты происходит посредством использования библиотеки pytmx. Данная библиотека позволяет загрузить .tmx файл игровой карты, и создать из этого файла объект, который способен возвращать положение клеток, картинку клетки по координатам и многое другое. 3) После создания объекта, код запрашивает положение стен, клеток для спавна игроков и ботов, а также клетку для установки орла. После класс карты используется лишь для отрисовки земли и деревьев.Алгоритм назначения клавиш и обработка нажатых клавиш:
- В проекте имеется .json файл, который хранит в себе описание кнопки и ее название в библиотеке Pygame.
- При изменении раскладки, программа запоминает имя нажатой клавиши и записывает его в .json файл.
- Код клиента обрабатывает нажатые клавиши, используя метод key_code(), мы можем узнать код кнопки через ее название.
- Перебрав список действий, которые записаны в .json файле, узнав нажатые клавиши, мы записываем их в список нажатых клавиш. Это сделано для того, чтобы подготовить основу для будущего введения онлайн режима в игру.
- Игра, а если быть точным, класс спрайта игрока, считывает переданные ему действия и передвигает или заставляет что-либо делать того или иного игрока в зависимости от действия.
Алгоритм воспроизведения игровой музыки:
- Т.к. в проекте планируется добавление онлайн режима, то было принято вынести воспроизведение музыки из непосредственно логики. В логике остались лишь строчки, передающие название необходимого музыкального трека.
- В игре есть 3 типа музыки:
- Быстрые звуки: стрельба, взрывы, подбор бонуса и пр. Их необходимо лишь запустить и все, дальше можно про них забыть.
- Звуки, воспроизводимые какое-то определенное количество времени. Их мы воспроизводим, пока приходит команда о воспроизведении. Если команды нет, то мы останавливаем воспроизведение этого звука.
- Фоновые звуки. Эти звуки играют на фоне.
- В процессе обновления состояния игры, некоторые классы могут добавлять или название трека, который надо воспроизвести, или словарь, в котором указан объект и название трека.
- Класс плеера анализирует переданный ему список из звуков, которые надо воспроизвести, и воспроизводит или останавливает те или иные звуки. Звуки, которые должны воспроизводиться в течении некоторого времени, проверяются со списком уже воспроизводимых звуков. Если данный звук не найден, то он останавливается и удаляется из списка. Если же найден, то он не трогается.
- Что касается фоновой музыки. Добавление музыки происходит так же в список воспроизводимых треков, однако под специальным ключом – change_music, где значение, это ключ из доступных фоновых звуков.
- Получив команду о смене фоновой музыки, происходит плавное затухание старого и плавное выведение из тишины нового звука.
Алгоритм спавна ботов:
- За спавн и обработку временных периодов отвечает класс BotManager. В его конструктор передается объект игры, через который устанавливается связь, как с ботами, так и с другими игровыми объектами.
- После создания объекта, он начинает спавнить ботов в зависимости от некоторых условий:
- Если количество ботов на карте меньше необходимого числа (4 для одного игрока и 6 для двух).
- Если истек таймер спавна бота. Таймер динамический и изменяется в зависимости от количества игроков и номера уровня.
- Если свободна клетка для спавна.
- Если игра еще продолжается (т.е. не установлен ни проигрыш, ни выигрыш игрока).
- Если выполнены все вышеперечисленные условия, то создается объект бота.
- Общее количество и количество каждого типа ботов назначается в файле mobs_count.py, в котором хранится некоторое количество комбинаций ботов, которые загружаются в момент создания объекта менеджера.
Алгоритм движения ботов:
- В зависимости от того какой у ботов в данный момент проходит временной период – боты могут двигаться: просто катаясь по карте или следуя за какой-либо определенной целью (орел или ближайший игрок)
- Если бот не имеет определенной цели (т.е. он должен просто кататься по карте), то боту случайным образом назначается направление движения. 2.1) Если бот уперся во что-либо, то он случайно выбирает другое направление для движения. Фактически бот начинает крутиться или почасовой, или против часовой стрелки, случайно выбирая следующее направление для движения.
- Если у бота установлена цель – игрок, то:
- Бот узнает координаты ближайшего игрока.
- Начинает двигаться в направлении игрока, пока не упрется во что-то или не приедет к танку игрока
- Если бот уперся в стенку, то бот начинает передвигаться по «лабиринту», используя алгоритм «движение по лабиринту, держась одной из рук за одну из сторон лабиринта».
- Найдя выход в сторону, в которую раньше двигался бот, бот поворачивает туда и продолжает движение, держась одной из сторон, пока его направление движения не совпадет с «предпочтительным» направлением (это то направление, в котором надо двигаться в принципе, чтобы добраться до цели).
- Если направление совпало, то бот сразу начинает двигаться в «предпочтительном» направлении.
- Если у бота установлена цель – орел, то выполняется алгоритм, описанный в пункте 3, с одним лишь различием, а именно в том, что бот двигается не в направлении игроков, а в направлении орла.
- Если у бота установлена цель – игрок, но живых игроков нет, то бот начинает просто кататься по карте.
Некоторое пояснение к работе выше изложенного алгоритма. Хочу остановиться на пункте движения к цели. В нашем проекте не используется анализ карты или построение кратчайшего маршрута к цели из-за того, что цель, к которой должен двигаться бот, может быть окружена непроходимыми стенами. Исходя из этой логики, было решено выбрать алгоритм, который бы просто определял «предпочтительное» направление движения, и далее обрабатывал ситуации столкновения с различными препятствиями. Что касается обработки столкновений, то тут всем занимается рандом.
Небольшое пояснение по поводу случайного выбора. У ботов реализован алгоритм обследования лабиринта, придерживаясь одной из сторон этого лабиринта. Рандом в данном алгоритме лишь решает, куда боту нужно повернуть, в зависимости от стороны, в которую бот в данный момент двигается, от предыдущей стороны, в которую он двигался и от предпочтительного направления. Что я имею в виду: допустим, бот двигается вниз и перед ним появляется стена, то столкнувшись с ней, он случайно выбирает, куда ему двигаться – вправо или влево. Выбрав, ну допустим лево, бот двигается в этом направлении, пытаясь найти выход снизу – в стороне, в которую он ехал до этого. Допустим, что он уперся в угол. Теперь бот выбирает – или ему начать двигаться вправо (в обратную сторону, ибо есть некая доля вероятности, что выход мог оказаться в другой стороне) или двигаться вверх (при этом пытаясь найти выход в левой стороне – в той, в которую он раньше двигался). Однако, из-за того, что вероятности, иногда, могут быть диаметрально противоположными, бот может начать крутиться на месте (особенно это характерно в П-образных местах лабиринта), пытаясь поймать вероятность, которая ему прикажет двигаться в некую другую сторону, тем самым вызволив его из «замкнутого круга». В процессе тестирования алгоритм показал, что в нем достаточно много частных случаев, однако практически все в данный момент программа умеет отлавливать, тем самым, не позволяя ботам «застрять» на одном месте.
Разрабатываемый программный продукт представляет собой многооконное приложение. В нашем проекте классы являются как логической составляющей игры, так и позволяют отрисовывать различные картинки.Начнем с того, что в нашем проекте планировалось добавление онлайн кооператива, именно этим можно объяснить причину вынесения воспроизведения музыки в отдельный класс, а также обработку нажатых клавиш и многое другое. Также перед дальнейшим описанием реализации тех или иных алгоритмов хочется пояснить, почему по-разному происходит загрузка музыки и текстур игры. Связано это также с подготовкой базы для онлайна. Музыка загружается вся на стороне клиента и от сервера не зависит. Однако с изображениями другая история. В игре обработка некоторых столкновений происходит через сравнение масок картинок, вследствие чего на сервере обязаны быть текстуры игры. Плюс к этому, загрузка бы всех картинок привела бы к увеличению размера пересылаемого файла через интернет, что, возможно бы привело к задержкам в обновлении объекта игры. Именно поэтому все спрайты игры хранят в себе лишь одно изображение, которое они могут перезагружать в процессе игры. Хочется отметить, что при создании объекта игры с динамической подгрузкой текстур, производительность игры не уменьшается.
Начнем с описания реализации класса Client. Конструктор класса принимает тип игры, номер уровня и Surface, который используется для правильного начального масштабирования игры. Конструктор загружает настройки игры, создает объекты плеера и объект игры. В будущем планируется изменить реализацию, для возможности создания объекта игры и управления им удаленно, через использование сети интернет. Класс клиента имеет 2 ключевых метода: update() – метод, обновляющий состояние игры, считывающий нажатые клавиши, а также считывающий список треков, которые надо воспроизвести. Кроме этого, он отвечает и за выход в главное меню. Т.е. проверяя параметр game.feedback, мы можем узнать, что необходимо сделать с игрой (перезапустить или выйти в меню) 2 метод – render(), вызывающий аналогичный метод у объекта игры, который отрисовывает необходимую геометрию на переданную поверхность.
Стоит уделить внимание и классу MusicPlayer, отвечающий за воспроизведение и остановку игровой музыки. При инициализации загружается большое количество файлов звуков и музыки, что вызывает долгую первую загрузку класса. Класс имеет два метода для воспроизведения, как музыки, так и звуков (play_music и play_list). Для анализирования списка воспроизводимых звуков был написан метод analyze_active_sound_list, который составляет «белый списко» - список тех звуков, которые должны продолжать играть, далее метод просматривает список активных звуков, и если активный звук не найден в «белом списке», то он выключается. Это очень краткое, но достоверное описание работы данного класса. Класс Game занимается созданием уровня, обновлением состояния спрайтов и отслеживанием состояния игры (проигрыш, выигрыш, пауза, окно быстрого выхода в меню). В данном классе особый интерес вызывает обработка .tmx файла уровня игры. Для этого создан класс Map, который позволяет определять положение клеток тех или иных слоев. Здесь следует сделать небольшое пояснение: программа Tiled Map Editor каждому тайлу текстуры присваивает порядковый номер, через который можно узнать расположение этих тайлов название их слоя и пр. Однако чтобы определить положение клетки, нужно передать не просто id, указанный в программе, а именно gid, это специальный id, который формирует объект .tmx файла. Как раз класс Map и занимается обработкой обычного id в gid и возвращением положения клеток на карте.
Прежде чем речь пойдет об игровых спратах, хочется закончить описание остальных классов, таких как: PauseScreen, ConfirmWindow, GameOverScreen и некоторых других, которые фактически не являются игровыми объектами, а лишь занимаются отображением каких-либо картинок/текстов за определенные промежутки времени. В них использованы стандартные возможности Pygame, таких как blit (для отображения одних поверхностей на другие), font/font.render (создание шрифта и преобразование строки в поверхность, также в этих классах много различных коэффициентов и других расчетов для правильного позиционирования картинок).
Необходимо отметить, что все игровые спрайты (танки игроков и ботов, бонусы, пули, взрывы) должны иметь как минимум две составляющие: image (картинка, которая отображается на экране) и rect (прямоугольник, с помощью которого происходит позиционирование спрайта на карте и обработка столкновений с другими спрайтами). Если кратко описать, чем занимаются спрайты в нашем проекте, так это как раз-таки изменением картинки, в зависимости от игровой ситуации, а также изменение положения rect (т.е. это обработка перемещения, столкновения и пр.).
Разберемся с классом пули. Пуля представляет собой передвигающийся с постоянной скоростью прямоугольник (вместо прямоугольника установлена картинка). Обработка столкновений происходит через обнаружение пересекающихся прямоугольников. Т.к. пуля умеет рикошетить, то было принято решение оставить одну картинку пули, а при загрузке ее вращать с использованием Pygame.transform.rotate. Остановимся на рикошетах. Рикошет работает так: если пуля попала в 1/3 по краям танка, то возможет рикошет, если выпадет вероятность < 1/3. Если такое происходит, то пуля поворачивает на 45 градусов от своей первоначальной траектории. Следует заметить, что было введено ограничение на количество рикошетов – пуля сможет срикошетить лишь 1 раз. Что касается пересечения со стенами. После попадания в стену, происходит проверка на возможность разрушения стены. Если разрушение возможно, то с помощью пересечения по маске вычисляется первая точка соприкосновения. Далее эта точка отправляется в объект стены, с которой произошло пересечение, и стена, сравнивая координаты и свое состояние (какой внешний вид она имеет) решает в какую часть стены было попадание, и какое новое внешнее изображение нужно загрузить. Сделаем небольшое отступление по поводу обработки коллизий у танков. Обработка столкновений со стенами происходит через проверку пересечений по маске, что позволяет танкам заезжать на «отломанные» кусочки стен. Реализация логики передвижения танков ботов реализовано стандартными проверками и с использованием функции choice из библиотеки random, позволяющей выбирать случайно один элемент из списка.
Реализация различных анимации происходит через поочередную смену изображений, с использованием таймера (ориентация по времени происходит с использованием Pygame.time.get_ticks). Использование таймеров и флагов позволяет полностью контролировать, как анимации, так и передвижение игроков. Что я подразумеваю: в игре есть механика, когда при проигрывании анимации спавна танк не должен перемещаться по карте. Реализовано это с использованием некоторых флагов и таймеров. Также, что касается некоторых анимаций, большинство анимаций «мигания» происходит с помощью «прозрачных поверхностей», которые устанавливаются вместе image, тем самым скрывая объекты с игровой карты.
На этом я закончу краткий экскурс по описанию реализации некоторых алгоритмов проекта.
Цель и задачи, поставленные в работе, выполнены. Создана игра «Tanks Battle», имеющая интуитивнопонятный графический интерфейс. Для реализации была выбрана среда разработки Pycharm, приложение для создания игровых карт Tiled Map Editor, библиотеки Pygame и pytmx. Программа предоставляет возможность погрузиться в мир бесконечных танковых сражений и ощутить себя настоящим танкистом.


