Как перевести renpy на русский
Перевод игр на Ren’Py
Внимание! Я не гуру перевода. В этой статье я собрал всё, что знаю про переводы на Ren’Py после нескольких переведённых игр для тех, кому интересно или кто сам хочет что-нибудь перевести. Замечания и дополнения приветствуются. Пользуйтесь на здоровье и делитесь качественными переводами 🙂
Основы
Распакуйте архив. В нём лежит файл UnRen.bat
Зачем нужен UnRen.bat (необязательная инфа):
Порой не хочется продираться через всю игру только для того, чтобы получить все сцены, и в конечном итоге упустить некоторые из них. Здесь пригодится опция 1 (Extract RPA packages), она позволяет извлечь все рендеры, анимации (и другие файлы) из пакетов rpa и непосредственно посмотреть их.
Опция 2 (Decompile rpyc files) предназначена для людей, которые хотят расшифровать скрипты. Обычно это делается, если хочется посмотреть код игры и найти переменные, которые могут помочь «обмануть» игру. Чтобы отредактировать эти переменные во время игры, нужно выбрать опцию 3, чтобы включить консоль для ввода кодов во время игры.
3) Запустите UnRen.bat. Выберите опцию 1. После завершения её работы нажмите ещё раз 1, чтобы вернуться в меню. Теперь выберите опцию 2.
Папка fonts. Здесь лежат файлы шрифтов. Какой за что отвечает можно посмотреть в файле gui.rpy:
Часто в своей игре разработчик использует шрифт, который не поддерживает кириллицу. И после перевода в игре «квадратики» вместо букв. Чтобы это исправить, заходим в gui.rpy и смотрим какой шрифт нужно заменить. Находим в интернете понравившийся нам кириллический шрифт. Меняем имя (вместе с расширением) скачанного шрифта на тот, который нужно заменить. Кидаем в папку fonts и перезаписываем.
В images — рендеры, анимации и другие различные картинки
В music или sounds — музыка и звуковые эффекты
В saves — сохранения и persistent-файл. Persistent несёт информацию о persistent-переменных, которые обычно используются для хранения информации об открытых наградах и сценах. Поэтому, когда вам нужно открыть все сцены в игре, вам нужен не сейв, а persistent-файл! 🙂
Структура папок отличается от игры к игре. Часто есть папка phone, если в новелле есть телефон.
.rpy – файлы проекта Ren’Py
.rpyc – зашифрованные файлы скриптов
script.rpy — обычно здесь лежит весь сценарий, но разработчики часто добавляют новые по Эпизодам или Главам, поэтому, кроме script.rpy, вы можете увидеть Chapter2.rpy или Day5.rpy
Файлы .rpy — это те, которые нужно переводить. Файлы .rpyc нужно удалять после каждого перевода, чтобы перезаписать данные. — Только, если переводить «в лоб» ( про этот метод читайте ниже)
Перевод (Translation)
Содержание
Ren’Py включает обширную основу для перевода визуальных новелл.
Существует четыре основных типа вещей, которые можно перевести:
Диалог Можно перевести основной диалог скрипта, включая положение для разделения, объединения, пропуска и изменения порядка строк. Меню и строки интерфейса Можно перевести весь текст интерфейса. Изображения и файлы Сюда можно включить различные изображения и другие файлы, которые используются при выборе языка. Стили Можно настроить стили в зависимости от языка, чтобы игра автоматически переключалась на шрифт, соответствующий выбранному языку.
В настоящее время поддержка перевода Ren’Py сосредоточена на санкционированных переводах, когда создатели игры либо выпускают скрипты игры для переводчика, либо сами создают шаблоны перевода. Поддержка неофициальных переводов более ограничена.
Основные и дополнительные языки (Primary and Alternate Languages) [ править ]
Ren’Py ожидает, что каждая игра будет написана на одном основном языке. Он называется языком None, независимо от того, какой это язык на самом деле. Например, если игра была написана на английском языке, то английский будет языком None.
Если выбран язык None, большая часть функций перевода Ren’Py перевода отключена.
На альтернативные языки ссылаются по именам, которые могут удваиваться как идентификаторы в Python (начинаются с буквы или символа подчеркивания, за которыми следуют буквы, цифры и подчеркивания).
Создание файлов перевода (Generating Translation Files) [ править ]
Файлы перевода находятся в папках под подпапкой «tl» в директории игры. Например, если вы создаёте перевод обучающего проекта, например на французский, файлы перевода будут помещен в tutorial/game/tl/french.
Для каждого файла скрипта игры будет создан один файл перевода. Также будет создан файл common.rpy, содержащий переводы строк, которые являются общими для всех игр, созданных с помощью Ren’Py.
Перевод диалога (Translating Dialogue) [ править ]
Так как Ren’Py является движком для создания визуальных новелл, мы ожидаем, что большая часть перевода затронет диалог. Ren’Py включает гибкую структуру, которая позволяет разделять, объединять, перестраивать и полностью пропускать диалог.
◇ Единицы перевода [ править ]
Основная единица перевода – блок из нуля или более переводимых операторов, за которым может следовать один оператор say. Переводимые операторы – операторы voice и nvl. Для примера возьмём следующую игру:
Код разбит на несколько единиц перевода. Каждой единице присваивается идентификатор, который генерируется на основе метки, предшествующей единице перевода и операторам внутри неё. Если нескольким единицам будет присвоен один и тот же номер перевода, порядковый номер для второй и последующих единиц будет различаться.
В примере, приведенном выше, первой сгенерированной единице присваивается идентификатор start_636ae3f5 и она содержит оператор:
Вторая единица получает идентификатор start_bd1ad9e1m и содержит:
Третья единица имеет идентификатор start_9e949aac и содержит:
Эти единицы перевода создаются Ren’Py автоматически при загрузке игрового скрипта.
◇ Оператор translate [ править ]
При создании переводов для языка, Ren’Py создаст оператор translate, соответствующий каждой единице перевода. При переводе вышеупомянутого кода, Ren’Py создаст:
Это можно перевести, отредактировав сгенерированные файлы. Готовый перевод может выглядеть так:
При обнаружении блока в основном скрипте, Ren’Py проверяет, существует ли оператор translate, соответствующий этому блоку. Если да, то Ren’Py выполняет оператор translate вместо переведенного блока, показывая пользователю перевод.
◇ Более сложные переводы [ править ]
Операторы translate не должны включать переводы, соответствующие языку оригинала один в один. Например, можно разделить длинную строку:
Или оператор можно удалить, заменив его на оператор pass:
Кроме того, можно запускать недиалоговые операторы, такие как условные конструкции или Python. Например, мы можем перевести:
◇ Советы [ править ]
Будьте очень осторожны при изменении переведенного диалога, особенно когда этот диалог повторяется в нескольких местах внутри метки. В некоторых случаях может возникнуть необходимость напрямую присвоить идентификатор перевода, используя оператор, например:
Хотя блоки перевода могут включать код на языке Python, этот код не должен иметь побочных эффектов, видимых за пределами блока. Это связано с тем, что при смене языков перезапускается блок перевода, что приводит к многократному возникновению побочных эффектов.
Переводы меню и строк (Menu and String Translations) [ править ]
В дополнение к диалогу, Ren’Py способен переводить текст, содержащийся в меню и других строках. Перевод интерфейса – взаимно однозначная замена. Везде, где строка найдена, она будет заменена на одну замену.
При создании переводов Ren’Py проверять файлы скриптов на наличие меню и строк, заключенных в функцию _(). Затем Ren’Py помещает строки в блок translate strings. Например, если у нас есть следующий скрипт:
Ren’Py сгенерирует следующий код:
Который затем может быть переведен, как:
Переводы строк также применяются к диалоговым строкам, которые не переводятся как диалог.
Если одна и та же строка используется в нескольких местах кода, её можно выделить с помощью текстового тега <#. >. Несмотря на то, что строки отображаются одинаково, Ren’Py рассматривает все эти отдельные строки для перевода:
Оператор translate strings также можно использовать для перевода языка None. Это может быть использовано для перевода пользовательского интерфейса Ren’Py, когда игра написана на языке, отличном от английского.
◇ Возможность замены перевода [ править ]
◇ Извлечение и объединение переводов строк [ править ]
Перевод строк можно извлечь из одного проекта и переместить в другой. Это многоэтапный процесс:
Есть несколько вариантов, которые управляют процессом объединения:
Заменить существующие переводы Когда пункт выбран, это приведёт к замене уже существующих нетривиальных переводов (те из них, которые не являются пустой или исходной строкой). По умолчанию объединение не будет перезаписывать уже существующие нетривиальные переводы. Обратить языки Меняет строки перед объединением. Это может пригодиться, например, для созданию русско-английского перевода с помощью набора англо-русских переводов.
Переводы изображений и файлов (Image and File Translations) [ править ]
При переводе игры может потребоваться заменить файл оригинала на версию перевода. Например, если изображение содержит текст, возможно, имеет смысл заменить его на версию изображения, где текст переведён на другой язык.
Ren’Py решает эту проблему, путём поиска изображения в папке перевода. Например, если используется французский язык и загружено изображение library.png, Ren’Py будет использовать «game/tl/french/library.png», отдавая ей предпочтение перед «game/library.png».
Переводы стилей (Style Translations) [ править ]
При переводе игры может потребоваться изменить стили, особенно связанные со шрифтами. Ren’Py обрабатывает это с помощью блоков translate style и translate python. Эти блоки могут изменять языковые переменные и стили. Например:
При активации языка (либо в начале игры, или после смены языка), Ren’Py сбрасывает стили с их содержанием в конце фазы инициализации. Затем он запускает все блоки translate style и translate python, связанные с текущим языком, гарантируя при этом, что блоки, фигурирующие в файле ранее, выполняются в первую очередь. Наконец, он восстанавливает стили, позволяя изменениям вступить в силу.
Язык по умолчанию (Default Language) [ править ]
Язык по умолчанию выбирается следующим способом:
Перевод действия, функций и переменных (Translation Actions, Functions, and Variables) [ править ]
Основным способом переключения языков является action Language.
Languagee (language) Изменяет язык игры на указанный в language. language Строка, задающая язык, на который необходимо перевести, либо значение None, чтобы по умолчанию использовать язык скрипта игры.
action Language можно использовать для добавления в настройки возможности выбрать язык. Например:
Есть две функции, связанные с переводом:
renpy.change_language (language, force=False)
Изменяет текущий язык на указанный в language, который может быть строкой или значением None, чтобы использовать язык по умолчанию.
Возвращает набор известных языков. Не предусматривает язык по умолчанию при значении None.
Кроме того, существует несколько функций, связанных с переводом строк::
_ (s) (Одинарное подчёркивание) Возвращает s без изменений. Ren’Py просканирует строки, заключенные в эту функцию, и добавит их в список переводимых строк. Строки не будут переведены, пока они не будут показаны. __ (s) (Двойное подчёркивание) Возвращает s, сразу же переведенную на текущий язык. Строки, заключенные в эту функцию, будут добавлены в список переводимых строк. Обратите внимание, что строка может быть дважды переведена, если она совпадает с переводом строки при его отображении. _p (s) Переформатирует строку и помечает её как переводимую. Строка будет переведена при отображении отображаемого текста. Это сделано в целях определения многострочного использования в строках, а именно:
Переформатирование выполняется путём разбиения текста на строки, удаления пробелов в начале и конце каждой строки. Пустые строки удаляются в конце. Когда есть пустая строка, она вставляется в отдельные абзацы. Тег
разрывает строку, но не добавляет пустую.
Это может быть использовано в переводе строки с использованием конструкции:
Существует две языковые переменные. Одна из них – переменная config.language, которая применяется для изменения языка игры, установленного по умолчанию.
_preferences.language Наименование текущего языка или значение None, если используется язык по умолчанию Её следует рассматривать, как переменную только для чтения. Для изменения языка вызовите renpy.change_language().
Несанкционированные переводы (Unsanctioned Translations) [ править ]
Ren’Py включает небольшую поддержку для создания переводов без активного содействия создателей игры. Эта поддержка заключается в возможности автоматического создания файла перевода строк из всех строк в игре. Поскольку переводы строк используются для непереведенного диалога, данная технология делает возможным перевод игры.
Для создания файла перевода строк, выполните следующие действия:
Это обновит файл «game/tl/language/strings.rpy» с шаблоном перевода, содержащим все строки в нём.
Если игра не поддерживает смену языка, может быть целесообразно использовать блок init python для установки переменной config.language на нужном языке.
Наряду с использованием переводов строк для диалога, несанкционированные переводчики могут быть заинтересованы в использовании описанных выше методов для перевода изображений и стилей.
Как перевести renpy на русский
Многоязычность в Ren’Py: можно ли сделать лучше?
Любой, кто задумывался о переводе новеллы на иностранный язык или с него, знает, что в Ren’Py встроена мощная система интернационализации. Возможности её не ограничиваются простым переводом текста — она позволяет переводить надписи на иллюстрациях, заменять шрифты и даже встраивать в сценарий произвольный код. «Pretty much everything your game needs!» — утверждает Эйлин, и, пожалуй, она недалека от истины. В этом хорошем гайде человек объясняет, как ею пользоваться.
К сожалению, есть в бочке и ложка дёгтя: к началу работы над переводами ваша игра должна быть уже полностью завершена. Если же вы потом захотите что-то поменять в сценарии, вас ждёт много головной боли — тем больше, чем крупнее правки.
Мы, в частности, столкнулись с этой проблемой при создании новеллы «Всё в порядке!». Одновременное написание русской и английской версий грозило стать, мягко говоря, не самым приятным занятием.
Позвольте мне рассказать, в чём дело и к чему всё это в итоге привело.
Возьмём для примера небольшой отрывок. Скажем, вот этот:
label smoothstart:
«На всякий случай проверил часы. Опасения не подтвердились — свою станцию не проехал. Да и прочих не так много успел.» # ⑴
«Я зевнул, да так широко, что прослезился даже. Шли вторые сутки без сна.» # ⑵
«А то и третьи.» # ⑶
jump hard_job
label hard_job:
«Всю ночь я вчера доводил до ума статьи, упиваясь крепчайшим чаем, но всё-таки сделал работу на два дня вперед. Чтобы выкроить их для поездки.» # ⑷
# game/script.rpy:633
translate english smoothstart_0c5ceafe:
# «На всякий случай проверил часы. Опасения не подтвердились — свою станцию не проехал. Да и прочих не так много успел.»
«I checked my watch: no, I hadn’t missed my station yet. Unfortunately, not many others either.»
# game/script.rpy:634
translate english smoothstart_00986b0e:
# «Я зевнул, да так сладко и от души, что прослезился даже. Шли вторые сутки без сна.»
«I yawned so heartily that a tear came out. It was a second day without sleep.»
# game/script.rpy:635
translate english smoothstart_09feead2:
# «Всю ночь я вчера доводил до ума статьи, упиваясь крепчайшим чаем, но всё-таки сделал работу на два дня вперед. Чтобы выкроить их для поездки.»
«The night before I had been finishing my articles, while helping myself with the strongest tea I could brew, and finally managed to do the work for the coming two days. To free them up for this trip.»
# TODO: Translation updated at 2019-07-13 12:34
# game/script.rpy:634
translate english smoothstart_ceb6699e:
# «Я зевнул, да так широко, что прослезился даже. Шли вторые сутки без сна.»
«»
# game/script.rpy:635
translate english smoothstart_451bdca2:
# game/script.rpy:639
translate english hard_job_09feead2:
# «Всю ночь я вчера доводил до ума статьи, упиваясь крепчайшим чаем, но всё-таки сделал работу на два дня вперед. Чтобы выкроить их для поездки.»
«»
А значит, нужно создать новый.
Уже через две недели после постановки задачи был готов рабочий прототип. Ещё две ушли на совершенствование интерфейса и отлов хитрых багов. Сейчас мы считаем программу достаточно стабильной и выкладываем на всеобщее обозрение. Скачать 7z-архив можно с Гитхаба; запускать следует bin/renpy-update-tl-gui.exe. А выглядит это так:
Давайте взглянем, как он справится с нашим примером:
# game/script.rpy:633
translate english smoothstart_0c5ceafe:
# «На всякий случай проверил часы. Опасения не подтвердились — свою станцию не проехал. Да и прочих не так много успел.»
«I checked my watch: no, I hadn’t missed my station yet. Unfortunately, not many others either.»
# game/script.rpy:634
translate english smoothstart_ceb6699e:
# «Я зевнул, да так широко, что прослезился даже. Шли вторые сутки без сна.»
# «Я зевнул, да так сладко и от души, что прослезился даже. Шли вторые сутки без сна.» # TODO: OUTDATED; delete this line when no longer needed.
«I yawned so heartily that a tear came out. It was a second day without sleep.»
# game/script.rpy:635
# TODO: NEW.
translate english smoothstart_451bdca2:
# game/script.rpy:639
translate english hard_job_09feead2:
# «Всю ночь я вчера доводил до ума статьи, упиваясь крепчайшим чаем, но всё-таки сделал работу на два дня вперед. Чтобы выкроить их для поездки.»
«The night before I had been finishing my articles, while helping myself with the strongest tea I could brew, and finally managed to do the work for the coming two days. To free them up for this trip.»
Спасибо за внимание.
P. S. Уже после релиза мы обнаружили Ren’Py Translator Toolkit. Он добивается той же цели иными средствами: конвертирует .rpy в .po и обратно. Так что если вы собираетесь переводить свою новеллу, советую обратить внимание и на этот проект тоже. У каждого подхода есть свои сильные и слабые стороны; попробуйте оба и решите, какой вам удобнее.
Как перевести renpy на русский
Script for automatic translation Renpy games.
Latest commit
Git stats
Files
Failed to load latest commit information.
README.md
Скрипт для автоматического. ну ладно, полуавтоматического перевода renpy игр через гуглтранслейт. 🙂 Работает под Windows на python 3.7, на других версиях и ОС не проверял. Для работы требуется textblob https://textblob.readthedocs.org
Для начала нужно получить файлы для перевода. Для этого окне renpy жмем на «Создать переводы». Вводим имя папки на латинице, например rus. В папке game движок создаст папку tl с нужными файлами.
Добавляем в главное меню выбор языка игры. Для этого в файл screens.rpy вставляем эти строки.
Если на предыдущем шаге папку для переводов вы назвали не rus, а как-то po_drugomu, то измените Language(«rus») на Language(«po_drugomu») Для примера можно посмотреть файл screens.rpy из обучающей игры, если сомневаетесь куда именно вставить этот код.
Создаем временную папку, например D:\tmp (русских букв в пути быть не должно) Копируем туда скрипт renpy_translator.py и папку tl. Запускаем командную строку, переходим во временную папку и запускаем скрипт. python3 renpy_translator.py
Скрипт создаст папку transl. В папке transl будут лежать переведенные файлы,
Копируем файлы из папки transl в папку game/tl Запускаем проект. Смотрим на лист ошибок 🙂 Скорее всего они будут, особенно, если игра большая. Гуглтранслейт иногда переносит точку за кавычки «. Или добавляет пробелы в экранированные \ » кавычки \ «» Всё предусмотреть у меня не получилось, и такие ошибки надо исправлять руками.
Да, игра должна быть разархивирована. Если игра в архиве, то инструкции по распаковке можно найти тут https://github.com/Shizmob/rpatool Если шрифт, указанный в игре не содержит кирилицу, и вместо русского текста у вас квадраты то в файле options.rpy замените шрифты на кирилические. Например constan.ttf
Поддерживается перевод на любые языки, которые поддерживает гуглтранслейт. Для этого в коде скрипта надо заменить (to=’ru’) на нужную пару языков. Инструкции тут: https://textblob.readthedocs.org/en/dev/quickstart.html#translation-and-language-detection
UPDATE 05.04.2019 При создании файлов перевода в Renpy, убедитесь, что опция Generate empty strings for translations НЕ выбрана. Ощибок стало меньше, но до идеала далеко.
После перевода нужно проверить результат. Вот такие вещи надо исправить
Полезные регулярки для правки переведенного текста:
Пишем текстовую игру на Python/Ren’Py
Как сделать текстовую игру? Да как угодно. Как сделать кроссплатформенную текстовую игру на русском с иллюстрациями, звуком, работающими сохранениями, без проблем с кириллицей, и с каким-никаким геймплеем? Да ещё и в свободное время, не отрываясь от основной работы? Вот это уже интересней и на самом деле — довольно несложно. Заинтересовавшихся прошу под кат.
Примерно год назад мы с товарищем задумали сделать небольшую текстовую игру приблизительно в духе Sunless Sea и 80 days: про мореплавание, торговлю, исследование странных поселений и общение со странными личностями. Там должна была фигурировать религия, а лучше несколько, главного героя хотелось видеть не спасителем, героем страны и прославленным мореходом, а умеренно неудачливым предпринимателем/авантюристом, до которого и дела никому нет, а модный выбор между меньшим и большим злом заменить на выбор между добром и добром: никакого набившего оскомину гримдарка ради гримдарка. Довольно быстро придумались основные фракции и персонажи, крупные порты, политическая обстановка и куча симпатичных мелочей вроде подводной охоты на осьминогов (изображена на КДПВ) и гениальной идеи дать почти всем персонажам венгерские имена, которые звучат экзотичней привычных европейских и вызывают некоторую неявную симпатию. В общем, деревянных домиков понабигало немало.
В команде у нас на тот момент был один писатель и один программист (то есть я). Требования в предыдущем абзаце относятся скорее к сетингу и духу игры, так что исполнять их должен был мой товарищ, а передо мной встали вопросы геймдизайна и функциональности движка. Во-первых, большую часть времени игрок будет тратить, читая текст и выбирая действия главного героя. Для этого нужна только сносная типографика и возможность писать сценарий с меню, опциями и переменными. Вскоре подключилась художница, так что надо было думать ещё и об иллюстрациях. Во-вторых, игра про исследования и торговлю, поэтому нужно где-то в доступном игроку виде хранить информацию о собранных слухах и купленных товарах (а также всячески её обрабатывать). И, наконец, в игре про мореходство нужна карта и возможность по ней перемещаться; просто команда “поплыть к тартарам и послушать сказки морских лошадей” явно не соответствует духу проекта. Значит, движок должен ещё и поддерживать хотя бы несложные мини-игры, а не ограничиваться только показом текста и обсчётом игровых переменных.
Почему Ren’Py
Сразу скажу, что писать движок с нуля мы даже не пытались: велосипедостроение увлекательно само по себе, но малоэффективно, если стоит цель всё-таки выпустить игру до выхода на пенсию. Также мы не рассматривали парсерную Interactive Fiction: у неё и на английском языке очень небольшая аудитория, а на русском наш проект, будь он парсерным, мог бы заинтересовать в лучшем случае несколько сот человек. А хочется если не заработать денег, то хотя бы пройти гринлайт и набрать какую-никакую репутацию. К счастью, большинство нынешних англоязычных разработчиков текстовых игр перешло от некоммерческих хобби-проектов к профессиональному геймдеву буквально несколько лет назад. Поэтому основные движки либо опенсорсны, либо, во всяком случае, бесплатны. Давайте посмотрим, что нам предлагают.
Первый вариант, пришедший мне в голову – Storynexus от Failbetter games, разработчиков Fallen London и Sunless Sea. Проекты на нём редактируются через браузер, хостятся Failbetter и через браузер же доступны игрокам. Возможности для монетизации с прошлого года удалили. Главный минус, однако, не в этом, а в том, что в Fallen London большая часть событий представлена картами, выпадающими из колоды, и сделать на Storynexus игру, не использующую эту метафору – задача нетривиальная. Да и вообще намертво привязывать свой проект к стороннему серверу с закрытым кодом, который теоретически может вообще прекратить работу в любой момент, довольно рискованно.
Есть ещё два хороших проприетарных движка для Choose Your Own Adventure, то есть игр примерно нашего типа: ChoiceScript и Inklewriter. Оба обещают прекрасную типографику, простоту разработки (браузерный редактор у Inklewriter, скриптовый язык у ChoiceScript) и возможность коммерческой публикации. К сожалению, оба позволяют делать только чистое CYOA: нет никакой возможности добавлять в игру что-то помимо собственно текста, меню и иллюстрациий. Внимательный читатель воскликнет: “Но как же так? В 80 days ведь был довольно сложный инвентарь и интерфейс путешествий, верно? А в Sorcery! я точно видел боёвку!” Увы, эти системы разрабатывались Inkle Studios под конкретные игры и в редакторе нет ни их, ни хоть какой-нибудь возможности сделать себе такие же. По той же причине (а также потому что он, эм, своеобразный) мы отказались от Twine.
Единственным устраивающим нас вариантом оказался Ren’Py. Это бесплатный опенсорсный движок для визуальных новелл (например, именно на нём сделаны “Бесконечное лето” и “Katawa shoujo”), который довольно легко настраивается для наших задач. Игры получаются кроссплатформенные: сборка дистрибутива под Win/Mac/Linux – вопрос нажатия одной кнопки, причём даже не надо иметь под рукой целевую ОС. Android и iOS также заявлены и Ren’Py-релизы под мобильные оси существуют, но мы сами пока на мобильный рынок не целимся и о разработке для него рассказать не можем. К тому же у Ren’Py очень дружелюбное и живое сообщество на русском и английском.
Простейший сценарий на Ren’Py
Ren’Py написан на Python 2.7 + Pygame и имеет собственный DSL. На этом языке, во-первых, за счёт команд типа “Показать bg_city_night_53.png в качестве фона без анимации” или “Произнести реплику «Cем… СЕМПАЙ. » от имени персонажа nyasha1” в императивном стиле пишется собственно сценарий. Во-вторых, подмножеством этого языка является Screen Language, на котором можно в декларативном стиле собирать из ограниченного набора Displayables (то есть виджетов: кнопок, изображений, текстовых полей и тому подобного) экраны и настраивать их функциональность. Если встроенных возможностей недостаточно, то с помощью Python можно добавлять собственные. Этим мы займёмся в следующей статье, а пока разберёмся со сценарием.
Сценарий в Ren’Py состоит из последовательности реплик, действий с экранами и ввода игрока. Про экраны и ввод чуть ниже, а для начала мы разберёмся с персонажами. В визуальной новелле они создаются так (код из официального туториала, с незначительными правками):
Создано два персонажа: протагонист и Сильви, оба пишут бледно-синим цветом в стандартное окошко внизу экрана. У Сильви к тому же есть портрет, который появится на экране перед тем, как она начнёт говорить. Выглядит это вот так:
Если бы мы создавали визуальную новеллу, то продолжали бы в том же духе, но мы-то не собираемся показывать портреты персонажей, да и иллюстраций пара десятков на всю игру. Большая часть текста вдобавок не является прямой речью персонажей, так что нелогично было бы привязывать её к кому-то из них. Лучше создадим виртуального персонажа-рассказчика:
Его зовут narrator; это специальное имя, которое отдаёт ему весь текст, явно не аттрибутированный другим персонажам (строго говоря, его зовут None, а narrator, как и m и s в предыдущем примере – переменная, в которую помещается объект персонажа и из которой вызываются его методы, например, say) Аргумент kind принимает два значения: adv и nvl. Первое – это дефолтное поведение, описанное выше, а второе включает nvl-режим, в котором портреты не показываются, а текстовое поле занимает большую часть экрана. Как раз то, что нам было нужно. Этот режим описывается экраном nvl_screen в файле screens.rpy и группой стилей styles.nvl* (файлы screens.rpy и options.rpy соответственно), в которых мы зададим шрифт, фон текстового поля, цвет меню и всё остальное.
Разберём построчно: сперва объявляется ярлык start, с которого начнётся игра. Это название зарезервировано и движок всегда будет переходить на него после нажатия кнопки “Новая игра”, где бы в сценарии он ни находился. Всё, что следует за ярлыком, логически находится “внутри” этого ярлыка, поэтому выделяется индентацией: она в Ren’Py работает так же, как и в чистом питоне. Инициализация картинки достаточно очевидна, а вот следующая строчка делает важную вещь: убирает весь текст с экрана nvl_screen. Автоматически это не делается, поэтому, если не расставлять nvl clear в конце каждой страницы, текст спокойно уползёт за пределы экрана и будет выводиться туда, пока экран не будет наконец очищен. Вроде бы мелочь, но на отладку пропущенных nvl clear я потратил намного больше времени, чем готов признать. Свежевымытый экран мы временно уберём, чтобы позволить игроку полюбоваться фоном, покажем фон, включим бесконечную паузу (то есть дождёмся клика) и начнём историю. Как только на nvl_screen начнёт выводиться текст, экран сам вернётся на место.
Строка с паузой, кстати, уже на питоне: для включения единичной строки её достаточно начать с ‘$’, а более длинные куски кода нужно писать внутри блока ‘python:’. Любой код, исполняемый игрой, видит модули самого Ren’Py и явно импортировать их уже не нужно.
Добавляем ветвление и переменные
К этому моменту игра представляет собой читалку, которая показывает текст, меняя при необходимости фоны. Сохранение, перемотка, главное меню и настройки уже работают из коробки. Однако если бы мы хотели написать иллюстрированную повесть, то мы бы её и написали, верно? Добавим перед текстом небольшое меню:
Теперь после включения игры пользователь (или, скорее, разработчик) сможет при желании войти в режим дебага или пропустить уже готовый кусок вступления и начать тестировать сразу кусок из последнего коммита. Строка show screen nvl закомменчена за ненадобностью – как я уже упоминал выше, экран покажется сам собой, когда на нём обновится текст. Комменты, как видите, работают абсолютно очевидным образом.
Ярлыки, меню и другие индентированные блоки могут быть вложены до произвольной глубины, но на практике мы стараемся дробить текст на эпизоды в десяток страниц. Каждый такой эпизод описан внутри отдельного ярлыка с нулевой индентацией (он уже не обязан быть внутри ярлыка start или даже в одном с ним файле), а переходы из одного эпизода в другой осуществляются прыжками. Так мы не только боремся с десятками уровней индентации, но и обеспечиваем модульность кода: каждый эпизод может тестироваться отдельно и довольно несложно проверить, какие переменные он читает, в какие пишет и куда позволяет перейти.
Внутриигровые меню и переменные устроены абсолютно так же. Поскольку и переменных, и ярлыков даже в небольшом эпизоде на десять минут игры разводится невероятное количество, мы приняли несложный вариант венгерской нотации: имя ярлыка ‘the_very_start_lazlo_nooptions’ состоит из трёх частей: названия локации the_very_start (то есть период от начала игры до первого выхода в море), названия эпизода lazlo (то есть пьянка у Лазло, на которой можно нанять молодых бездельников в матросы) и имени собственно ярлыка. При таком подходе имена получаются достаточно громоздкими, но лучше так, чем обнаружить при тестировании, что три месяца назад кто-то уже создал переменную ship_listing, выставил True бог весть где и теперь крен из одного случайного события влияет на исход другого случайного события на другом конце моря.
Вместо заключения
К этому моменту мы уже воспроизвели на Ren’Py функционал упоминавшихся выше Choicescript и inklewriter. Вроде бы наш кораблик готов к отплытию. В следующей статье я покажу, как можно создавать более сложный интерфейс с использованием экранного языка RenPy и ещё более сложный — на чистом питоне.







