- О проекте
- Установка
- Архитектурное решение
- Мысли разработчика
Проект представляет собой упрощенную версию игры "Астероиды". Изначально проект разрабатывался в качестве лабораторной работы в ВУЗе для знакомства со структурой данных "Квадродерево" и не подразумевал под собой создание игры. В этом проекте квадродерево используется для эффективного обнаружения коллизий разных объектов. Однако, из-за любви автора проекта к Python и ООП, он решил зайти немного дальше и сделать полноценную игру, из-за чего проект получился гораздо больше задуманного.
Что задумывалось изначально:
Что получилось по итогу:
-
Первым делом необходимо скачать с официального сайта Python версии 3.9.6 -
https://www.python.org/downloads/
. -
Затем нужно установить Git -
https://git-scm.com/downloads
. -
Теперь Вы можете клонировать репозиторий в любое удобное место:
git clone https://github.com/Sazoks/asteroids.git
-
Теперь создайте в этой папке виртуальное окружение:
python -m venv venv
-
После создания виртуального окружения активируйте его.
Для Windows:
.\venv\Scripts\activate
Для Linux:
source venv/bin/activate
-
Теперь установите все необходимые зависимости:
pip install -r requirements.txt
-
Играйте!
cd src/ python main.py
В центре внимания находится игрок (класс Player), который может летать с помощью клавиш wasd
и стрелять с помощью ЛКМ
.
Также в системе есть подсистема для менеджмента уровней (класс LevelsManager), которая контролирует уровни в зависимости от счета игрока, а также управляет уровнями всех объектов, зарегистрированных в ней. Объекты для этого должны реализовывать интерфейс (в Python - абстрактный класс) ManagingLevels.
Далее идет подсистема генерации астероидов (класс AsteroidGenerator), которая отвечает за генерацию астероидов в игре. Этот класс реализует интерфейс ManagingLevels и может повышать и понижать свои уровни. Чем больше уровень генератора, тем чаще появляются новые астероиды. Экземпляр этого класса будет зарегистрирован в менеджере уровней.
После этого идет подсистема генерации усилений (класс PowerupsGenerator), которая генерирует объекты усилений. Каждое усиление должно наследовать абстрактный класс Powerup. После этого конкретный класс усиления нужно зарегистрировать в классе PowerupsGenerator.
Суть в том, что каждый конкретный класс усиления, наследующий абстрактный класс Powerup, должен реализовать методы influence и rollback_param, которые оказывают влияние на игрока и возвращают исходные параметры обратно соответственно.
Это позволит легко добавлять любые конкретные классы усилений, чтобы как-то разнообразить игру.
Подсистема менеджмента активных усилений отвечает за контроль времени действия усилений на зарегистрированных в этой подсистеме игроков. Кроме этого, подсистема на каждой итерации игрового цикла отрисовывает все необходимые индикаторы усилений. По истечению времени действия усиления оно удаляется из коллекции усилений конкретного игрока.
И, наконец, подсистема разрешения столкновений игровых объектов. Здесь ключевую роль играют два класса - CollideResolver и CollideResolveFactory. Первый - непосредственно объект для решения коллизий. Он принимает в параметры инициализатора второй класс, реализующий паттерн абстрактной фабрики.
Каждую итерацию игрового цикла квадродерево заполняется игровыми объектами. По ходу заполнения квадродерева оно собирает список коллизийных узлов - узлов, которые имеют неделимый размер и в которых больше одного объекта. Это узлы, которые стоит проверить на столкновения объектов. Так коллайдер в методе решения принимает два различных объекта, которые могут столкнуться, из таких узлов и по их типам определяет нужный класс-решение, которое займется разрешением коллизии этих двух объектов.
Также есть интерфейс (в Python - абстрактный класс, но здесь по смыслу используется как интерфейс) AbstractCollideResolve и конкретные классы, реализующие его. Эти классы и есть классы-решения, которые непосредственно решают столкновения двух объектов.
На выходе мы получаем подсистему, в которой достаточно просто зарегистрировать новый класс-решение (обработчик), чтобы научить подсистему сталкивать любые объекты. Это позволит добавлять любые сущности в проект и с легкостью учить коллайдер, как эти сущности сталкивать между собой.
Вот эта абстракция решает любые столкновения в системе и не нуждается в корректировках, достаточно просто зарегистрировать новый класс-решение в CollideResolveFactory:
# main.py
# Получаем список коллизийных нод.
collision_nodes = game_objects.quadtree.get_collision_nodes()
for node in collision_nodes:
# Получаем все объекты в текущей ноде.
node_objects = node.get_data()
# Попарно отправляем их в коллайдер.
for i in range(len(node_objects) - 1):
for k in range(i + 1, len(node_objects)):
collide_resolver.resolve(node_objects[i], node_objects[k])
- В системе, на взгляд автора, есть один большой недостаток - использование Singleton-класса GlobalGameObjects. В ходе разработки возникла необходимость получать глобальный доступ к некоторым игровым объектам, по типу групп спрайтов, квадродерева, менеджера активных уровней. Делать каждый из этих классов Singleton'ом, очевидно, не самая лучшая идея, поэтому было принято решение сделать единую глобальную точку доступа в проекте для получения нужных данных.
- Автор проекта не самый большой поклонник физики, поэтому физика столкновений астероидов немного кривая :)
- Будь у автора проекта 108 часов в сутках, он бы добавил и генератор вражеских кораблей с ИИ, и классы орудий для игрока, которые сами бы инкапсулировали в себе всю логику стрельбы, и усиления в виде новых орудий, которые мог бы подбирать игрок. Благодаря нынешней архитектуре все это можно добавить с относительной легкостью. Возможно, когда автору вновь станет скучно, он вернется и реализует все свои идеи.