Гид по технологиям

Система сохранения и загрузки в PyGame

7 min read Разработка игр Обновлено 13 Dec 2025
Сохранение и загрузка в PyGame — руководство
Сохранение и загрузка в PyGame — руководство

Игровой монитор с контроллером на столе

Коротко: возможность сохранять и загружать прогресс — ключевой элемент удобства и удержания игроков. PyGame не даёт встроённой «магии» для сохранений, зато Python предоставляет инструменты для сериализации данных и работы с файловой системой. Ниже — подробное руководство и готовые шаблоны, которые можно адаптировать под собственную игру.

Что будет в статье

  • Как устроить простую игру и представить состояние в виде структуры данных
  • Как сохранять и загружать состояние (pickle, JSON, SQLite)
  • Интерфейс слотов сохранения и подтверждение перезаписи
  • Риски и безопасность: почему не стоит доверять pickle из внешних источников
  • Тесты, критерии приёмки, чек‑листы и план восстановления при инцидентах

Введение и простая игра

Перед началом убедитесь, что у вас установлен pip, затем установите PyGame:

pip install pygame

Ниже — минимальный пример игры, где игрок перемещается влево и вправо. Код оставлен в виде рабочей основы — его можно расширять.

import pygame

# Инициализация PyGame
pygame.init()

# Параметры окна
window_width = 800
window_height = 600
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Save and Load System Tutorial")

# Переменные игрока
player_x = 400
player_y = 500
player_speed = 5

# Основной цикл
running = True
clock = pygame.time.Clock()
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

    # Отрисовка
    window.fill((0, 0, 0))
    pygame.draw.rect(window, (255, 255, 255), (player_x, player_y, 50, 50))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Пояснение терминов: сериализация — преобразование структуры данных в байты или текст для хранения; десериализация — обратный процесс.

Представление состояния игры

Самый простой и удобный способ — держать состояние игры в одном словаре. Это облегчает сериализацию и даёт единый источник правды для сохранения.

# Пример структуры состояния
game_state = {
    'player': {
        'x': 400,
        'y': 500,
        'hp': 100
    },
    'level': 1,
    'inventory': ['key', 'potion']
}

Хорошая практика: хранить только примитивные типы (числа, строки, списки, словари). Объекты классов можно сохранять через их словарное представление (.to_dict()) и восстанавливать через фабрики.

Сохранение и загрузка с помощью pickle

Pickle удобен, потому что позволяет сериализовать произвольные объекты Python. Но важно помнить: десериализация непроверенных данных — риск выполнения произвольного кода. Используйте pickle только для локальных, доверенных файлов.

Пример модуля save_game.py с функциями сохранения и загрузки:

import pickle

def save_game_state(game_state, file_name):
    try:
        with open(file_name, 'wb') as file:
            pickle.dump(game_state, file)
            print("Game state saved successfully!")
    except IOError:
        print("Error: Unable to save game state.")

def load_game_state(file_name):
    try:
        with open(file_name, 'rb') as file:
            game_state = pickle.load(file)
            print("Game state loaded successfully!")
            return game_state
    except (IOError, pickle.UnpicklingError):
        print("Error: Unable to load game state.")
        return None

Интеграция в игровой цикл (фрагмент):

# при нажатии S — сохранить, при L — загрузить
if keys[pygame.K_s]:
    save_game_state(game_state, 'save_game.pickle')
if keys[pygame.K_l]:
    loaded = load_game_state('save_game.pickle')
    if loaded:
        game_state = loaded
        player_x = game_state['player']['x']

ВАЖНО: не загружайте pickle‑файлы, полученные извне (интернет, пользователи), без проверки.

Альтернативы pickle: когда применять и почему

  1. JSON

    • Подходит для простых структур (числа, строки, списки, словари).
    • Читаемый человеком формат, безопаснее для внешнего обмена.
    • Не поддерживает бинарные объекты и собственные классы без .to_dict()/from_dict().

    Пример сохранения в JSON:

import json

with open('save_game.json', 'w', encoding='utf-8') as f:
    json.dump(game_state, f, ensure_ascii=False, indent=2)

# Загрузка
with open('save_game.json', 'r', encoding='utf-8') as f:
    game_state = json.load(f)
  1. SQLite

    • Подходит для игр с большим количеством сохранённых сущностей (позиций, инвентарей, прогресса нескольких персонажей).
    • Удобно для сложных запросов и частичных восстановлений (например, загрузить только инвентарь).
  2. Binary formats (MessagePack, protobuf)

    • Быстрее и компактнее JSON; полезны для больших объёмов данных и сетевой передачи.

Выбор зависит от требований: читаемость, безопасность, объём данных и потребность в миграциях.

Интерфейс слотов сохранения

Слоты удобны игрокам — они позволяют иметь несколько независимых сохранений. Пример реализации: три слота, выбор через клавиши. В простом примере вывод в консоль, но тот же принцип применим к GUI в PyGame.

# save_slots — имена файлов для сохранений
save_slots = ['Slot 1', 'Slot 2', 'Slot 3']
selected_slot = None

for event in pygame.event.get():
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_s:
            # показать слоты в консоли
            for i, slot in enumerate(save_slots):
                print(f"{i+1}: {slot}")
            print("Нажмите 1/2/3 чтобы сохранить в слот")

        if event.key in [pygame.K_1, pygame.K_2, pygame.K_3]:
            slot_index = event.key - pygame.K_1
            selected_slot = save_slots[slot_index]
            save_game_state(game_state, f"{selected_slot}.pickle")
            print(f"Game saved in {selected_slot}!")

        if event.key == pygame.K_l:
            for i, slot in enumerate(save_slots):
                print(f"{chr(97+i)}: {slot}")
            print("Нажмите a/b/c чтобы загрузить слот")

        if event.key in [pygame.K_a, pygame.K_b, pygame.K_c]:
            slot_index = event.key - pygame.K_a
            selected_slot = save_slots[slot_index]
            loaded = load_game_state(f"{selected_slot}.pickle")
            if loaded:
                game_state = loaded
                player_x = game_state['player']['x']
                print(f"Game loaded from {selected_slot}!")

Слоты сохранений в аркадной игре

Подпись: пример консольного интерфейса выбора слота.

Подтверждение перезаписи и обработка отсутствующих файлов

Перед перезаписью полезно спросить пользователя, особенно если у него есть несколько важных сохранений. Ниже — расширение функции сохранения с подтверждением и проверкой существования файла.

import os

def save_game_state(game_state, file_name):
    if os.path.exists(file_name):
        overwrite = input("Файл сохранения уже существует. Перезаписать? (y/n): ")
        if overwrite.lower() != 'y':
            print("Сохранение отменено.")
            return
    try:
        with open(file_name, 'wb') as file:
            pickle.dump(game_state, file)
            print("Game state saved successfully!")
    except IOError:
        print("Error: Unable to save game state.")

def load_game_state(file_name):
    if not os.path.exists(file_name):
        print("Error: Save file does not exist.")
        return None
    try:
        with open(file_name, 'rb') as file:
            return pickle.load(file)
    except (IOError, pickle.UnpicklingError):
        print("Error: Unable to load game state. Файл может быть повреждён.")
        return None

Если вы делаете GUI, вместо input() используйте модальное окно с Yes/No.

Обработка ошибок и восстановление при повреждении файлов

Повреждение файлов сохранений — реальная проблема. Вот упрощённый план действий при ошибках:

  • При загрузке: если десериализация падает — не фатальная ошибка. Сообщите пользователю и предложите загрузить другой слот.
  • Храните резервную копию предыдущего сохранения (например, .bak) перед записью нового файла.
  • Используйте контрольную сумму (SHA256) для проверки целостности файла перед десериализацией.
  • Логируйте ошибки в отдельный файл для последующего анализа.

Пример: атомарная запись с резервной копией

import shutil

def atomic_save(game_state, file_name):
    backup = file_name + '.bak'
    if os.path.exists(file_name):
        shutil.copy(file_name, backup)
    try:
        with open(file_name, 'wb') as f:
            pickle.dump(game_state, f)
    except Exception:
        # попытка восстановления из резервной копии
        if os.path.exists(backup):
            shutil.copy(backup, file_name)
        raise

Безопасность и конфиденциальность

  • Никогда не загружайте pickle‑файлы, полученные из ненадёжных источников.
  • Если в сохранениях есть личные данные (имена игроков, прогресс в онлайн‑учёте), соблюдайте требования конфиденциальности: шифрование, минимизация данных и прозрачность для пользователя.
  • Для общераспространяемых файлов используйте JSON или protobuf, а не pickle.

Лучшие практики

  • Храните только необходимые данные в состоянии: позиции, параметры, прогресс. Не сохраняйте открытые файловые дескрипторы, сокеты и т. п.
  • Версионируйте формат сохранений. В game_state добавляйте поле schema_version.
  • Делайте миграции: при загрузке файла более старой версии выполняйте преобразование данных.
  • Держите каталог для сохранений отдельным (например, ./saves/) и проверяйте права доступа.
  • Тестируйте сценарии восстановления и повреждения файлов.

Миграции и совместимость форматов

Добавьте версию формата в game_state:

game_state = {
    'schema_version': 2,
    'player': { ... }
}

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

Тесты и критерии приёмки

Критерии приёмки:

  • Сохранение в слот создаёт файл в каталоге ./saves и его можно загрузить без ошибок.
  • При перезаписи появляется запрос подтверждения.
  • Если файл сохранения повреждён, приложение не аварийно завершает работу — выдаёт понятное сообщение.
  • Миграция формата успешно конвертирует старое сохранение в новую структуру.

Примеры тестов (ручные и автоматические):

  • TC1: Сохранить/загрузить базовый слот — ожидаемый результат: позиция игрока восстанавливается.
  • TC2: Попытаться загрузить несуществующий слот — приложение сообщает об ошибке.
  • TC3: Пометить файл как повреждённый (изменить байты) — приложение предлагает восстановить из .bak или загрузить другой слот.
  • TC4: Проверка миграции — загрузить сохранение старой версии и убедиться, что структура обновлена.

Чек‑листы по ролям

Разработчик:

  • Представление состояния централизовано
  • Есть функции save/load с обработкой ошибок
  • Реализована резервная копия перед перезаписью

QA:

  • Тесты сценариев сохранения/загрузки
  • Проверка миграций
  • Проверка поведения при повреждённых файлах

Дизайнер UX:

  • Информативные сообщения при сохранении/загрузке
  • Подтверждение перезаписи
  • Доступность выбора слота (клавиатура, геймпад, сенсор)

Риск‑матрица и смягчение рисков

  • Риск: повреждение файла сохранения. Смягчение: резервное копирование .bak, контрольные суммы, атомарная запись.
  • Риск: удаление/перезапись важного сохранения. Смягчение: подтверждение, версия, корзина.
  • Риск: выполнение вредоносного кода через pickle. Смягчение: не загружать чужие файлы, использовать JSON/protobuf для обмена.

Примеры ситуаций, когда pickle не подходит

  • Многоплатформенные клиенты, где сохранения обмениваются через сеть с другими пользователями.
  • Если вы хотите, чтобы пользователи могли легко редактировать сохранения вручную.
  • Когда нужно минимизировать риск исполнения произвольного кода.

В таких случаях лучше JSON, protobuf или явная миграция в собственный формат.

Шаблоны и готовые фрагменты

  • Папка хранения: ./saves/
  • Именование файлов: {slot_name}.pickle или {slot_name}.json
  • Добавьте в файл дату/время создания: поле saved_at в ISO 8601 (например, 2025-12-13T10:15:30Z).

Пример структуры meta в сохранении:

'meta': {
    'schema_version': 2,
    'saved_at': '2025-12-13T10:15:30Z',
    'game_version': '1.0.3'
}

План восстановления при инциденте

  1. Пользователь жалуется: “Не загружается сохранение”.
  2. Проверить логи игры и наличие .bak.
  3. Попробовать загрузить .bak вручную.
  4. Если .bak тоже повреждён — предложить пользователю восстановить прогресс из чекпоинтов либо загрузить другое сохранение.
  5. Зафиксировать репродуцируемый кейс и добавить автоматический тест.

Советы по локализации и UX для разных платформ

  • На мобильных устройствах используйте локальные диалоги подтверждения вместо input().
  • Для облачных сохранений (если используются) реализуйте синхронизацию и конфликт‑resolution (версия файла, временная метка, merge).
  • Учитывайте размер сохраняемых данных: на слабых устройствах предпочтительна компактная сериализация.

Интерфейс PyGame со слотами сохранения и загрузки

Указание авторства не требуется: скриншот — Imran

Заключение

Система сохранения и загрузки — не только удобство для игрока, но и важная часть архитектуры игры. Простое представление состояния в словаре, аккуратная сериализация, резервное копирование и понятный интерфейс слотов покрывают большинство потребностей небольших и средних проектов. Для крупных проектов рассмотрите СУБД, версионирование формата и безопасные форматы обмена.

Краткие рекомендации для старта:

  • Начните с простого словаря game_state и JSON для ранней разработки.
  • Добавьте версию схемы и резервные копии до перехода на бинарную сериализацию.
  • Не используйте pickle для данных из ненадёжных источников.

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

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Настройки AirPods: обнаружение уха и двойное касание
Гайды

Настройки AirPods: обнаружение уха и двойное касание

Удалить сохранённые Wi‑Fi сети на Mac
Mac

Удалить сохранённые Wi‑Fi сети на Mac

Открыть сайт по расписанию в Windows
Windows

Открыть сайт по расписанию в Windows

Как выделить больше RAM для Roblox
Игры

Как выделить больше RAM для Roblox

Найти ключ восстановления BitLocker в Windows 11
Windows

Найти ключ восстановления BitLocker в Windows 11

Как обрезать видео в Windows 11 — быстро и просто
Видео

Как обрезать видео в Windows 11 — быстро и просто