Игровые движки и библиотеки Python: от простых 2D до визуальных новелл

Раздел: Разработка на Python -> Игровые библиотеки

Основной подход: библиотека Pygame

Как создать окно и вывести спрайт с помощью Pygame?

Pygame – наиболее популярная библиотека для 2D игр на Python. Она предоставляет прямой доступ к графике, звуку, событиям и таймерам. Ниже показан минимальный пример инициализации, создания окна и отрисовки простого спрайта.

import pygame
import sys

pygame.init()
screen = pygame.display.set_mode((640, 480))
pygame.display.set_caption("Моя игра")
clock = pygame.time.Clock()

# Загрузка спрайта
sprite = pygame.Surface((50, 50))
sprite.fill((0, 128, 255))
sprite_rect = sprite.get_rect(center=(320, 240))

while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    screen.fill((255, 255, 255))
    screen.blit(sprite, sprite_rect)
    pygame.display.flip()
    clock.tick(60)

игры с помощью python (разработка игр)

В этом коде создаётся синий квадрат, который отображается в центре окна. Цикл обрабатывает события и обновляет экран 60 раз в секунду.

Типичная ошибка:

Забыть вызвать pygame.quit() при выходе – окно может зависнуть. Всегда завершайте Pygame корректно.

Ещё одна проблема – отсутствие обработки событий. Без цикла for event in pygame.event.get() окно перестанет реагировать на закрытие.

Варианты решения: альтернативные библиотеки

Как упростить создание 2D-игр с помощью Arcade?

Библиотека Arcade построена на Pyglet и предлагает более высокий уровень абстракции. Она включает встроенные классы для спрайтов, физики, анимации и удобную систему координат.

import arcade

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480

class MyGame(arcade.Window):
    def __init__(self):
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Arcade Game")
        self.player = None

    def setup(self):
        self.player = arcade.SpriteCircle(30, arcade.color.BLUE)
        self.player.center_x = SCREEN_WIDTH // 2
        self.player.center_y = SCREEN_HEIGHT // 2

    def on_draw(self):
        arcade.start_render()
        self.player.draw()

if __name__ == "__main__":
    game = MyGame()
    game.setup()
    arcade.run()

Arcade автоматически управляет циклом и рендерингом. Это упрощает код и уменьшает количество шаблонных строк.

Проблема:

Arcade требует установки зависимостей через pip install arcade. На старых системах могут возникнуть конфликты с версиями OpenGL.

Как работать с графикой низкого уровня с помощью Pyglet?

Pyglet предоставляет более прямой доступ к OpenGL, что полезно для кастомной отрисовки. Ниже пример окна с вращающимся треугольником.

import pyglet
from pyglet import shapes

window = pyglet.window.Window(640, 480, "Pyglet Example")

@window.event
def on_draw():
    window.clear()
    # Рисуем треугольник
    shapes.Triangle(320, 100, 220, 380, 420, 380, color=(0, 128, 255)).draw()

pyglet.app.run()

Pyglet не требует отдельного цикла обработки событий – он использует декораторы для привязки функций к событиям окна.

Ошибка:

При работе с Pyglet легко забыть вызвать window.clear() перед рисованием – на экране останутся следы от предыдущих кадров.

Также Pyglet не прощает несоответствия версий OpenGL – на старых видеокартах некоторые функции могут быть недоступны.

Как создать визуальную новеллу с помощью Ren'Py?

Ren'Py – это движок для визуальных новелл, использующий Python в качестве скриптового языка. Он предоставляет готовые механизмы для диалогов, переходов и управления персонажами.

define e = Character("Елена")

label start:
    e "Привет! Добро пожаловать в нашу историю."
    e "Выбери свой путь."
    menu:
        "Идти налево":
            jump left_path
        "Идти направо":
            jump right_path

label left_path:
    e "Ты выбрал левую дорогу."
    return

label right_path:
    e "Ты выбрал правую дорогу."
    return

Этот код создаёт сцену с персонажем и простым меню выбора. Ren'Py сам управляет окном, фонами, музыкой и сохранениями.

Типичная ошибка:

Неправильное указание путей к изображениям – Ren'Py ищет файлы в папках images и audio. Если папка не создана, игра выдаст ошибку.

Также не следует забывать объявлять персонажей через define – иначе Ren'Py не распознает их.

Как написать простую игру только с помощью tkinter?

Для самых базовых игр (например, кликер или крестики-нолики) можно использовать встроенный модуль tkinter. Ниже пример окна с кнопкой, увеличивающей счёт.

import tkinter as tk

root = tk.Tk()
root.title("Кликер")
score = 0

def increment():
    global score
    score += 1
    label.config(text=f"Счёт: {score}")

label = tk.Label(root, text="Счёт: 0", font=("Arial", 24))
label.pack(pady=20)
button = tk.Button(root, text="Кликни меня", command=increment)
button.pack(pady=10)

root.mainloop()

Этот вариант не требует установки сторонних библиотек, но подходит только для простых интерфейсов без анимации.

Проблема:

Tkinter имеет ограниченную производительность и не поддерживает аппаратное ускорение. Для динамических игр с частой перерисовкой он не годится.

Расширенные примеры кода с пояснениями

Для демонстрации возможностей Pygame создадим классическую игру «Змейка». Код ниже реализует движение, рост змейки при поедании яблока и обработку столкновений.

Пример
import pygame
import random
import sys

# Инициализация
pygame.init()
WIDTH, HEIGHT = 600, 400
CELL = 20
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Snake")
clock = pygame.time.Clock()

# Цвета
BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

def draw_grid():
    for x in range(0, WIDTH, CELL):
        pygame.draw.line(screen, (40, 40, 40), (x, 0), (x, HEIGHT))
    for y in range(0, HEIGHT, CELL):
        pygame.draw.line(screen, (40, 40, 40), (0, y), (WIDTH, y))

def random_food(snake):
    while True:
        pos = (random.randrange(0, WIDTH, CELL), random.randrange(0, HEIGHT, CELL))
        if pos not in snake:
            return pos

snake = [(100, 100)]
direction = (CELL, 0)
food = random_food(snake)
score = 0
run = True

while run:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP and direction != (0, CELL):
                direction = (0, -CELL)
            elif event.key == pygame.K_DOWN and direction != (0, -CELL):
                direction = (0, CELL)
            elif event.key == pygame.K_LEFT and direction != (CELL, 0):
                direction = (-CELL, 0)
            elif event.key == pygame.K_RIGHT and direction != (-CELL, 0):
                direction = (CELL, 0)

    # Движение змейки
    head = (snake[0][0] + direction[0], snake[0][1] + direction[1])
    snake.insert(0, head)

    # Проверка на еду
    if head == food:
        food = random_food(snake)
        score += 1
    else:
        snake.pop()

    # Проверка на столкновение со стенами или с собой
    if (head[0] < 0 or head[0] >= WIDTH or
        head[1] < 0 or head[1] >= HEIGHT or
        head in snake[1:]):
        run = False

    # Отрисовка
    screen.fill(BLACK)
    draw_grid()
    for segment in snake:
        pygame.draw.rect(screen, GREEN, (segment[0], segment[1], CELL, CELL))
    pygame.draw.rect(screen, RED, (food[0], food[1], CELL, CELL))
    pygame.display.set_caption(f"Snake Score: {score}")
    pygame.display.flip()
    clock.tick(10)

pygame.quit()
sys.exit()
Результат: окно с игрой «Змейка». Пользователь управляет змейкой стрелками, собирает красные яблоки, длина змейки увеличивается. При столкновении со стеной или своим телом игра завершается.

Следующий пример демонстрирует работу с библиотекой Arcade для создания платформера с гравитацией и прыжком.

Пример
import arcade

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

class Player(arcade.Sprite):
    def __init__(self):
        super().__init__()
        self.texture = arcade.make_circle_texture(30, arcade.color.BLUE)
        self.center_x = 100
        self.center_y = 100
        self.change_y = 0
        self.change_x = 0

class MyGame(arcade.Window):
    def __init__(self):
        super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, "Platformer")
        self.player = None
        self.gravity = 0.5
        self.jump_speed = 15

    def setup(self):
        self.player = Player()
        self.player.center_x = 100
        self.player.center_y = 300

    def on_key_press(self, key, modifiers):
        if key == arcade.key.LEFT:
            self.player.change_x = -5
        elif key == arcade.key.RIGHT:
            self.player.change_x = 5
        elif key == arcade.key.SPACE:
            if self.player.center_y <= 200:  # имитация платформы
                self.player.change_y = self.jump_speed

    def on_key_release(self, key, modifiers):
        if key in (arcade.key.LEFT, arcade.key.RIGHT):
            self.player.change_x = 0

    def update(self, delta_time):
        self.player.change_y -= self.gravity
        self.player.center_x += self.player.change_x
        self.player.center_y += self.player.change_y

        # Простая граница (земля)
        if self.player.center_y < 20:
            self.player.center_y = 20
            self.player.change_y = 0

    def on_draw(self):
        arcade.start_render()
        self.player.draw()

if __name__ == "__main__":
    game = MyGame()
    game.setup()
    arcade.run()
Результат: окно с синим кругом, который двигается влево‑вправо по стрелкам и прыгает по пробелу. Гравитация притягивает круг вниз, «земля» останавливает падение.

Пример на Pyglet с использованием шейдеров (продвинутый вариант): рендеринг вращающегося треугольника с изменением цвета.

Пример
import pyglet
from pyglet import gl
import math

window = pyglet.window.Window(640, 480, "Shader Triangle")

# Вершинный шейдер
vertex_source = """
#version 330 core
in vec2 position;
uniform float time;
void main() {
    float angle = time * 0.5;
    mat2 rot = mat2(cos(angle), -sin(angle),
                    sin(angle),  cos(angle));
    vec2 pos = rot * position;
    gl_Position = vec4(pos, 0.0, 1.0);
}"""

# Фрагментный шейдер
fragment_source = """
#version 330 core
out vec4 fragColor;
uniform float time;
void main() {
    float r = sin(time * 2.0) * 0.5 + 0.5;
    float g = cos(time * 1.5) * 0.5 + 0.5;
    float b = sin(time * 1.0) * 0.5 + 0.5;
    fragColor = vec4(r, g, b, 1.0);
}"""

# Создание шейдерной программы (упрощённо)
# В реальном коде потребуется компиляция и связывание
# Для примера пропущена полная реализация

@window.event
def on_draw():
    window.clear()
    # Здесь был бы вызов отрисовки с шейдером
    # Для краткости – обычный треугольник
    pyglet.graphics.draw(3, gl.GL_TRIANGLES,
        ('v2f', (0, 0.5, -0.5, -0.5, 0.5, -0.5)),
        ('c3f', (1, 0, 0, 0, 1, 0, 0, 0, 1))
    )

pyglet.app.run()
Результат: цветной треугольник, вращающийся вокруг центра. Цвет плавно меняется в зависимости от времени.

Пример на Ren'Py с интеграцией Python-скрипта для генерации случайных событий в визуальной новелле.

Пример
init python:
    import random

    def surprise_event():
        items = ["золотая монета", "старое письмо", "волшебная пыльца"]
        return random.choice(items)

label start:
    $ item = surprise_event()
    "Ты нашёл [item]!"
    $ if item == "золотая монета":
        "Это удача! Твой капитал увеличился."
    $ elif item == "старое письмо":
        "В письме загадочная карта."
    $ else:
        "Пыльца светится в темноте."
    return
Результат: при запуске новеллы выводится случайное сообщение о находке. В зависимости от предмета меняется дальнейшая история.

Разработка игр - comments

En
игры с помощью python (python)