Постоянные данные

Ren'Py поддерживает постоянные данные — сохраняемые данные, которые не связаны с определённой точкой в игре. Доступ к постоянным данным осуществляется через поля объекта persistent, который привязан к переменной persistent.

Все данные, доступные через поля persistent, сохраняются, когда Ren'Py завершает работу или когда вызывается функция renpy.save_persistent(). Постоянные данные загружаются при запуске Ren'Py и когда Ren'Py обнаруживает, что постоянные данные на диске были обновлены.

Объект persistent является особенным в том, что доступ к неопределенному полю будет иметь значение None, а не вызывать исключение. Если по умолчанию для постоянного значения должно быть что-то отличное от None, следует использовать оператор default:

default persistent.main_background = "princess_not_saved"

Пример использования persistent — создание разблокируемой галереи изображений. Это делается путем хранения флага в persistent, который определяет, была ли галерея разблокирована, как в примере:

label gallery:

    if not persistent.gallery_unlocked:
        show background
        centered "You haven't unlocked this gallery yet."
        $ renpy.full_restart()

    # Здесь фактическое отображение галереи.

Когда игрок получает концовку, которая разблокирует галерею, флаг должен быть установлен в True.

$ persistent.gallery_unlocked = True

Поскольку постоянные данные загружаются до выполнения блоков init python, постоянные данные должны содержать только типы, которые являются родными для Python или Ren'Py. В качестве альтернативы можно использовать классы, определенные в блоках python early, при условии, что эти классы могут быть сериализованы (pickled) и реализуют равенство.

Поля, начинающиеся с двух подчёркиваний (__), поддерживаются, но к ним будет применён эффект искажения имён, описанный в этом разделе, что означает, что они будут специфичны для файла, в котором они определены. Это подразумевает, что если файл будет переименован между двумя релизами, доступ к значению, которое это поле имело в предыдущем релизе, будет нарушен.

Кроме того, эти поля не затрагиваются методом persistent._clear().

Напоминаем, что поля, начинающиеся с одного подчёркивания _, зарезервированы и не должны использоваться.

Слияние постоянных данных

Бывают случаи, когда Ren'Py должен объединить постоянные данные из двух источников. Например, Ren'Py может потребоваться объединить постоянные данные, хранящиеся на USB-накопителе, с постоянными данными с локальной машины.

Ren'Py выполняет это слияние по полям, принимая значение поля, которое было обновлено последним. В некоторых случаях это нежелательное поведение. В таком случае можно использовать функцию renpy.register_persistent().

Например, если у нас есть набор просмотренных концовок, мы бы хотели получить объединение этого набора при слиянии данных.

init python:
    if persistent.endings is None:
        persistent.endings = set()

    def merge_endings(old, new, current):
        current.update(old)
        current.update(new)
        return current

    renpy.register_persistent('endings', merge_endings)

Функции для постоянных данных

persistent._hasattr(field_name)

Проверяет, было ли установлено постоянное поле field_name или нет. Это позволяет различать поля, которые были явно установлены в None, и поля, которые никогда не устанавливались.

persistent._clear(progress=False)

Сбрасывает постоянные данные, за исключением полей, начинающихся с __.

progress

Если true, также сбрасывает данные о прогрессе, которые хранит Ren'Py.

Обратите внимание, что это приведет к повторному применению значений по умолчанию.

renpy.register_persistent(field, func)

Регистрирует функцию, которая используется для слияния значений поля persistent-объекта, загруженного с диска, со значениями текущего persistent-объекта.

field

Имя поля в persistent-объекте.

function

Функция, которая вызывается с тремя параметрами: old, new и current:

old

Значение поля в более старом объекте.

new

Значение поля в более новом объекте.

current

Значение поля в текущем persistent-объекте. Этот параметр предоставляется для случаев, когда идентификатор объекта, на который ссылается поле, не может измениться.

Ожидается, что функция вернёт новое значение поля в persistent-объекте.

renpy.save_persistent()

Сохраняет persistent-данные на диск.

Межигровые постоянные данные

Межигровые постоянные данные (Multi-Game persistence) — это функция, которая позволяет вам обмениваться информацией между играми Ren'Py. Это может быть полезно, если вы планируете сделать серию игр и хотите, чтобы они обменивались информацией.

Для использования межигровых постоянных данных объект MultiPersistent должен быть создан во время инициализации (желательно с помощью define). Затем пользователь может обновлять этот объект и сохранять его на диск, вызывая его метод save. Неопределенные поля по умолчанию равны None. Чтобы обеспечить возможность повторной загрузки объекта в другой игре, мы настоятельно не рекомендуем хранить в объекте экземпляры пользовательских типов.

class MultiPersistent(name, save_on_quit=False)

Создает новый объект MultiPersistent. Это следует вызывать только во время инициализации, и он возвращает новый MultiPersistent с заданным именем.

name

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

save_on_quit

Если это True, этот объект будет автоматически сохраняться при завершении работы Ren'Py.

save()

Сохраняет межигровые постоянные данные на диск. Этот метод необходимо вызывать после изменения данных.

В качестве примера, возьмем первую часть игры, состоящей из двух частей:

define mp = MultiPersistent("demo.renpy.org")

label start:

    # ...

    # Записываем факт, что пользователь прошел первую часть.

    $ mp.beat_part_1 = True
    $ mp.save()

    e "You beat part 1. See you in part 2!"

А это вторая часть:

define mp = MultiPersistent("demo.renpy.org")

label start:

    if mp.beat_part_1:
        e "I see you've beaten part 1, so welcome back!"
    else:
        e "Hmm, you haven't played part 1, why not try it first?"