Прокручивающиеся фоны в Pygame — параллакс и оптимизация
Кратко
Прокручивающиеся фоны (скроллинг) в Pygame добавляют глубину и динамику: несколько слоёв, движущихся с разной скоростью, создают параллакс. В статье показано, как реализовать базовый скроллинг, улучшить его параллаксом, использовать изображения вместо заливки цветом и какие оптимизации применить для плавного рендеринга.

Прокручивающиеся фоны существенно повышают визуальную привлекательность и ощущение глубины в игре. Они создают иллюзию движения и делают мир более живым. В Pygame реализовать скроллинг можно в несколько простых шагов — от прямой заливки цветом до слоёв с изображениями и параллакса.
Что вы получите
- Рабочую структуру простого проекта Pygame с прокручивающимся фоном;
- Варианты реализации: цветные прямоугольные слои, несколько слоёв параллакса, изображение-фон с масштабированием и оптимизацией;
- Советник по производительности и чек-листы для тестирования на разных устройствах.
1. Создаём простую игру (каркас)
Начните с минимального примера, где игрок может двигаться влево и вправо, а на экране есть две платформы. Создайте файл simple-game.py.
Опишите начальную настройку Pygame, создайте размеры экрана, задайте стартовые координаты игрока и скорость движения, определите платформы с помощью pygame.Rect.
Пример структуры (псевдо/каркас):
import pygame
pygame.init()
screen_width, screen_height = 800, 600
screen = pygame.display.set_mode((screen_width, screen_height))
clock = pygame.time.Clock()
player_x, player_y = 100, screen_height - 150
player_speed = 5
rect1 = pygame.Rect(50, screen_height - 100, 200, 10)
rect2 = pygame.Rect(screen_width - 250, screen_height - 200, 200, 10)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Обработка ввода, обновление, рендеринг
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
player_x -= player_speed
if keys[pygame.K_RIGHT]:
player_x += player_speed
screen.fill((0, 0, 0))
pygame.draw.rect(screen, (0, 255, 0), rect1)
pygame.draw.rect(screen, (0, 255, 0), rect2)
pygame.draw.rect(screen, (255, 255, 255), (player_x, player_y, 30, 50))
pygame.display.flip()
clock.tick(60)
pygame.quit()Ссылка: код-репозиторий упоминался в оригинале (MIT), используйте свой репозиторий для хранения примеров и тестов.
2. Несколько слоёв фона: параллакс простым способом
Для параллакса создайте несколько слоёв фона (прямоугольников) и задайте каждой разную скорость. Чем дальше слой — тем медленнее он движется. Это создаёт ощущение глубины.
Пример инициализации слоёв:
# Добавьте этот код после настройки экрана
background_layers = [
pygame.Rect(0, 0, screen_width, screen_height),
pygame.Rect(0, 0, screen_width, screen_height)
]
background_colors = [(30, 30, 30), (60, 60, 60)]
background_speeds = [0.1, 1.0]Каждый элемент background_layers покрывает весь экран. background_speeds определяет относительную скорость слоя.
3. Обновление и отрисовка прокручивающегося фона
Обновляйте координаты слоёв в игровом цикле — смещайте по оси X на значение скорости. Когда слой вышел за левый край, возвращайте его в исходное положение, чтобы создать бесконечный цикл.
# Внутри игрового цикла
for i in range(len(background_layers)):
background_layers[i].x -= background_speeds[i]
if background_layers[i].x <= -screen_width:
background_layers[i].x = 0
pygame.draw.rect(screen, background_colors[i], background_layers[i])Обратите внимание: использование вещественных скоростей (например, 0.1) потребует аккуратной работы с позициями: pygame.Rect хранит целочисленные координаты, поэтому при очень маленьких смещениях стоит хранить отдельный float-офсет и присваивать целочисленное значение rect.x при отрисовке.
4. Параллакс, зависящий от движения игрока
Чтобы усилить ощущение глубины, двигайте платформы и фоны относительно игрока: когда игрок двигается — за ним сдвигаются слои или платформы. Это особенно полезно в сайд-скроллерах.
Пример, где платформы представлены словарями с rect и speed:
# Определение платформ
rect1 = pygame.Rect(50, screen_height - 100, 200, 10)
rect2 = pygame.Rect(screen_width - 250, screen_height - 200, 200, 10)
platforms = [
{"rect": rect1, "speed": 3},
{"rect": rect2, "speed": 1}
]
# Внутри игрового цикла
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and player_x > 0:
player_x -= player_speed
for platform in platforms:
platform["rect"].x -= platform["speed"]
if keys[pygame.K_RIGHT] and player_x < screen_width:
player_x += player_speed
for platform in platforms:
platform["rect"].x += platform["speed"]
for platform in platforms:
pygame.draw.rect(screen, (0, 255, 0), platform["rect"])Такой подход включает прокрутку только при движении игрока, делая эффект более выразительным.
5. Дополнительные возможности
Ниже — набор простых улучшений, которые легко интегрировать.
Случайные цвета фонов
Меняйте цвета слоёв случайно, чтобы добавить вариативности при запуске.
import random
background_colors = [
(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
]Дополнительные слои
Добавление большего количества слоёв делает параллакс богаче. Пропорции скоростей должны быть последовательными: дальние слои — медленнее, ближние — быстрее.
background_layers = [
pygame.Rect(0, 0, screen_width, screen_height),
pygame.Rect(0, 0, screen_width, screen_height),
pygame.Rect(0, 0, screen_width, screen_height),
pygame.Rect(0, 0, screen_width, screen_height)
]
background_colors = [
(30, 30, 30),
(60, 60, 60),
(90, 90, 90),
(120, 120, 120)
]
background_speeds = [1, 2, 3, 4]Использование изображений вместо цветов
Изображения дают богатые и детализированные фоны. Загрузите их, выполните convert() для ускорения и масштабируйте под экран.
# До начала игрового цикла
background_images = [
pygame.image.load("background_0.png").convert(),
pygame.image.load("background_1.png").convert(),
pygame.image.load("background_2.png").convert()
]
background_speeds = [1, 2, 3]
for i in range(len(background_images)):
size = (screen_width, screen_height)
background_images[i] = pygame.transform.scale(background_images[i], size)Для отрисовки используйте blit вместо draw.rect:
# Внутри игрового цикла
for i in range(len(background_layers)):
background_layers[i].x -= background_speeds[i]
if background_layers[i].x <= -screen_width:
background_layers[i].x = 0
screen.blit(background_images[i], background_layers[i])Если вы используете изображения, рассмотрите приёмы тайлинга (повторения) по оси X и Y, чтобы избежать заметных швов.
6. Лучшие практики и оптимизация
Оптимизируйте только где нужно
Профилируйте игру — прежде чем оптимизировать, найдите узкие места. Для большинства 2D-игр узким местом чаще всего является частая перезагрузка/скейлинг больших изображений.
Используйте convert() и кэшируйте
Вызов surface.convert() или convert_alpha() снижает затраты на blit. Кэшируйте масштабированные версии изображений под целевые разрешения.
Минимизируйте перерисовку
При возможности перерисовывайте только те области экрана, которые изменились (dirty rects). Однако для полного экрана с параллаксом часто проще обновлять весь экран и держать частоту кадров стабильной.
Управление вещественными смещениями
Храните смещение слоя в отдельной float-переменной (например, offset_x) и преобразуйте в int для rect.x:
layer_offset = 0.0
layer_offset -= 0.1
background_layers[0].x = int(layer_offset)Память и форматы
Сжимаемые форматы а-ля PNG хороши для качества, JPEG — для снимков без альфа. Следите за использованием памяти при множестве больших текстур.
Тестирование на разных разрешениях
Реализуйте масштабирование или отдельные наборы фоновых изображений для популярных аспектов (4:3, 16:9, 21:9).
7. Когда прокрутка не подходит (контр-примеры)
- Не требуется в статических пазлах или минималистичных UI: лишнее движение отвлекает игрока.
- Когда фон должен быть читабельным (например, карта с мелким текстом): прокрутка ухудшит читаемость.
- На слабых устройствах при большом количестве высоких разрешений изображений — стоит отказаться или упростить эффект.
8. Альтернативные подходы
- Tile-based (тайловая) прокрутка: хорошо для платформеров с большим миром.
- Шейдерный параллакс (в OpenGL/SDL): более гибкий и эффективный на GPU.
- Скелетная/слойная композиция: комбинируйте спрайты и эффекты вместо глобального смещения.
9. Ментальные модели и эвристики
- “Чем дальше от камеры — тем медленнее движется” — базовое правило параллакса.
- Поддерживайте относительную скорость слоёв в стабильной пропорции 1 : 0.5 : 0.25 …
- Если производительность падает на 60 FPS, снижайте разрешение фонов или уменьшайте количество слоёв.
10. Чек-лист перед выпуском
- Проверить масштабирование фонов на 3–4 разрешениях
- Прогнать профайлер: рендер → memory → CPU
- Кэшировать все масштабированные изображения
- Обеспечить fallback: упрощённый фон для слабых устройств
- Убедиться в отсутствии визуальных швов при тайлинге
11. Мини-методология внедрения (SOP)
- Прототип: быстрые прямоугольные слои, три скорости.
- Тест: проверьте плавность и восприятие глубины.
- Итерация: добавьте графику вместо цвета, кэшируйте и оптимизируйте.
- Тестирование на устройствах и обратная связь.
- Релиз с опцией «низкое качество фона».
12. Шпаргалка (cheat sheet)
- Для быстрой прокрутки используйте int скорости > 1;
- Для тонкого параллакса используйте float-скорости и храните offset в float;
- convert() = быстрее blit; convert_alpha() = сохраняет альфа;
- При тайлинге: используйте две копии изображения и циклический сдвиг, чтобы избежать «провалов».
13. Пример решения проблемы швов при тайлинге
Если видно швы между повторяющимися изображениями, попробуйте:
- Сгладить края изображений в редакторе;
- Добавить небольшой overlapping (наложение) при blit, затем обрезать;
- Использовать seamless-тайлы (в редакторе подготовить текстуру).
14. Дерево решений: как выбрать тип фона
flowchart TD
A[Нужен фон для сцены?] -->|Да| B{Требуется ли высокая детализация?}
B -->|Да| C[Использовать изображения 'png/jpeg']
B -->|Нет| D[Использовать цветовые слои / градиенты]
C --> E{Требуется параллакс?}
E -->|Да| F[Несколько масштабированных слоёв с разными скоростями]
E -->|Нет| G[Статический фон с витальностью]
D --> H{Производительность важна?}
H -->|Да| I[Минимизировать слои, использовать solid fills]
H -->|Нет| F15. Критерии приёмки
- Фон плавно двигается на целевой частоте кадров (обычно 60 FPS);
- При масштабировании не теряется важная визуальная информация;
- Нет визуальных швов или скачков при бесконечной прокрутке;
- На слабых устройствах есть опция «низкое качество фона».
16. Роли и обязанности (короткий план)
- Геймдизайнер: определяет желаемую глубину и художественный стиль;
- Художник: готовит seamless-текстуры и вариативные слои;
- Программист: реализует прокрутку, оптимизацию и fallback-сценарии;
- QA: тестирует на наборе устройств и фиксирует баги с производительностью.
Итог
Прокручивающиеся фоны — простой и мощный инструмент для оживления 2D-игр в Pygame. Начните с простых цветных слоёв, затем добавляйте слои, изображения и кэширование по мере необходимости. Всегда профилируйте и тестируйте на целевых устройствах: качество эффекта должно балансироваться с производительностью.
Внедряйте параллакс поэтапно: прототип → тест → оптимизация → релиз. Это позволит сохранить ясность архитектуры и обеспечить хороший пользовательский опыт.
Краткое резюме доступно в начале статьи. Если хотите — могу подготовить готовые файлы simple-game.py, scrolling-bg.py и parallax.py на базе примеров выше и снабдить их комментариями для запуска.
Похожие материалы
Как экономить мобильные данные в Apple Music
Персональные результаты Google Assistant на блокировке
Настройка уведомлений Outlook: отключить и адаптировать
Добавить дату и время в Google Sheets
Таймер Помодоро на Python с Tkinter