Загрузчик для больших игр на мобильных устройствах
Такие платформы, как Android и iOS, имеют ограничение на размер приложения, которое можно загрузить из их магазинов приложений. Для поддержки больших игр Ren'Py предоставляет загрузчик (downloader), представляющий собой небольшую игру, которая загружает основную игру.
Требования
Чтобы это работало, вам понадобится достаточно производительный веб-хостинг. Сервисы для загрузки файлов, такие как mega, или игровые хостинги, вроде itch.io, не подойдут. Загрузчик был протестирован с дроплетом Digital Ocean под управлением Nginx, а также с Cloudflare R2, который позволяет бесплатно размещать до 10 ГБ.
В частности, веб-сервер должен поддерживать загрузку файлов, и он будет работать лучше, если сможет обрабатывать множество HTTP Range-запросов одновременно.
Как это работает
Для использования загрузчика вам потребуется собрать две игры. Первая — это игра-загрузчик, которая содержит движок Ren'Py и сам загрузчик. Именно её пользователь получит на свой телефон.
Вторая — это основная игра, визуальная новелла, в которую они хотят играть. Она будет загружена игрой-загрузчиком и установлена на устройство, если это возможно.
Обе игры должны быть собраны с одной и той же версией Ren'Py — если будет несоответствие, загрузчик обновит основную игру. В остальном, основная игра должна обновлять себя сама, что стало возможным благодаря тому, что теперь система обновлений Ren'Py работает и на мобильных платформах.
Когда игра-загрузчик запускается, она проверяет, установлена ли основная игра. Если да, и версии совпадают, запустится основная игра. В противном случае запустится загрузчик и предложит игроку скачать основную игру.
Сборка и загрузка основной игры
Основное изменение, которое нужно внести в вашу основную игру, — это добавить строку:
define build.game_only_update = True
в начало вашего файла options.rpy. Это добавит новую опцию, которая появится на экране "Сборка дистрибутивов" (Build Distributions) в лаунчере: "Game-Only Update for Mobile".
В "Сборке дистрибутивов" (Build Distributions) отметьте "Game-Only Update for Mobile". Снимите галочки со всех остальных пакетов сборки. В разделе "Опции" (Options) убедитесь, что включена опция "Build Updates" (Создавать обновления), а остальные параметры настроены по вашему усмотрению. Настройки по умолчанию вполне подойдут.
Затем запустите процесс сборки.
Когда сборка игры будет завершена, у вас будет всё необходимое. Вам нужно будет загрузить updates.json, updates.ecdsa и всё содержимое каталога rpu на ваш веб-сервер. (Убедитесь, что каталог rpu находится рядом с двумя файлами обновлений, а файлы .rpu — внутри каталога rpu.)
Создание игры-загрузчика
Игра-загрузчик — это игра на Ren'Py, но её размер должен быть значительно меньше 100 МБ. В ней можно делать всё то же, что и в маленькой игре: показать вступительную сцену, воспроизводить музыку и т. д. Её нужно будет создавать отдельно и собирать для Android или iOS, используя обычные методы.
В то же время она должна спросить игрока, хочет ли он начать загрузку, упомянуть, что это может повлечь за собой расходы, если он не подключен к Wi-Fi, а затем загрузить игру.
Для максимальной безопасности файл public_key.pem, созданный при сборке основной игры, следует скопировать в корневой каталог игры-загрузчика. Это открытый ключ, который будет использоваться для проверки загрузки.
Вот пример скрипта для игры-загрузчика с использованием ассетов из обучающей игры:
# URL-адрес файла updates.json на вашем веб-сервере.
define URL = "https://www.domain.com/game-updates/updates.json"
# Отключаем сохранения в игре-загрузчике.
define config.save = False
define e = Character("Eileen", image="eileen")
label splashscreen:
scene bg washington
show eileen happy at left
$ downloader = updater.start_game_download(URL)
e "Добро пожаловать в игру-загрузчик."
e "Она скачает основную игру на ваш телефон, чтобы вы могли в неё играть."
if downloader.download_total:
$ download_mb = int(round(downloader.download_total / 1024 / 1024, 0))
e "Чтобы играть в эту игру, вам нужно скачать [download_mb] мегабайт данных. Если вы не подключены к Wi-Fi, с вас может быть списана плата. Нажмите на экран, чтобы продолжить."
else:
e "Чтобы играть в эту игру, вам нужно скачать некоторые данные. Если вы не подключены к Wi-Fi, с вас может быть списана плата. Нажмите на экран, чтобы продолжить."
$ updater.continue_game_download()
Всё довольно просто, но есть несколько моментов. Во-первых, он устанавливает config.save в значение False. Это важно, так как отключает сохранение и загрузку, включая настройки, чтобы сохранения загрузчика не влияли на основную игру.
Также всё происходит на сплеш-скрине (splashscreen), чтобы начаться немедленно и отключить возможность входа в игровое меню.
В самом начале игра вызывает updater.start_game_download() для сбора информации о загрузке. После этого игра немного тянет время, заставляя Эйлин произносить несколько реплик, пока проверяется размер загружаемых данных.
После нескольких кликов игрока игра может уже знать размер загрузки, а может и нет, поэтому мы используем условие для его проверки. Если точное число известно, оно преобразуется в мегабайты и отображается. Если нет, отображается более общий текст. (Также можно было бы жёстко задать приблизительный размер загрузки прямо в коде игры).
Наконец, вызывается updater.continue_game_download(), которая запускает процесс загрузки. Появится экран, и вы мало что сможете сделать — но музыка будет играть, и ATL продолжит работать.
Когда загрузка завершится, основная игра запустится автоматически. Если она провалится, загрузчик перезапустится, и игрок сможет попробовать снова
Экран загрузчика
Загрузчик — это экран, который показывает прогресс загрузки. Экран по умолчанию находится в файле renpy/common/00updater.rpy и может быть настроен путём создания его копии в каталоге вашей игры.
Вот код по умолчанию:
screen downloader(u):
style_prefix "downloader"
frame:
has vbox
if u.state == u.CHECKING or u.state == u.PREPARING:
text _("Preparing to download the game data.")
elif u.state == u.DOWNLOADING or u.state == u.UNPACKING:
text _("Downloading the game data.")
elif u.state == u.FINISHING or u.state == u.DONE:
text _("The game data has been downloaded.")
else: # An error or unknown state.
text _("An error occurred when trying to download game data:")
if u.message is not None:
text "[u.message!q]"
text _("This game cannot be run until the game data has been downloaded.")
if u.progress is not None:
null height gui._scale(10)
bar value (u.progress or 0.0) range 1.0
if u.can_proceed:
textbutton _("Retry") action u.proceed
style downloader_frame:
xalign 0.5
xsize 0.5
xpadding gui._scale(20)
ypos .25
ypadding gui._scale(20)
style downloader_vbox:
xfill True
spacing gui._scale(10)
style downloader_text:
xalign 0.5
text_align 0.5
layout "subtitle"
style downloader_label:
xalign 0.5
style downloader_button:
xalign 0.5
Функции загрузчика
- updater.continue_game_download(screen='downloader')
Продолжает загрузку игровых данных. Цикл будет продолжаться до тех пор, пока загрузка не будет завершена или пользователь не выйдет из игры.
- updater.start_game_download(url, **kwargs)
Начинает загрузку игровых данных с url. Этот процесс определяет, что необходимо загрузить, и возвращает объект Update.