PyEngine
Библиотека для создания 2D-игр на Python, построенная поверх pygame. PyEngine не заменяет pygame — он дополняет его, беря на себя рутинные задачи: управление окном, обработку ввода, коллизии, камеры, очереди отрисовки, анимации и работу со сценами.
Вместо того чтобы каждый раз писать цикл обработки событий, считать дельту времени, вручную проверять пересечения прямоугольников и сдвигать координаты для камеры, ты получаешь готовые методы. Если чего-то не хватает — всегда можно обратиться напрямую к pygame.
Установка
Есть два способа начать работу с PyEngine.
Способ 1: Через pip (рекомендуется для пользователей)
Когда библиотека будет опубликована на PyPI, ты сможешь установить её одной командой:
pip install pyengine-x
Способ 2: Ручная установка (для разработчиков и тестирования)
Скопируй папку pyengine из этого репозитория в папку своего проекта. Затем импортируй нужные модули:
from pyengine.engine import PyEngine
from pyengine.assets import Assets
from pyengine.scene import Scene
from pyengine.tilemap import Tile, TileSet, TileMap
Assets
Класс с предустановленными цветами и шрифтами, чтобы не гуглить RGB-значения и не помнить названия системных шрифтов.
from pyengine.assets import Assets
# Использование констант
engine.create_figure('player', 'rect', Assets.BLUE, (100, 100, 32, 32))
engine.create_font('title', Assets.FONT_ARIAL, 48)
# Утилиты для работы с цветами
dark_red = Assets.darken(Assets.RED, 0.7)
light_blue = Assets.lighten(Assets.BLUE, 1.3)
random = Assets.random_color()
Константы
Цвета: WHITE, BLACK, RED, GREEN, BLUE, YELLOW, CYAN, MAGENTA, GRAY, DARK_GRAY, ORANGE, PURPLE
Шрифты: FONT_ARIAL, FONT_COMIC, FONT_TIMES, FONT_COURIER
Методы
get_color(name) Возвращает кортеж цвета по строковому имени. Если цвет не найден, возвращает чёрный.
name(str): название цвета, например'red','dark_gray'.
random_color() Генерирует случайный цвет в формате (R, G, B).
darken(color, factor=0.7) Затемняет цвет, умножая каждую RGB-компоненту на коэффициент.
color(tuple): исходный цвет.factor(float): коэффициент затемнения (0.7 = темнее на 30%).
lighten(color, factor=1.3) Осветляет цвет, умножая каждую RGB-компоненту на коэффициент, но не даёт превысить 255.
color(tuple): исходный цвет.factor(float): коэффициент осветления (1.3 = ярче на 30%).
Sound
Миксин для работы со звуками и музыкой. Автоматически подключается к PyEngine, поэтому все методы доступны через engine.
# Загрузка и воспроизведение звуков
engine.load_sound('jump', 'sounds/jump.wav')
engine.play_sound('jump')
# Фоновая музыка
engine.load_music('music/theme.ogg')
engine.play_music(loop=True)
engine.set_music_volume(0.5)
Звуковые эффекты
load_sound(name, path) Загружает звуковой эффект из файла и сохраняет под указанным именем.
name(str): идентификатор звука.path(str): путь к файлу.
play_sound(name, loop=False) Воспроизводит загруженный звуковой эффект.
name(str): имя звука.loop(bool): если True, зацикливает воспроизведение.
stop_sound(name) Останавливает воспроизведение звукового эффекта.
name(str): имя звука.
set_sound_volume(name, volume) Устанавливает громкость конкретного звукового эффекта.
name(str): имя звука.volume(float): громкость от 0.0 до 1.0.
Музыка
load_music(path) Загружает фоновую музыку из файла. Можно загрузить только один трек одновременно.
path(str): путь к файлу.
play_music(loop=False) Запускает воспроизведение загруженной музыки.
loop(bool): если True, зацикливает музыку.
stop_music() Полностью останавливает музыку.
pause_music()
Приостанавливает воспроизведение музыки. Продолжить можно через resume_music.
resume_music() Продолжает воспроизведение музыки с момента паузы.
set_music_volume(volume) Устанавливает громкость фоновой музыки.
volume(float): громкость от 0.0 до 1.0.
fade_in_music(seconds=2.0, loop=False) Запускает музыку с плавным нарастанием громкости от нуля.
seconds(float): длительность нарастания в секундах.loop(bool): зацикливать ли музыку.
fade_out_music(seconds=2.0) Плавно затухает музыку до тишины за указанное время.
seconds(float): длительность затухания в секундах.
set_all_volume(volume) Устанавливает одну громкость сразу для всех звуков и музыки.
volume(float): громкость от 0.0 до 1.0.
Scene
Базовый класс для создания сцен. Сцена — это отдельный экран игры: меню, уровень, пауза, game over. Каждая сцена имеет свой жизненный цикл.
from pyengine.scene import Scene
class MyScene(Scene):
def on_enter(self, **kwargs): ...
def on_exit(self): ...
def events(self, event): ...
def update(self): ...
def draw(self): ...
Методы жизненного цикла
**on_enter(kwargs) Вызывается при активации сцены. Здесь создаются фигуры, загружаются карты, устанавливаются начальные позиции.
**kwargs: любые аргументы, переданные при переключении черезengine.set_scene('name', key=value).
on_exit() Вызывается при выходе из сцены. Используется для очистки ресурсов, остановки музыки, сохранения прогресса.
Методы каждого кадра
events(event) Принимает каждое событие pygame. Обрабатывай здесь ввод: нажатия клавиш, клики мыши, движение курсора.
event(pygame.event.Event): объект события pygame.
update() Вызывается каждый кадр перед отрисовкой. Здесь логика: движение, физика, проверки коллизий, обновление состояний.
draw() Вызывается каждый кадр. Здесь отрисовка: фигуры, текст, карты, UI.
Figure
Универсальный графический объект. Одна фигура может быть прямоугольником, кругом, эллипсом, линией, полигоном или спрайтом.
# Создание фигур
engine.create_figure('player', 'rect', Assets.BLUE, (100, 100, 32, 32))
engine.create_figure('enemy', 'circle', Assets.RED, (300, 200, 20))
engine.create_figure('coin', 'sprite', Assets.YELLOW, (500, 400, 'coin_img'))
# Управление
engine.figures['player'].set_pos(200, 150)
engine.figures['player'].move(10, -5)
engine.figures['player'].clamp_to_screen()
engine.figures['player'].play('idle_anim') # Запуск анимации
Позиция и движение
set_pos(x, y) Устанавливает новые координаты фигуры. Автоматически синхронизирует внутренние переменные и параметры отрисовки.
x(float): новая позиция по X.y(float): новая позиция по Y.
move(dx, dy) Смещает фигуру относительно текущей позиции.
dx(float): смещение по X.dy(float): смещение по Y.
clamp_to_screen() Ограничивает позицию фигуры границами экрана. Если фигура выходит за край, возвращает её обратно.
Отрисовка
draw(offset=(0.0, 0.0)) Отрисовывает фигуру на экране. Обычно вызывается автоматически движком, но можно вызвать вручную.
offset(tuple[float, float]): смещение камеры (ox, oy). По умолчанию (0, 0).
set_image(image_name)
Меняет изображение спрайта. Работает только для фигур с shape='sprite'.
image_name(str): имя изображения, ранее загруженного черезengine.load_image.
Геометрия
radius (свойство) Возвращает радиус фигуры. Работает только для круга. Для остальных форм возвращает 0.0.
get_center() Возвращает координаты (x, y) центра фигуры. Для круга — центр, для прямоугольника — середина, для линии — середина отрезка.
get_bounds() Возвращает ограничивающий прямоугольник в формате (x, y, ширина, высота). Работает для любой формы, включая полигон.
Коллизии
push_out(other) Выталкивает текущую фигуру наружу из другой фигуры, если они пересеклись. Работает для любых комбинаций форм.
other(Figure): фигура, из которой нужно вытолкнуть.
Анимации
play(anim_name) Запускает указанную анимацию, сбрасывая текущий кадр и таймер. Движок автоматически обновляет кадры в главном цикле.
anim_name(str): Имя анимации, зарегистрированной черезengine.add_animation.
Engine
Главный класс движка. Создаёт окно, управляет сценами, фигурами, вводом, коллизиями и отрисовкой.
from pyengine.engine import PyEngine
engine = PyEngine(
title='My Game',
background=Assets.BLACK,
width=800,
height=600
)
engine.initialize(start_scene='menu')
engine.run()
Конструктор
init(title='Py-Engine', background=(50, 50, 50), width=800, height=600)
Создаёт экземпляр движка. Окно пока не открывается — это делает initialize.
title(str): заголовок окна.background(tuple): цвет фона в RGB.width(int): ширина окна в пикселях.height(int): высота окна в пикселях.
Жизненный цикл
initialize(start_scene=None)
Инициализирует pygame, создаёт окно, настраивает таймер. После этого можно вызывать run.
start_scene(str | None): имя сцены, на которую переключиться сразу после запуска.
run() Запускает главный игровой цикл. Сам обрабатывает события, считает dt, очищает экран, вызывает update/draw сцен, обновляет камеру, отрисовывает очередь и ограничивает FPS до 60. Блокирующий метод — работает пока окно не закроют.
Сцены
add_scene(name, scene)
Регистрирует сцену под именем. Без регистрации set_scene не сработает.
name(str): уникальное имя сцены.scene(Scene): экземпляр класса, наследующего от Scene.
add_scenes(scenes) Регистрирует сразу несколько сцен из словаря.
scenes(dict[str, Scene]): словарь {имя: экземпляр_сцены}.
**set_scene(name, destroy=True, kwargs) Планирует переключение на другую сцену. Переключение произойдёт безопасно в конце текущего кадра.
name(str): имя целевой сцены.destroy(bool): если True, удаляет все фигуры старой сцены.**kwargs: аргументы, которые попадут вon_enterновой сцены.
Фигуры
create_figure(name, shape, color, params, width=0, auto_draw=False, use_camera=True) Создаёт фигуру и добавляет в движок.
name(str): уникальное имя.shape(Literal): тип формы —'rect','circle','ellipse','line','polygon','sprite'.color(tuple): цвет RGB.params(tuple): параметры формы:- rect:
(x, y, ширина, высота) - circle:
(x, y, радиус) - ellipse:
(x, y, ширина, высота) - line:
(x1, y1, x2, y2) - polygon:
(список_точек,) - sprite:
(x, y, имя_картинки)
- rect:
width(int): толщина обводки. 0 = заливка.auto_draw(bool): если True, фигура сама добавляется в очередь отрисовки каждый кадр.use_camera(bool): если True, фигура двигается вместе с камерой. Для элементов UI (счёт, HP-бар) ставьFalse.
draw_figure(name) Добавляет фигуру в очередь отрисовки на текущий кадр.
name(str): имя фигуры.
remove_figure(name) Удаляет фигуру из движка.
name(str): имя фигуры.
clear_figures()
Удаляет все фигуры. Обычно не нужно вызывать вручную — set_scene(destroy=True) делает это сам.
Текст и шрифты
create_font(name, font='Arial', size=36) Создаёт шрифт и кэширует его.
name(str): идентификатор для дальнейших вызовов.font(str): название системного шрифта.size(int): размер в пунктах.
draw_text(font, text, x, y, color=(0, 0, 0), antialias=True, use_camera=False) Рисует текст на экране.
font(str): имя шрифта, созданного черезcreate_font.text(str): текст.x, y(int): координаты левого верхнего угла текста.color(tuple): цвет текста.antialias(bool): сглаживание.use_camera(bool): двигать ли текст с камерой. По умолчаниюFalse, так как текст часто является UI.
draw_text_on_figure(fig_name, font, text, color=(0, 0, 0), use_camera=False) Рисует текст по центру фигуры. Сам считает центр и размеры текста.
fig_name(str): имя фигуры.font(str): имя шрифта.text(str): текст.color(tuple): цвет текста.use_camera(bool): двигать ли текст с камерой.
draw_overlay(color=(0, 0, 0), alpha=180) Рисует полупрозрачный слой поверх всего. Годится для затемнения при паузе или открытия меню. Всегда игнорирует камеру.
color(tuple): цвет слоя.alpha(int): прозрачность от 0 (невидимо) до 255 (глухо).
Изображения
load_image(name, path) Загружает картинку из файла.
name(str): идентификатор.path(str): путь к файлу.
scale_image(name, scale) Масштабирует картинку пропорционально.
name(str): имя картинки.scale(float): коэффициент (0.5 = в два раза меньше, 2.0 = в два раза больше).
resize_image(name, width, height) Растягивает/сжимает картинку до точных размеров. Пропорции не сохраняет.
name(str): имя картинки.width, height(int): новые размеры в пикселях.
flip_image(name, new_name, flip_x=False, flip_y=False) Зеркалит картинку и сохраняет копию под новым именем. Оригинал не трогает.
name(str): имя исходной картинки.new_name(str): имя копии.flip_x(bool): отразить по горизонтали.flip_y(bool): отразить по вертикали.
rotate_image(name, new_name, angle) Поворачивает картинку и сохраняет копию.
name(str): имя исходной картинки.new_name(str): имя копии.angle(float): угол в градусах, против часовой стрелки.
Ввод
get_event(event, etype='keyDown', key=None) Проверяет конкретное событие в цикле обработки.
event(pygame.event.Event): объект события.etype(EventLiteral): тип —'quit','keyDown','keyUp','mouseButtonDown','mouseButtonUp','mouseMotion'.key(KeyLiteral | None): конкретная клавиша. Если None, проверяется только тип.
is_pressed(key) Возвращает True, пока клавиша или кнопка мыши удерживается. Это не событие, а состояние.
key(KeyLiteral): имя клавиши ('w','space','left') или кнопки мыши ('leftMouseButton').
is_mouse_over(name, cooldown=0.0) Возвращает True, если курсор над фигурой.
name(str): имя фигуры.cooldown(float): минимальный интервал между True. Полезно чтобы не спамить наведением.
is_clicked(name, cooldown=0.0, button='leftMouseButton') Возвращает True, если по фигуре кликнули мышью. Встроенный кулдаун не даёт сработать дважды.
name(str): имя фигуры.cooldown(float): интервал в секундах.button(MouseButton):'leftMouseButton','rightMouseButton'или'middleMouseButton'.
Коллизии
check_collision(name1, name2, cooldown=0.5) Проверяют ли две фигуры друг друга. Встроенный кулдаун не даст вернуть True дважды за один "контакт". Поддерживает точную проверку круг-прямоугольник.
name1, name2(str): имена фигур.cooldown(float): время в секундах до повторного True.
can_move_to(name, new_x, new_y, obstacles) Симулирует перемещение и проверяет, не врежется ли фигура в препятствия. Если врежется — возвращает старые координаты.
name(str): имя фигуры.new_x, new_y(float): куда хотим переместить.obstacles(list[str]): список имён фигур-препятствий.- Возвращает:
(bool, float, float)— (можно_ли, итоговый_x, итоговый_y).
resolve_collisions(movers, walls) Проходит по списку movers и выталкивает каждого из каждого wall, если они пересекаются.
movers(list[str]): имена подвижных фигур.walls(list[str]): имена статичных препятствий.
Движение
move(name, speed, dx=0.0, dy=0.0, target=None, map_name=None, obstacles=None, use_dt=True, grid_mode=False) Универсальный метод. Двигает фигуру, сам умножает скорость на dt, сам проверяет коллизии с тайлами карты и другими фигурами.
name(str): имя фигуры.speed(float): скорость. Еслиuse_dt=True— пикселей в секунду. ЕслиFalse— пикселей за кадр.dx, dy(float): направление. Обычно -1, 0 или 1. Вgrid_modeигнорируются, если фигура уже движется к текущей цели.target(tuple[float, float] | None): координаты цели. Фигура плавно пойдёт к ним. Игнорируется вgrid_mode.map_name(str | None): имя TileMap для коллизий с тайлами. Обязательно дляgrid_mode.obstacles(list[str] | None): имена фигур-препятствий.use_dt(bool): учитывать ли dt. False для UI-анимаций.grid_mode(bool): если True, включает пошаговое движение по сетке тайлов. Фигура будет останавливаться в центре каждого тайла и ждать нового ввода.
Пример использования:
# Стандартное непрерывное движение (игрок бежит, врезается в стены)
dx, dy = 0, 0
if engine.is_pressed('left'): dx -= 1
if engine.is_pressed('right'): dx += 1
engine.move('player', speed=200, dx=dx, dy=dy, map_name='level')
# Пошаговое движение по тайлам (герой идет клетка за клеткой)
dx, dy = 0, 0
if engine.is_pressed('left'): dx = -1
if engine.is_pressed('right'): dx = 1
if engine.is_pressed('up'): dy = -1
if engine.is_pressed('down'): dy = 1
# Фигура плавно перейдет в центр соседнего тайла и остановится там
engine.move('hero', speed=150, dx=dx, dy=dy, map_name='level', grid_mode=True)
move_towards(current_x, current_y, target_x, target_y, speed=0.1) Линейная интерполяция (lerp). Возвращает новую точку, которая ближе к цели на долю speed.
current_x, current_y(float): текущие координаты.target_x, target_y(float): целевые координаты.speed(float): коэффициент от 0.0 до 1.0 (1.0 = телепорт, 0.05 = медленное скольжение).- Возвращает:
(int, int).
move_at_speed(current_x, current_y, target_x, target_y, speed=5.0) Сдвигает точку ровно на speed пикселей в сторону цели. Если до цели ближе чем speed — ставит точно на цель.
current_x, current_y(float): текущие координаты.target_x, target_y(float): целевые координаты.speed(float): смещение в пикселях за один вызов.- Возвращает:
(int, int).
TileMap и камера
add_tilemap(name, tilemap)
Регистрирует карту в движке. После этого move сможет сам проверять коллизии с ней.
name(str): имя карты.tilemap(TileMap): экземпляр TileMap.
set_camera_target(figure_name) Камера автоматически следует за фигурой, держа её в центре экрана.
figure_name(str): имя фигуры.
set_camera_bounds(bounds_type, bounds_data) Ограничивает движение камеры, чтобы она не показывала пустоту за пределами мира.
bounds_type(str):'map'(ограничить картой) или'window'(не уходить в отрицательные координаты).bounds_data: экземплярTileMap(еслиbounds_type='map').
clamp_to_map(name, map_name)
Не даёт фигуре вылезти за границы карты. Аналог clamp_to_screen, но для размеров карты.
name(str): имя фигуры.map_name(str): имя карты.
check_tile_collision(name, map_name, tile_id) Проверяет, стоит ли фигура на тайле с указанным ID.
name(str): имя фигуры.map_name(str): имя карты.tile_id(int): ID тайла.
Пример использования:
# Камера следит за игроком и не выходит за границы карты
engine.set_camera_target('player')
engine.set_camera_bounds('map', level)
# Игрок двигается с коллизиями
engine.move('player', speed=200, dx=dx, dy=dy, map_name='level')
engine.clamp_to_map('player', 'level')
# Проверка подбора монетки
if engine.check_tile_collision('player', 'level', tile_id=10):
level.remove_tile(tx, ty, layer_index=1)
score += 1
Анимации
add_animation(name, frames, fps=8.0) Регистрирует анимацию для использования фигурами.
name(str): Уникальное имя анимации.frames(list[str]): Список имен изображений (кадров), загруженных черезload_image.fps(float): Количество кадров в секунду.
Пример использования:
engine.load_image('p1', 'assets/player_1.png')
engine.load_image('p2', 'assets/player_2.png')
engine.add_animation('player_idle', frames=['p1', 'p2'], fps=8)
engine.figures['hero'].play('player_idle') # Движок сам меняет кадры
Утилиты
check_cooldown(name, seconds) Таймер-защитка. Возвращает True, если прошло нужное время с последнего вызова. Иначе False.
name(str): уникальный ID таймера.seconds(float): длительность в секундах.
Пример использования:
# Выстрел не чаще раза в полсекунды
if engine.is_pressed('space') and engine.check_cooldown('shoot', seconds=0.5):
print("Бах!")
get_text_pos(font, text, align_x, align_y, offset_x=0, offset_y=0) Считает координаты для текста с учётом выравнивания и размера текста.
font(str): имя шрифта (нужен для измерения текста).text(str): текст.align_x(AlignX):'left','middle'или'right'.align_y(AlignY):'up','middle'или'down'.offset_x, offset_y(int): дополнительный сдвиг.- Возвращает:
(int, int).
get_shape_pos(shape_w, shape_h, align_x, align_y, offset_x=0, offset_y=0) Считает координаты для фигуры с учётом её размеров и выравнивания.
shape_w, shape_h(int): ширина и высота.align_x(AlignX):'left','middle'или'right'.align_y(AlignY):'up','middle'или'down'.offset_x, offset_y(int): дополнительный сдвиг.- Возвращает:
(int, int).
Пример использования:
# Кнопка по центру экрана
btn_pos = engine.get_shape_pos(200, 60, 'middle', 'middle')
engine.create_figure('btn', 'rect', Assets.GREEN, btn_pos)
TileMap
Система тайловых карт с поддержкой нескольких слоёв. Тайлы — это статичная основа мира: пол, стены, декорации. Динамические объекты (игрок, враги, монетки) — это Figure.
from pyengine.tilemap import Tile, TileSet, TileMap
# 1. Создаём набор тайлов
tileset = TileSet(tile_width=32, tile_height=32)
tileset.add_tile(Tile(1, Assets.GREEN, solid=False)) # Земля (не твёрдая)
tileset.add_tile(Tile(2, Assets.GRAY, solid=True)) # Стена (твёрдая)
tileset.add_tile(Tile(3, Assets.YELLOW)) # Монетка
# 2. Создаём карту и слои
level = TileMap(engine, tileset, width=30, height=20)
ground_layer = level.add_layer() # Индекс 0
object_layer = level.add_layer() # Индекс 1
# Указываем слой для коллизий
level.collision_layer_index = ground_layer
# 3. Быстрая загрузка из строк (прототипирование)
level.load_from_lines([
"######################",
"#....................#",
"#..22222.............#",
"######################",
], legend={'#': 2, '.': 1, '2': 1}, layer_index=ground_layer)
# 4. Конвертация координат
px, py = level.cell_to_pixel(5, 2) # Вернёт (160, 64)
col, row = level.pixel_to_cell(170, 70) # Вернёт (5, 2)
engine.add_tilemap('level', level)
Tile
Один тип тайла. init(tile_id, color=None, image_name=None, solid=False, exceptions=None)
tile_id(int): уникальный ID тайла.color(tuple | None): цвет тайла (используется, если нетimage_name).image_name(str | None): имя изображения изengine.images(приоритетнее цвета).solid(bool): если True, фигуры не смогут проходить сквозь этот тайл.exceptions(list[str] | None): список названий фигур, которые могут проходить сквозь этот тайл даже еслиsolid=True.
TileSet
Набор тайлов с общим размером. init(tile_width=32, tile_height=32)
tile_width(int): ширина одного тайла в пикселях.tile_height(int): высота одного тайла в пикселях.
add_tile(tile) Добавляет тайл в набор. Если тайл с таким ID уже существует, он будет перезаписан.
tile(Tile): экземпляр Tile.
TileLayer
Один слой тайлов. Карта может содержать несколько слоёв для разделения пола, объектов, переднего плана. init(width, height, default_tile=0)
width(int): ширина слоя в тайлах.height(int): высота слоя в тайлах.default_tile(int): ID тайла по умолчанию (используется при создании и удалении).
set_tile(x, y, tile_id) Устанавливает тайл с указанным ID в позицию (x, y). Если координаты выходят за границы слоя, метод ничего не делает.
x, y(int): позиция в тайлах.tile_id(int): ID тайла.
get_tile(x, y) Возвращает ID тайла в позиции (x, y). Если координаты выходят за границы слоя, возвращает 0.
x, y(int): позиция в тайлах.
remove_tile(x, y) Удаляет тайл из позиции (x, y), делая ячейку пустой (ID=0). Пустые тайлы не отрисовываются, что позволяет видеть слои под ними. Если координаты выходят за границы слоя, метод ничего не делает.
x, y(int): позиция в тайлах.
TileMap
Двумерная карта тайлов с поддержкой нескольких слоёв. init(engine, tileset, width, height)
engine(PyEngine): экземпляр PyEngine.tileset(TileSet): набор тайлов.width(int): ширина карты в тайлах.height(int): высота карты в тайлах.
add_layer(default_tile=0) Добавляет новый слой тайлов в карту. Возвращает индекс созданного слоя (начинается с 0).
default_tile(int): ID тайла по умолчанию для этого слоя.
set_tile(x, y, tile_id, layer_index=0)
Устанавливает тайл с указанным ID в позицию (x, y) на заданном слое. Если layer_index не указан, используется слой 0.
x, y(int): позиция в тайлах.tile_id(int): ID тайла.layer_index(int): индекс слоя.
get_tile(x, y, layer_index=0)
Возвращает ID тайла в позиции (x, y) на заданном слое. Если layer_index не указан, используется слой 0. Если координаты или индекс слоя некорректны, возвращает 0.
x, y(int): позиция в тайлах.layer_index(int): индекс слоя.
remove_tile(x, y, layer_index=0)
Удаляет тайл из позиции (x, y) на заданном слое, делая ячейку пустой. Пустые тайлы не отрисовываются, позволяя видеть нижние слои. Если layer_index не указан, используется слой 0.
x, y(int): позиция в тайлах.layer_index(int): индекс слоя.
is_solid(x, y, figure_name=None)
Проверяет, является ли тайл в позиции (x, y) непроходимым. Проверка выполняется только на слое collision_layer_index. Если figure_name указан и находится в списке exceptions тайла, возвращается False.
x, y(int): позиция в тайлах.figure_name(str | None): имя фигуры для проверки исключений.
draw() Отрисовывает все слои карты на экран с учётом смещения камеры. Слои рисуются по порядку их добавления (от 0 до последнего). Оптимизировано: рисуются только видимые тайлы.
load_from_lines(lines, legend, layer_index=0) Загружает карту из списка строк (текстовый уровень).
lines(list[str]): Список строк, где каждый символ представляет тайл.legend(dict[str, int]): Словарь соответствия символа и ID тайла (например,{'#': 1, '.': 0}).layer_index(int): Индекс слоя для записи данных.
cell_to_pixel(col, row) Преобразует координаты ячейки (col, row) в пиксельные координаты (x, y).
col, row(int): координаты ячейки.- Возвращает:
(int, int).
pixel_to_cell(x, y) Преобразует пиксельные координаты (x, y) в координаты ячейки (col, row).
x, y(float): пиксельные координаты.- Возвращает:
(int, int).
Мини-игры
Четыре примера, показывающие разные комбинации функционала.
Игра 1. Без сцен, без TileMap
Простая игра: убегай от красного круга.
from pyengine.engine import PyEngine
from pyengine.assets import Assets
engine = PyEngine('Убеги!', Assets.BLACK, 640, 480)
engine.initialize()
engine.create_figure('player', 'rect', Assets.BLUE, (300, 220, 24, 24))
engine.create_figure('enemy', 'circle', Assets.RED, (50, 50, 16))
engine.create_font('f', Assets.FONT_ARIAL, 20)
alive = True
def update():
global alive
if not alive: return
dx = dy = 0
if engine.is_pressed('left'): dx -= 1
if engine.is_pressed('right'): dx += 1
if engine.is_pressed('up'): dy -= 1
if engine.is_pressed('down'): dy += 1
engine.move('player', speed=250, dx=dx, dy=dy)
engine.figures['player'].clamp_to_screen()
px, py = engine.figures['player'].get_center()
engine.move('enemy', speed=120, target=(px, py))
if engine.check_collision('player', 'enemy', cooldown=999):
alive = False
def draw():
engine.draw_figure('player')
engine.draw_figure('enemy')
if not alive:
engine.draw_overlay(Assets.BLACK, 180)
engine.draw_text('f', 'Game Over', 250, 220, Assets.RED, use_camera=False)
engine.update = update
engine.draw = draw
engine.run()
Игра 2. Без сцен, с TileMap
Собирай монетки в лабиринте. Камера следит за игроком и ограничена границами карты.
from pyengine.engine import PyEngine
from pyengine.assets import Assets
from pyengine.tilemap import Tile, TileSet, TileMap
engine = PyEngine('Монетки', Assets.BLACK, 480, 320)
engine.initialize()
ts = TileSet(32, 32)
ts.add_tile(Tile(1, Assets.DARK_GRAY, solid=True))
ts.add_tile(Tile(2, Assets.YELLOW))
level = TileMap(engine, ts, 30, 20)
ground = level.add_layer()
objects = level.add_layer()
level.collision_layer_index = ground
level.load_from_lines([
"##############################",
"#............................#",
"#...####...........####......#",
"#...#................#....2..#",
"##############################",
], legend={'#': 1, '.': 0, '2': 2}, layer_index=ground)
engine.add_tilemap('level', level)
engine.create_figure('p', 'rect', Assets.CYAN, (64, 64, 24, 24))
engine.set_camera_target('p')
engine.set_camera_bounds('map', level) # Камера не покажет пустоту за краями
engine.create_font('f', Assets.FONT_ARIAL, 18)
score = 0
def update():
global score
dx = dy = 0
if engine.is_pressed('left'): dx -= 1
if engine.is_pressed('right'): dx += 1
if engine.is_pressed('up'): dy -= 1
if engine.is_pressed('down'): dy += 1
engine.move('p', speed=180, dx=dx, dy=dy, map_name='level')
# Простая проверка: если коснулись тайла 2, меняем его на 0 (пустоту)
if engine.check_tile_collision('p', 'level', 2):
cx, cy = engine.figures['p'].get_center()
tx, ty = level.pixel_to_cell(cx, cy)
level.remove_tile(tx, ty, layer_index=objects)
score += 1
def draw():
level.draw()
engine.draw_figure('p')
engine.draw_text('f', f'Монетки: {score}', 10, 10, Assets.WHITE, use_camera=False)
engine.update = update
engine.draw = draw
engine.run()
Игра 3. Со сценами, без TileMap
Три сцены: меню, арена, game over.
from pyengine.engine import PyEngine
from pyengine.scene import Scene
from pyengine.assets import Assets
engine = PyEngine('Арена', Assets.BLACK, 480, 320)
class Menu(Scene):
def on_enter(self, **kw):
self.engine.create_figure('btn', 'rect', Assets.GREEN,
self.engine.get_shape_pos(180, 50, 'middle', 'middle'))
self.engine.create_font('f', Assets.FONT_ARIAL, 24)
def events(self, event):
if self.engine.get_event(event, 'mouseButtonDown', 'leftMouseButton'):
if self.engine.is_clicked('btn', cooldown=0.3):
self.engine.set_scene('arena')
def update(self): pass
def draw(self):
self.engine.draw_figure('btn')
self.engine.draw_text_on_figure('btn', 'f', 'Играть', Assets.WHITE, use_camera=False)
class Arena(Scene):
def on_enter(self, **kw):
self.engine.create_figure('p', 'circle', Assets.BLUE, (100, 160, 18))
self.engine.create_figure('e', 'circle', Assets.RED, (380, 160, 18))
self.engine.create_font('f', Assets.FONT_ARIAL, 16)
def events(self, event): pass
def update(self):
px, py = self.engine.figures['p'].get_center()
self.engine.move('e', speed=100, target=(px, py))
dx = dy = 0
if self.engine.is_pressed('left'): dx -= 1
if self.engine.is_pressed('right'): dx += 1
if self.engine.is_pressed('up'): dy -= 1
if self.engine.is_pressed('down'): dy += 1
self.engine.move('p', speed=200, dx=dx, dy=dy)
self.engine.figures['p'].clamp_to_screen()
if self.engine.check_collision('p', 'e', cooldown=999):
self.engine.set_scene('dead')
def draw(self):
self.engine.draw_figure('p')
self.engine.draw_figure('e')
class Dead(Scene):
def on_enter(self, **kw):
self.engine.create_font('f', Assets.FONT_ARIAL, 36)
def events(self, event): pass
def update(self):
if self.engine.is_pressed('space'):
self.engine.set_scene('menu')
def draw(self):
self.engine.draw_text('f', 'Ты умер. Пробел = меню', 80, 140, Assets.RED, use_camera=False)
engine.add_scenes({'menu': Menu(engine), 'arena': Arena(engine), 'dead': Dead(engine)})
engine.initialize(start_scene='menu')
engine.run()
Игра 4. Со сценами, с TileMap и пошаговым движением
Меню → подземелье → победа. Герой двигается строго по клеткам.
from pyengine.engine import PyEngine
from pyengine.scene import Scene
from pyengine.assets import Assets
from pyengine.tilemap import Tile, TileSet, TileMap
engine = PyEngine('Подземелье', Assets.BLACK, 480, 320)
class Menu(Scene):
def on_enter(self, **kw):
self.engine.create_figure('btn', 'rect', Assets.ORANGE,
self.engine.get_shape_pos(200, 50, 'middle', 'middle'))
self.engine.create_font('f', Assets.FONT_ARIAL, 22)
def events(self, event):
if self.engine.get_event(event, 'mouseButtonDown', 'leftMouseButton'):
if self.engine.is_clicked('btn', 0.3):
self.engine.set_scene('dungeon')
def update(self): pass
def draw(self):
self.engine.draw_figure('btn')
self.engine.draw_text_on_figure('btn', 'f', 'Войти в подземелье', Assets.WHITE, use_camera=False)
class Dungeon(Scene):
def on_enter(self, **kw):
ts = TileSet(32, 32)
ts.add_tile(Tile(1, Assets.GRAY, solid=True))
ts.add_tile(Tile(2, Assets.GREEN))
self.level = TileMap(self.engine, ts, 15, 10)
ground = self.level.add_layer()
self.level.collision_layer_index = ground
self.level.load_from_lines([
"###############",
"#.............#",
"#.#####.......#",
"#.#...#.......#",
"#.#.2.#.......#",
"#.#...#.......#",
"#.#####.......#",
"#.............#",
"#.............#",
"###############",
], legend={'#': 1, '.': 0, '2': 2}, layer_index=ground)
self.engine.add_tilemap('dungeon', self.level)
self.engine.create_figure('p', 'rect', Assets.CYAN, (48, 48, 24, 24))
self.engine.set_camera_target('p')
self.engine.set_camera_bounds('map', self.level)
self.engine.create_font('f', Assets.FONT_ARIAL, 14)
def events(self, event): pass
def update(self):
dx = dy = 0
if self.engine.is_pressed('left'): dx = -1
if self.engine.is_pressed('right'): dx = 1
if self.engine.is_pressed('up'): dy = -1
if self.engine.is_pressed('down'): dy = 1
# grid_mode=True: герой сделает один шаг и остановится в центре тайла
self.engine.move('p', speed=200, dx=dx, dy=dy, map_name='dungeon', grid_mode=True)
if self.engine.check_tile_collision('p', 'dungeon', 2):
self.engine.set_scene('win')
def draw(self):
self.level.draw()
self.engine.draw_figure('p')
self.engine.draw_text('f', 'Найди зелёный выход (пошагово!)', 10, 10, Assets.WHITE, use_camera=False)
class Win(Scene):
def on_enter(self, **kw):
self.engine.create_figure('btn', 'rect', Assets.PURPLE,
self.engine.get_shape_pos(180, 50, 'middle', 'middle'))
self.engine.create_font('f', Assets.FONT_ARIAL, 28)
def events(self, event):
if self.engine.get_event(event, 'mouseButtonDown', 'leftMouseButton'):
if self.engine.is_clicked('btn', 0.3):
self.engine.set_scene('menu')
def update(self): pass
def draw(self):
self.engine.draw_text('f', 'Ты прошёл!', 170, 100, Assets.GREEN, use_camera=False)
self.engine.draw_figure('btn')
self.engine.draw_text_on_figure('btn', 'f', 'Заново', Assets.WHITE, use_camera=False)
engine.add_scenes({
'menu': Menu(engine),
'dungeon': Dungeon(engine),
'win': Win(engine)
})
engine.initialize(start_scene='menu')
engine.run()
Философия PyEngine
PyEngine не пытается стать заменой pygame или навязать сложную архитектуру. Его цель — убрать рутину.
Ты больше не будешь писать одни и те же 20 строк кода для проверки клика по кнопке. Ты не будешь каждый раз изобретать велосипед для камеры, которая следует за игроком. Ты не будешь тратить вечер на отладку коллизий между кругом и прямоугольником.
Библиотека дает готовые строительные блоки. Если нужно что-то специфичное — бери pygame и делай как хочешь. Но для 90% задач в 2D-играх у PyEngine уже есть решение в одну строку.
Для кого это:
- Для тех, кто хочет прототипировать идеи быстро.
- Для новичков, которые хотят понять логику игр, а не тонуть в boilerplate-коде.
- Для опытных разработчиков, которым просто лень каждый раз писать одно и то же.
Удачи в разработке.
Release files for pyengine-x 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pyengine_x-0.1.0.tar.gz | 58.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pyengine_x-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 94.9 kB
Release files / pyengine_x-0.1.0.tar.gz
| Download URL | pyengine_x-0.1.0.tar.gz |
|---|---|
| Size | 58.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
09a7b1b7fcffe9ac5a32a88236c8890c67ea3dd5e57d042da4e52ac68e2c2cd7
|
|
BLAKE2b-256 checksum How to use checksums |
b603647e1ca727f7441b99fe534ede4fa186aa5d96f3cc9c8b40260a2913a0d5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.5
|
Release files / pyengine_x-0.1.0-py3-none-any.whl
| Download URL | pyengine_x-0.1.0-py3-none-any.whl |
|---|---|
| Size | 37.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
70db3d0ec078bf4b8c30832762b6f81a56905aff13994d8e8c9e198d5a45d822
|
|
BLAKE2b-256 checksum How to use checksums |
5ec6de6f0e74b153c5f5e20c2a51d027f277be9694bf086d8cfe58999385b8c9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.5
|