Как написать визуальную новеллу renpy

Руководство для начинающих

Добро пожаловать в Руководство для начинающих по использованию движка визуальных новелл Ren’Py. Здесь мы, на примере создания с нуля игры Знакомство с Визуальными Новеллами, рассмотрим процесс написания простой визуальной новеллы.

Начало работы

Запустим центр управления Ren’Py. Для этого нужно запустить renpy.exe (renpy.sh для пользователей Линукса) из папки, в которую распакован архив с ним. В левом верхнем углу белым будет написано название активного проекта. Справа — ряд кнопок, поделённый на две секции:

секцию «Текущий проект» («This project» в английской версии — Команды для текущего проекта), состоящую из команд

«Запуск» («Launch» — запустить проект на исполнение),
«Править скрипт» («Edit Script» — Редактировать код проекта),
«Сменить тему» («Change Theme» — Сменить цветовую схему оформления проекта),
«Папка игры» («Game Directory» — Открыть папку текущего проекта (всё, относящееся к проекту — код, ресурсы — должно лежать в ней))
«Инструменты» («Tools» — Вспомогательные средства);
секцию «Сменить проект», состоящую из команд

«Выбрать проект» («Change Project» — Смена активного проекта, позволяет переключаться между созданными проектами. В поставку Ren’Py изначально входят проекты demo (демонстрация возможностей движка) и the_question (простейшая законченная визуальная новелла)) и
«Новый проект» («New Project» — Создание нового проекта).

Мы хотим создать новую игру, потому выберем «Новый проект». ЦУ попросит выбрать шаблон проекта — выбираем template за неимением других опций. Затем ЦУ попросит ввести название проекта. Вводим. Следом ЦУ попросит выбрать цветовую схему оформления проекта. На вкус и цвет. В результате вернёмся в главное меню ЦУ с только что созданным проектом в качестве активного (обратите внимание на левый верхний угол). Можно запустить его кнопкой «Запуск», чтобы полюбоваться на интерфейс. Но лучше приступить к собственно написанию игры.

Где, что и как писать

Приступим к собственно написанию игры, для чего выберем «Править скрипт». В результате файлы с кодом проекта откроются для редактирования во входящем в поставку Ren’Py редакторе SciTE. Редактор многовкладочный, поэтому трём имеющимся изначально (от шаблона) файлам «script.rpy», «options.rpy» и «localize.rpy» будут соответствовать три одноимённые вкладки. Нам в данный момент нужна та, что озаглавлена «script.rpy».

RenPy_1.JPG

RenPy_1.JPG

Мы увидим следующее: Самая первая строчка — это комментарий. Комментарием является любое сочетание символов, предварённое символом # и завершающееся с концом строчки. Комментарии не воспринимаются обработчиком и никак не отражаются в игре. Используются они для оставления каких-то пометок-пояснений себе. Или для временного исключения из обработки какой-либо строки.

Начиная со строчки 3 идёт блок init.

Отступы и блоки — основа основ

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

init:
    # Эта строка — в блоке init
    # Так же, как и эта.
    python:
        # Эта строка и в блоке python, и в блоке init.
        # То есть, в подобной ситуации невозможно быть в блоке python
        #  но не быть в блоке init.
    # эта строка всё еще в блоке init, но уже не в блоке python.
    #  так что можно сказать, что блок python «закрыт», т.к. туда больше не может попасть
    #  ни одна строка. Но мы можем продолжать добавлять строки в блок init.
# Эта строка НЕ в блоке init!

Зачем нужен блок init?

В любом проекте есть информация, такая как используемые изображения и персонажи, которая должна быть определенна до собственно начала истории. Такая информация объявляется в блоке init. Он может находиться в коде где угодно, но обычно его помещают в самое начало. Этот блок начинается со строчки:

init:

записанной без отступа, и все строчки с отступом, следующие за ней, принадлежат блоку init. Строчек этих может быть любое количество. Заканчивается блок первой же строкой БЕЗ отступа. И эта строка, конечно же, блоку init уже не принадлежит.

Метки.

Дальше обратим внимание на строчку 12:

label start:

Это так называемая метка. Метки позволяют давать названия нужным местам текста, дабы потом можно было переместиться к ним откуда угодно (но об этом позже). Меткой является строчка без отступа, начинающаяся с ключевого слова label и заканчивающаяся двоеточием. Слово, следующее за label — название метки. Название метки не должно содержать пробелов! (Но, как известно, настоящие_программисты_пробелом_не_пользуются ;-)). Также, название метки должно быть уникальным в рамках всего проекта! Существует специальная метка, необходимая в любом коде Ren’Py: label start: . Она обозначает место начала текста игры.

Поехали!

Можно приступать к работе. Для начала удалим блок init и всё в блоке label start, чтобы не мешалось. Теперь нам нужно заставить Ren’Py сказать первую реплику. Для этого в блоке label start напишем просто:

   "Интернациональный Колледж Цифровых Искусств."

(Не забываем про отступ!) Подобная конструкция называется «высказывание» (say statement). Ren’Py автоматически осуществляет перевод строки, если выведенное высказыванием в текстовое окно сообщение в одну строчку не вмещается. Но если есть нужда перейти на новую строку в каком-то определённом месте, то в этом месте нужно поставить сочетание символов n. Вот так:

   "Мне повезло, что я учусь здесь. nОсобенно, что на игровом направлении."

Каждое высказывание обновляет содержимое текстового окна.

Если в тексте высказывания необходимо использовать двойные кавычки » их необходимо предварить символом . Вот так:

  "Сейчас по расписанию "Визуальные новеллы". Новый курс. Интересно, о чём же нам там поведают?"

Фоны.

Если сейчас сохранить изменения, запустить проект и выбрать «Начать игру», увидим этот текст в текстовом окне. На фоне чёрного экрана. Не особо хорошо, не правда ли? Что ж, добавим фоновый рисунок. Но для этого изображение, что послужит фоном, сначала нужно объявить в блоке init. Получим в итоге следующий скрипт:

init:
    image bg uni = "uni.jpg"
    
label start:
    "Интернациональный Колледж Цифровых Искусств."
    "Мне повезло, что я учусь здесь. nОсобенно, что на игровом направлении."
    "Сейчас по расписанию "Визуальные новеллы". Новый курс. Интересно, о чём же нам там поведают?"

Объявление изображения — вторая строчка. Сначала идёт ключевое слово image, затем псевдоним (внутреннее имя изображения), затем знак равно, затем имя файла с изображением, внутри двойных кавычек . Предполагается, что файл с изображением лежит в рабочей папке проекта (доступна по выбору «Game Directory» в ЦУ). Можно также завести отдельную папку для изображений, в рабочей папке проекта, но тогда имя этой папки нужно будет дописать перед именем файла. Например, если изображения лежат в подпапке Images рабочей папки проекта, объявление изображения будет вида:

   image bg uni = "Images/uni.jpg"

Фоновые изображения должны быть того же размера, что и выбранное для игры разрешение. По умолчанию — 800х600 пикселей, наверное, самое удобное. Формат файла для фоновых изображений — JPEG или PNG. Теперь выведем это изображение в виде фона перед тем, как выводить текст. Для этого на следующей после label start строчке напишем (сдвинув высказывания на строчку вниз):

   scene bg uni

Сначала идёт ключевое слово scene, затем псевдоним изображения, которое нужно использовать как фон. Смена фона на другой также производится этой командой. При применении команды в таком виде предыдущее фоновое изображение вместе со всеми прочими визуальными элементами немедленно заменяется на указанное в команде. Однако процесс перехода можно сопроводить эффектами. В Ren’Py есть ряд предопределенных эффектов, например fade («упрозрачнивает» старое изображение в чёрный фон за полсекунды, затем «упрозрачнивает» за полсекунды чёрный фон в новое изображение), dissolve («растворяет» старое изображение в новое за полсекунды) и pixellate (пикселизует за полсекунды старое изображение, затем за полсекунды распикселизует новое). Для применения эффекта надо только приписать к нужной команде вывода изображения ключевое слово with и название нужного эффекта. Проще это увидеть самому. Потому выведем этот фон с эффектом dissolve. А после высказываний сменим фон на другой с эффектом fade. Надо только не забыть объявить изображение в блоке init. Получим такой скрипт:

init:
    image bg uni = "Images/uni.jpg"
    image bg lecturehall = "Images/class.jpg"

label start:
    scene bg uni with dissolve 
    "Интернациональный Колледж Цифровых Искусств."
    "Мне повезло, что я учусь здесь. nОсобенно, что на игровом направлении." 
    "Сегодня начинается курс "Визуальные новеллы". Интересно, о чём же нам там поведают?".
    scene bg lecturehall with fade

Украшаем текст.

Дальше хотелось бы, чтобы следующая реплика — мысленная речь героя — как-то выделялась. Например, вывелась курсивом. Нет ничего проще! Для подобной операции Ren’Py использует тэги, наподобие html, только с фигурными скобками вместо угловых. Так, текст, заключенный между тэгами {i} и {/i} будет курсивным, между {b} и {/b} — полужирным, а между {u} и {/u} — подчёркнутым. Также, тэги {size=<число>} … {/size} управляют размером заключённого в них текста (если <число> = число без знака, то заключённый в данные тэги текст будет размером в указанное число пикселей; если же <число> = число, предварённое знаком «+» или «-», то движок воспримет это как команду к увеличению или уменьшению размера шрифта для заключённого в теги текста на указанное число пикселей относительно размера по умолчанию). Наконец, текст, заключённый в теги {color=#rrggbb} и {/color}, будет изображён указанным цветом. #rrggbb — строка-идентификатор цвета формата RGB в виде стандартного шестнадцатеричного триплета. rr, gg и bb — соответственно интенсивности красной, зелёной и синей составляющей цвета. Могут принимать любое значение от 00 (нет этой составляющей цвета) до ff (255, максимум интенсивности). Так, #000000 = чёрный, #ffffff = белый, #ff0000 = ярко-красный, #0000ff = ярко-синий, а #аааа00 = ярко-жёлтый. Подобное представление цвета широко используется в Ren’Py, с ним нам ещё предстоит столкнуться. Важно: закрываются теги в порядке, обратном порядку открытия! Ладно, нам нужен был курсив. Потому пишем:

   "{i}Преподаватель опаздывает.{/i}"

Кто сказал «Гав»?

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

   "???" "Здравствуйте! Простите за эту задержку."
   "???" "Я -- профессор Наталь, и я буду вести у вас курс "Визуальные Новеллы"."

Эти реплики выведутся со строкой «???» вверху текстового окна. Важно: если в первой строке (т.е. между первыми и вторыми двойными кавычками) используются русские буквы, то её необходимо предварить английской буквой ю («u»)! Так, если нужно вывести реплику главного героя — от первого лица — то высказывание будет выглядеть так:

   u"Я" "Реплика!"

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

Так, теперь, когда нам известно имя преподавателя, указывать его как «???» нельзя, а каждый раз набирать «Профессор Наталь» неудобно. Здесь на помощь приходят такие объекты Ren’Py, как «персонажи» (character object). Персонажи позволяют использовать в именных высказываниях вместо длинных имён короткие псевдонимы. Но сначала персонаж нужно объявить в блоке init. Делается это следующей строчкой:

   $ eileen = Character(u'Эйлин')

Сначала идёт знак доллара, затем псевдоним персонажа, затем знак равно, ключевое слово Character и аргументы в скобках. В данном случае — имя персонажа (в кавычках), которое будет отображаться во всех его репликах. (Обратите внимание, поскольку имя персонажа дано кириллицей, его необходимо предварить символом u) Для облегчения написания кода псевдоним персонажа лучше делать как можно короче. Чаще всего имена персонажей выделяют цветом. Для этого нужно добавить в объявление персонажа аргумент color:

   $ e = Character(u'Эйлин', color="#c8ffc8")

Также можно заставить все реплики этого персонажа выделяться определённым цветом. Для этого нужно добавить в объявление персонажа аргумент what_color:

   $ e = Character(u'Эйлин', color="#c8ffc8", what_color="#c8ffc8")

Можно также автоматически применять теги ко всему тексту всех реплик персонажа. Для этого нужно добавить в объявление персонажа аргументы what_prefix со строкой открываемых тегов и what_suffix со строкой закрывающих тегов. Так, чтобы выделить всё, что скажет Эйлин жирным и курсивом, нужно записать:

   $ e = Character(u'Эйлин', color="#c8ffc8", what_prefix = "{b}{i}", what_suffix = "{/i}{/b}")

Правда, лучше было бы это реализовать с помощью аргументов стиля:

   $ e = Character(u'Эйлин', color="#c8ffc8", what_bold = True, what_italic = True)

Вообще, строка-значение аргумента what_prefix перед обработкой реплики приписывается движком к началу текста реплики, а строка-значение аргумента what_suffix — к концу. Персонажи ещё много чего могут, но мы сейчас ограничимся наиболее часто используемым минимумом:

   $ p = Character(u'Профессор Наталь', color="#ff6666")

Теперь все высказывания вида

    p "Прежде всего, что такое "визуальная новелла"?"

будут выводиться от имени профессора Наталь.

Портретное сходство.

Всё это хорошо, но неплохо было бы видеть профессора на экране, когда она что-либо говорит. Здесь в игру вступают изображения персонажей. Размером они обычно <~ четверть-треть ширины игрового окна>х<полная высота игрового окна> (т.е. если для игры выбрано разрешение 800х600, то изображение персонажа будет размером от 200х600 до 266х600). Также, вся область изображения, не занятая собственно рисунком персонажа, должна быть прозрачной! Выбранный формат файла для изображений персонажей — PNG. Объявляются изображения персонажей так же, как и фоновые изображения:

   image prof norm = "prof_norm.png"
   image prof smile = "prof_smile.png"

В подавляющем большинстве случаев для одного и того же персонажа нужно несколько изображений, с разными выражениями лица или в разной одежде. В данном случае применяются два изображения: с обычным выражением лица и с улыбкой. Можно заметить, что псевдонимы, использованные для этих изображений, совпадают первым словом. Это так называемый ярлык изображения (image tag). С его помощью удобно группировать изображения, а также скрывать любое из группы, независимо от того, какое именно сейчас на экране. Сообществом Ren’Py принято фоновые изображения отмечать ярлыком bg. Показывается изображение персонажа в игровом коде строчкой:

   show prof norm

Сначала ключевое слово show, затем псевдоним изображения. Введённая в таком виде команда выведет изображение по центру игрового окна по горизонтали, нижний край изображения совпадёт с нижним краем окна. В Ren’Py есть предопределённые позиции для изображений: справа (at right) — правый край изображения совпадает с правым краем экрана; слева (at left) — левый край изображения совпадает с левым краем экрана; по центру (at center) — изображение отцентрировано горизонтально; за пределами экрана справа (игроку не видно) — at offscreenright; за пределами экрана слева (игроку не видно) — at offscreenleft. Во всех случаях нижний край изображения совпадает с нижним краем игрового окна. Чтобы показать изображение в нужной позиции, необходимо приписать название этой позиции к команде, выводящей нужное изображение, после псевдонима. Лучше всегда указывать позицию для вывода. Итак, чтобы вывести изображение профессора по центру, нужно написать:

   show prof norm at center

Также, к изображениям персонажей тоже применимы эффекты, таким же образом, как и к фонам. То есть, приписыванием ключевого слова with и названия эффекта в конец команды вывода изображения (после указания позиции). Помимо эффектов появления можно показать изображение с эффектом выезда за полсекунды из-за края экрана: moveinright, moveinleft, moveintop, moveinbottom. Соответственно, справа, слева, сверху и снизу. Также есть эффект увеличения за полсекунды из точки: zoomin. Так, чтобы профессора по центру с выездом справа, нужно написать:

   show prof norm at center with moveinright

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

   scene bg uni with fade
   show prof norm with moveinleft
   show prof smile with dissolve

сначала сменится фон с эффектом fade, затем слева въедет изображение профессора, а потом оно растворится в свою улыбающуюся версию. Можно сказать, они сформировали очередь, и пока одно изображение с эффектом не выведется, следующее за ним не начнёт показываться. Если, конечно, игрок не нажмёт на какую-нибудь кнопку, что прерывает все эффекты и заставляет движок сразу отобразить конечный результат. Если же нужно отобразить всю сцену, состоящую, скажем, из фона bg, изображения персонажа ааа слева и изображения персонажа bbb справа, с применением ко всей сцене сразу эффекта dissolve, то следует после команд на вывод изображений без эффектов написать строчку with dissolve:

   scene bg 
   show aaa at left
   show bbb at right
   with dissolve

По историческим причинам это не одно и то же, что и:

   scene bg 
   show aaa at left
   show bbb at right with dissolve

Данный скрипт выведет фон и первое изображение персонажа моментально, затем второе изображение персонажа с эффектом dissolve.

Ren’Py следит за тем, что выведено, и при выводе нового изображения с тем же ярлыком, что и у уже находящегося на экране, старое изображение автоматически скрывается. Однако Ren’Py не следит за позициями, и поэтому при попытке вывести новое изображение в позицию, уже занятую изображением с другим ярлыком, новое изображение наложится на старое. Во избежание подобных эксцессов ненужные изображения следует скрывать. Делается это командой hide с ярлыком изображения, которое нужно скрыть, или же его полным псевдонимом. Так, чтобы немедленно скрыть любое изображение профессора, нужно использовать строчку:

   hide prof

В команде hide также может быть использован эффект, любой кроме zoomin и группы moveinчто-то. Зато есть их эквиваленты: moveoutright, moveoutleft, moveouttop, moveoutbottom — убирающие изображение за полсекунды с экрана вправо, влево, вверх или вниз соответственно; и zoomout, уменьшающий за полсекунды целевое изображение в точку.

Выбери свою судьбу!

Ладно, пока это может подождать. Пойдём дальше. Профессор объясняет, что такое визуальная новелла. Вот, она останавливается, словно ожидая вопросов. Самое время игроку повлиять на ход новеллы. Но как? Самый часто используемый метод — меню. Перед игроком возникают несколько вариантов реакции (например, варианты реплики в разговоре, или варианты действия в игровой ситуации), игрок выбирает один, и от этого выбора зависит дальнейшее течение повествования. В Ren’Py меню реализуются так: Сначала идёт строчка

   menu:

открывающая блок, т.е. все последующие строчки до конца меню идут с отступом на четыре пробела больше. Дальше может быть строчка-сообщение, которое будет находиться в текстовом окне, когда меню на экране. Это простое высказывание. Затем идут варианты меню — строки, заканчивающиеся двоеточием. Каждый вариант открывает свой блок, в котором записываются результаты выбора этого варианта. Например:

    menu:
        "Хорошая возможность задать вопрос."                         # Фраза, которая будет в текстовом окне
        "А как это выглядит с точки зрения игрока?":                 # Пункт №1 меню.
            "Я поднял руку, показывая, что у меня есть вопрос."      # Результаты
            <…>            
        "Ничего не спрашивать":                                      # Пункт №2 меню.
            pass                              # Результаты

Если реакции для какого-то пункта меню вообще не предусматривается, то есть данный пункт ничего не делает, в его блоке следует написать pass, как это сделано в примере с пунктом №2. Варианты могут быть любой длины, и их может быть любое число, главное, чтобы они все поместились на экране. Результаты выбора пункта меню также могут содержать любое число строк и состоять из любых команд, в том числе и из других меню.

А я помню…

Довольно часто возникает необходимость запоминать, какой выбор сделал игрок, дабы припомнить это ему позднее. Как в данном случае, если игрок задал вопрос о том, как визуальная новелла выглядит с точки зрения игрока, и профессор ему это объяснила, то когда она будет говорить про реализацию визуальных новелл, неплохо было бы, чтобы она заметила, что повторяется. Это можно сделать с помощью переменных. Надо завести логическую переменную (подобная переменная может принимать два состояния — истинно и ложно) и придать ей значение истинно, если вопрос задан (т.е. в результатах выбора варианта меню с вопросом) и ложно, если вопрос не задан. Ren’Py не требует предварительного объявления переменных, то есть их можно вводить просто по ходу скрипта. Но для удобства перед началом работы всем используемым переменным стоит присвоить начальные значения (скажем, ноль для цифровых, ложно для логических, хотя это сильно зависит от того, как эти переменные будут использоваться в дальнейшем). Это можно сделать сразу после метки старта, или вообще в блоке init (но лучше — после метки старта). Присваивается переменной значение так:

   $ question_asked = False

Сначала знак доллара и пробел, затем имя переменной (одно слово, т.е. без пробелов) затем символ присвоения — знак равно, затем присваиваемое значение (в данном случае, False — «ложно»; «истинно» будет True). C цифровыми и строковыми переменными обращаются так же:

   $ string_var = u"Это строка."
   $ ppoints = 0

С помощью цифровых переменных организуются концовки, базирующиеся на очках. Скажем, если игрок к исходу новеллы набрал менее половины из возможных очков, его ждёт плохая концовка, если более половины, но не все, то хорошая, а если все возможные, то наилучшая. Или, концовка зависит от того, какая из нескольких переменных имеет наибольшее значение (Скажем, в переменных хранятся значения атрибутов — силы, ума и удачи. Если наибольшее значение имеет атрибут силы, то игрок стал чемпионом мира по тяжёлой атлетике. Если ум, то игрок получил Нобелевскую премию по всем наукам. Если же удача, то игрок выиграл в лотерею миллиард рублей. И ещё какие-нибудь концовки в случае ничьих).

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

   $ ppoints += 1

Знаки плюс-равно означают «прибавить к текущему значению данной переменной число, стоящее справа». Это может быть любое число, не только единица. Если нам нужно было бы уменьшить значение переменной, нужно было бы использовать вместо += знаки -= (минус-равно). А вот если… Позднее, когда профессор говорит о составляющих визуальной новеллы и повторяет сказанное в ответе на вопрос игрока, нам нужно проверить значение логической переменной и если её значение — «истинно», то вывести реплику вроде «Что я, впрочем, уже говорила.». Проверяются значения переменных конструкцией «Если-То-Иначе», которая выглядит так:

    if <условие>:
        e "Блок выполняющийся, если условие истинно."
    else:
        e "Блок выполняющийся, если условие ложно."

Если в случае, если условие ложно, делать ничего не нужно, то else со своим блоком просто не пишут.

Можно также создать цепочку проверок, каждая следующее из которых осуществляется, только если все предыдущие оказались ложны:

    if <условие 1>:
        e "Блок выполняющийся, если условие 1 истинно."
    elif <условие 2>:
        e "Блок выполняющийся, если условие 1 ложно"
        e "а условие 2 истинно."
   elif <условие 3>:
        <...>
    else:
        e "Блок выполняющийся, если все условия ложны."

В качестве условия может выступать равенство значений (переменная == значение, или переменная1 == переменная2 (используются два знака равно), или неравенство (больше либо равно: «переменная >= значение»; строго больше: «переменная > значение»; меньше либо равно: «переменная <= значение»; строго меньше: «переменная < значение»). Условие может быть составным, т.е. состоять из нескольких элементарных (таких, как те, что были описаны только что), связанных между собой логическими операциями И (and или &&), ИЛИ (or или ||), Исключающее ИЛИ (xor). Также возможно использование операции логического отрицания (not). Также, в случае логических переменных вместо «имя_переменной == True» можно просто писать «имя_переменной» В нашем случае конструкция будет выглядеть так:

    if question_asked: 
        p "Что я, впрочем, уже говорила." 

Также, Ren’Py позволяет модифицировать состав меню в зависимости от значения переменных. Для этого в строчке нужного пункта меню после строки с текстом пункта, но до двоеточия нужно приписать if <условие>. Например, профессор снова делает паузу, и игрок вновь может задать вопрос. Создаём меню, один из пунктов которого — вопрос «какая из составляющих важнее», другой — «как визуальная новелла выглядит внешне», а третий — об интерактивности, но его можно задать, только если в первом меню был задан вопрос:

    menu:
        "Может, и правда, спросить?"
        "Спросить, какая из составляющих важнее.":
            "Я поднял руку, показывая, что у меня есть вопрос."
    <…>
        "Спросить о реализации визуальной новеллы.":
            "Я поднял руку, показывая, что у меня есть вопрос."
    <…>
        "Спросить о её оговорке, связанной с интерактивностью." if gameplay_asked:
    <…>

Заодно в блоке результатов первого или второго пункта во второй раз увеличим значение цифровой переменной на единицу.

Вызовите доктора!

Для пущего интереса вновь сделаем в одном из пунктов ещё одно меню. Но на этот раз не будем загромождать блок результатов пункта, а вынесем реализацию этого меню в другое место. И в нужный момент передадим туда управление, чтобы дальше новелла исполнялась с этого места кода. Для этого нужно во-первых обозначить нужное место меткой, а во-вторых перейти по этой метке. В Ren’Py возможно два варианта переходов: прыжок, когда управление просто передаётся на указанную метку и исполнение игры идёт с обозначенного меткой места, и вызов, когда управление передаётся на метку, а по окончании управление возвращается на строчку, следующую за вызовом. Нам, нужен именно вызов. Сначала пишем реализацию вынесенного куска скрипта, которую будем вызывать: после окончания меню, откуда будет идти вызов, пишем:

   label submenu_priority:
       <реализация>
   return

Обратите внимание: Блок метки, в котором записана нужная нам реализация, нужно закончить ключевым словом return — эта команда и вернёт управление в точку вызова. Также, надо удостовериться, что обычным образом в этот блок игра никак не попадёт. Для этого как раз перед меткой submenu_priority сделаем конец игры. Осуществляется это той же командой return. Можно объяснить сей факт так: игра вызывается из главного меню и в конце возвращает управление туда. Теперь нужно выполнить сам вызов. Для этого в блоке результатов пункта меню, где надо осуществить вызов, пишем:

    call submenu_priority

Всё!

Только, когда вы закончите писать скрипт, если используете вызовы, ОБЯЗАТЕЛЬНО запустите вспомогательный инструмент «Добавить From к Call’ам» из пункта «Инструменты» ЦУ Ren’Py! Без этого структура вызовов рискует не заработать в конечном варианте игры, что вы будете распространять.

Спой, светик, не стыдись…

Хм, если оставить всё как есть, конец у данной игры выйдет какой-то оборванный. Лучше сделаем так: После того, как игрок задал второй вопрос (первый, если в первом меню он выбрал пункт «Ничего не спрашивать»), звенит звонок, преподаватель отпускает всех и идёт определение концовки. Но как заставить звонок прозвенеть? Логично будет проиграть звуковой файл. Ren’Py делает это по команде:

   play sound "имя_файла_со_звуком.wav"

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

   $ renpy.pause (2.0)

Сначала знак доллара, затем ключевое слово renpy.pause, потом в скобках длительность паузы в секундах. Правда, при нажатии игроком на какую-либо управляющую клавишу или кнопку мыши пауза прерывается, как и любой эффект. Также Ren’Py умеет воспроизводить фоновую музыку. Это делается следующим образом:

   play music " имя_файла_с_музыкой.ogg "

Музыка может быть в формате OGG, MP3 или MIDI и лежать там же, где могут лежать все остальные ресурсы. Данная команда проигрывает указанный музыкальный файл постоянно, то есть при достижении конца воспроизведение начинается сначала. При этом если до этого проигрывался ещё какой-либо звуковой файл, его проигрывание прерывается. Если нужно остановить проигрывание музыки, применяется команда:

   stop music

Аналогично, если нужно остановить проигрывание звукового файла, применяется команда:

   stop sound

Ко всем командам вывода звуковой информации может быть приписано fadeout <значение>. В этом случае воспроизведение завершается спадом громкости за <значение> секунд.

Всё хорошо, что хорошо кончается.

Всё, что осталось, это определить, какую концовку заработал игрок. Поскольку мы считали достижения игрока в очках и максимум, достижимый в данном примере — 2, то определение сводится к цепочке проверок, начиная с максимума. Также, неплохо был бы вывести сообщение об обретенной концовке не в тестовом окне, а, скажем, по центру экрана. Это достигается путём использования специального персонажа centered. Собственно код, определяющий концовку, будет выглядеть так:

    if ppoints == 2:
        show prof smile
        p "И, я с нетерпением жду продолжения занятия с вами!"
        scene black with fade
        centered "Поздравляем, вы пришли к лучшему окончанию!"
    elif ppoints > 0:
        p "Надеюсь, вам было интересно."
        scene black with fade
        centered "Поздравляем, вы пришли к хорошему окончанию."
    else:
        scene black with fade
        centered "Нормальное окончание."
return

Ветвление как форма размножения.

Всё равно как-то коротковато игра выходит, даже для примера. Добавим-ка ещё одну ветку, со своим центральным персонажем. Причём, чтобы не загромождать редактор, вынесем её в отдельный файл. А в дальнейшем нам не придётся делать фактически никаких лишних действий для доступа к ней. А всё потому, что область видимости Ren’Py — весь проект, все его файлы. Сначала сделаем небольшие приготовления — объявим в блоке init нужные изображения (в нашем случае yuki norm и yuki smile) и персонажа. Теперь создадим новый файл (File > New в редакторе — откроется новая вкладка, озаглавленная Untitled) и сохраним его под каким-нибудь именем и с расширением rpy (File > Save as.. , ввести имя — например script2.rpy — в нужное поле). Теперь можно работать с ним. Прежде всего, нужно создать метку, по которой будут попадать в эту ветвь (Скажем, yuki_branch) . Теперь, в первом файле со скриптом создадим ситуацию, позволяющую туда попасть. Пусть во время рассказа преподавателя герой слышит со стороны соседа смешок и, если решит посмотреть, в чём дело, попадёт в новую ветку, а если проигнорирует, останется в первой. Реализуется, естественно, с помощью меню:

    "Слева от меня кто-то чуть слышно усмехнулся"
    menu:
        "Взглянуть.":
            jump yuki_branch    
        "Не отвлекаться":
            pass

Собственно переход осуществляется командой jump <имя_метки>. Это как раз и есть прыжок, о котором я говорил ранее. Напомню, при нём управление просто передаётся на указанную метку, и исполнение игры идёт с обозначенного меткой места. Положим, герой бросает взгляд на соседа (соседку, вообще-то). Можно это отобразить появлением на экране её образа вдобавок к изображению профессора:

   show prof norm at right
   show yuki norm at left

Заодно это автоматически уберёт образ преподавателя из центра, где он находился раньше, вправо.

Одновременно на экране может находиться сколь угодно много изображений персонажей, только при большом их числе многие будут перекрываться. Впрочем, обычно на экране присутствует не более трёх, в трёх предопределённых позициях. Вот поэтому изображения персонажей делают не шире трети экрана.

Герой с соседкой разговариваются, и всё внимание героя сосредотачивается на ней. Здорово было бы изобразить это сдвигом изображения профессора за границы экрана, а соседки — в центр. Можно реализовать это, скрыв изображение профессора с эффектом moveoutright, и затем показав изображение соседки в центре:

   hide prof with moveoutright
   show yuki norm at center

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

   show prof norm at offscreenright
   show yuki norm at center
   with move
   hide prof

Нужно не забывать скрывать ненужные изображения.

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

Конец игры в данной ветке можно обставить обычным return’ом, но, вероятно, лучше сначала сделать титры. А ещё лучше, чтобы титры предваряли завершение и первой ветки. Для этого разместим титры (обычные высказывания с прославляющим создателя и всё, с помощью чего он создал подобный шедевр, текстом) в блоке метки (скажем, credits), помещённом в первом файле после определения концовки, но до завершающего игру return’a. А в концовках ветви из второго файла поместим команды прыжка по этой метке. Тогда первая ветка попадёт в титры своим ходом (ведь прерывающий ход игры return стоит после блока с титрами), а вторая ветка — прыгнув по метке титров. И конец игры будет происходить в одном месте.

Завершающие штрихи.

Ну вот, работа со скриптом игры завершена. Теперь не мешало бы подправить разные мелочи, вроде заголовка окна или фона главного меню. За это и многое другое отвечает файл options.rpy. Все опции снабжены подробным комментарием. Первое, что нужно сделать, это поменять config.developer = True на config.developer = False. Это запретит пользователю применять приёмы, предназначенные для облегчения жизни разработчика, такие как быстрая перезагрузка игры по нажатию Shift+R или вывод значений всех переменных по нажатию Shift+D. Следующее, это установить нужный заголовок. Находим config.window_title и вписываем в кавычки нужное название. В нашем случае строчка будет выглядеть так:

   config.window_title = u"Знакомство с Визуальными Новеллами"

Затем нужно установить фон для главного меню и внутриигрового меню (доступно во время игры по нажатию Esc). Для этого присваиваем строки с именами нужных файлов переменным mm_root и gm_root. В нашем примере:

   mm_root = "Images/uni.jpg"
   gm_root = "Images/uni.jpg"

Дальше стоит задать, будет ли игра иметь звуковое, музыкальное и голосовое сопровождение, присвоив значения True, если да, или False, если нет, следующим переменным: config.has_sound (звук), config.has_music (музыка) и config.has_voice (голос). Вообще, в этом блоке есть ещё и переменные, отвечающие за звуки интерфейса, но по умолчанию они закомменчены. Самая интересная — это config.main_menu_music, управляющая музыкой, которая должна играть в главном меню.

С помощью этой группы переменных можно поменять положение главного меню:

   # style.mm_menu_frame.xpos = 0.5
   # style.mm_menu_frame.xanchor = 0.5
   # style.mm_menu_frame.ypos = 0.75
   # style.mm_menu_frame.yanchor = 0.5

Если, конечно их раскомментить (убрать решётку).

xpos и ypos определяют точку на экране, а xanchor и yanchor — точку собственно главного меню, которая будет соответствовать точке привязки на экране. Если значения — целые числа, то это расстояние в пикселях от верхнего левого угла экрана и главного меню соответственно. Если же, как по умолчанию, десятичных дроби, то они воспринимаются как доли ширины и высоты экрана и главного меню соответственно.

Напоследок необходимо сказать ещё о двух переменных. Только их значения, по уму, следует устанавливать в самом начале работы над игрой. Ибо они контролируют разрешение экрана игры, от которого зависят размеры игровых изображений. config.screen_width устанавливает ширину экрана, а config.screen_height — высоту. Важно: стоит придерживаться стандартных расширений, иначе при переключении в полноэкранный режим могут быть глюки. По умолчанию установлено:

   config.screen_width = 800
   config.screen_height = 600

Вот, в сущности, и всё. Теперь осталось только хорошо протестировать игру, отловить все дырки в игровой логике и просто очепятки, и можно готовить к выпуску. Также необходимо не забыть запустить из пункта «Инструменты» ЦУ «Добавить From к Call’ам» («Add From to Calls»), если в игре используются вызовы (call) и «Проверить скрипт (Lint)», обнаруживающий шероховатости, могущие плохо сказаться на работе игры на некоторых платформах.

Когда игра готова идти «на золото», обязательно нужно удалить со всем содержимым папку saves в рабочей папке проекта (saves, кстати, генерируется каждый раз при запуске игры на исполнение, если её нет). Стоит также подготовить файлы license.txt (c пользовательским соглашением) и readme.txt (со всей прочей информацией, что вы хотели бы сообщить пользователю — аннотация к игре, управление, благодарности и приветы…). И ещё решить, будет ли проводиться прятанье ресурсов от конечного пользователя. Ren’Py позволяет убрать из открытого доступа изображения и «зашифровать» скрипт игры. Последнее делается при каждом запуске проекта на исполнение, в виде сборки любого .rpy-файла в его аналог расширением .rpyc. Эти файлы достаточны для работы игры, так что если не хотите, чтобы ваш скрипт кто-то видел, можете удалить после финального запуска проекта все файлы .rpy вместе с папкой saves. Изображения архивируются в один файл командой «Архивировать файлы» («Archive Files») пункта «Инструменты» ЦУ Ren’Py. Кстати, в случае архивации смысла складировать изображения в отдельную папку нет.

В любом случае, для отправки «на золото» служит команда «Выпуск игры» («Build Distributions») из всё того же пункта «Инструменты». Сначала игру ещё раз проверят Lint’ом, после чего спросят, хотим ли мы продолжать (если Lint что-то нашёл, стоит выбрать «Нет» и исправить; иначе можно смело жать «Да»). Затем последует риторический вопрос «Хотите ли вы, чтобы Ren’Py создал распространяемые архивы для Windows, Linux x86 и MacOS X» (опять «Да»). Потом спросят имя игры (сразу введено имя проекта и в подсказке предлагается дописать версию) — вводим что надо, затем жмём Enter. Наконец спросят, файлы каких расширений вы не хотите включать в финальную версию — можно смело жать Enter ничего не меняя. Теперь нужно немного подождать, и можно забирать готовые архивы из папки Ren’Py. Поздравляю, создание визуальной новеллы завершено.

Время на прочтение
8 мин

Количество просмотров 110K

Как сделать текстовую игру? Да как угодно. Как сделать кроссплатформенную текстовую игру на русском с иллюстрациями, звуком, работающими сохранениями, без проблем с кириллицей, и с каким-никаким геймплеем? Да ещё и в свободное время, не отрываясь от основной работы? Вот это уже интересней и на самом деле — довольно несложно. Заинтересовавшихся прошу под кат.

image

Примерно год назад мы с товарищем задумали сделать небольшую текстовую игру приблизительно в духе 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 состоит из последовательности реплик, действий с экранами и ввода игрока. Про экраны и ввод чуть ниже, а для начала мы разберёмся с персонажами. В визуальной новелле они создаются так (код из официального туториала, с незначительными правками):

define m = Character('Me', color="#c8c8ff")
define s = Character('Sylvie', color="#c8ffc8")
image sylvie smile = "sylvie_smile.png"
label start
    m "Um... will you..."
    m "Will you be my artist for a visual novel?"
    show sylvie smile
    s "Sure, but what is a "visual novel?""

Создано два персонажа: протагонист и Сильви, оба пишут бледно-синим цветом в стандартное окошко внизу экрана. У Сильви к тому же есть портрет, который появится на экране перед тем, как она начнёт говорить. Выглядит это вот так:

image

Если бы мы создавали визуальную новеллу, то продолжали бы в том же духе, но мы-то не собираемся показывать портреты персонажей, да и иллюстраций пара десятков на всю игру. Большая часть текста вдобавок не является прямой речью персонажей, так что нелогично было бы привязывать её к кому-то из них. Лучше создадим виртуального персонажа-рассказчика:

define narrator = Character(None, kind = nvl, what_color="#000000", size = 12)

Его зовут narrator; это специальное имя, которое отдаёт ему весь текст, явно не аттрибутированный другим персонажам (строго говоря, его зовут None, а narrator, как и m и s в предыдущем примере – переменная, в которую помещается объект персонажа и из которой вызываются его методы, например, say) Аргумент kind принимает два значения: adv и nvl. Первое – это дефолтное поведение, описанное выше, а второе включает nvl-режим, в котором портреты не показываются, а текстовое поле занимает большую часть экрана. Как раз то, что нам было нужно. Этот режим описывается экраном nvl_screen в файле screens.rpy и группой стилей styles.nvl* (файлы screens.rpy и options.rpy соответственно), в которых мы зададим шрифт, фон текстового поля, цвет меню и всё остальное.

image

label start:
  image bg monet_palace_image = Image('images/1129_monet_palace.jpg', align=(0 .5, 0.5)) 
    nvl clear
    hide screen nvl
    scene bg monet_palace_image 
    $ Ren'Py.pause(None) 
   " — Я всегда говорил: твои песенки — дерьмо, Люсьен, и я не понимаю, где ты только находишь музыкантов, согласных это исполнять!"

Разберём построчно: сперва объявляется ярлык start, с которого начнётся игра. Это название зарезервировано и движок всегда будет переходить на него после нажатия кнопки “Новая игра”, где бы в сценарии он ни находился. Всё, что следует за ярлыком, логически находится “внутри” этого ярлыка, поэтому выделяется индентацией: она в Ren’Py работает так же, как и в чистом питоне. Инициализация картинки достаточно очевидна, а вот следующая строчка делает важную вещь: убирает весь текст с экрана nvl_screen. Автоматически это не делается, поэтому, если не расставлять nvl clear в конце каждой страницы, текст спокойно уползёт за пределы экрана и будет выводиться туда, пока экран не будет наконец очищен. Вроде бы мелочь, но на отладку пропущенных nvl clear я потратил намного больше времени, чем готов признать. Свежевымытый экран мы временно уберём, чтобы позволить игроку полюбоваться фоном, покажем фон, включим бесконечную паузу (то есть дождёмся клика) и начнём историю. Как только на nvl_screen начнёт выводиться текст, экран сам вернётся на место.

Строка с паузой, кстати, уже на питоне: для включения единичной строки её достаточно начать с ‘$’, а более длинные куски кода нужно писать внутри блока ‘python:’. Любой код, исполняемый игрой, видит модули самого Ren’Py и явно импортировать их уже не нужно.

Добавляем ветвление и переменные

К этому моменту игра представляет собой читалку, которая показывает текст, меняя при необходимости фоны. Сохранение, перемотка, главное меню и настройки уже работают из коробки. Однако если бы мы хотели написать иллюстрированную повесть, то мы бы её и написали, верно? Добавим перед текстом небольшое меню:

label start:
    menu: 
        "Зайти в меню разнообразного дебага": 
            $ debug_mode = True
            jump debug_menu 
        "Пропустить вступление": 
            jump the_very_start_lazlo_nooptions
        "Начать вступление": 
            label the_very_start: 
                #show screen nvl
                nvl clear 
                hide screen nvl 
                scene bg monet_palace_image 
                $ Ren'Py.pause(None) 
               " — Я всегда говорил: твои песенки — дерьмо, Люсьен, и я не понимаю, где ты только находишь музыкантов, согласных это исполнять!" 

Теперь после включения игры пользователь (или, скорее, разработчик) сможет при желании войти в режим дебага или пропустить уже готовый кусок вступления и начать тестировать сразу кусок из последнего коммита. Строка show screen nvl закомменчена за ненадобностью – как я уже упоминал выше, экран покажется сам собой, когда на нём обновится текст. Комменты, как видите, работают абсолютно очевидным образом.

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

Внутриигровые меню и переменные устроены абсолютно так же. Поскольку и переменных, и ярлыков даже в небольшом эпизоде на десять минут игры разводится невероятное количество, мы приняли несложный вариант венгерской нотации: имя ярлыка ‘the_very_start_lazlo_nooptions’ состоит из трёх частей: названия локации the_very_start (то есть период от начала игры до первого выхода в море), названия эпизода lazlo (то есть пьянка у Лазло, на которой можно нанять молодых бездельников в матросы) и имени собственно ярлыка. При таком подходе имена получаются достаточно громоздкими, но лучше так, чем обнаружить при тестировании, что три месяца назад кто-то уже создал переменную ship_listing, выставил True бог весть где и теперь крен из одного случайного события влияет на исход другого случайного события на другом конце моря.

Вместо заключения

К этому моменту мы уже воспроизвели на Ren’Py функционал упоминавшихся выше Choicescript и inklewriter. Вроде бы наш кораблик готов к отплытию. В следующей статье я покажу, как можно создавать более сложный интерфейс с использованием экранного языка RenPy и ещё более сложный — на чистом питоне.

image

На примере создания детской игры с загадками

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

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

В комментариях к прошлой записи несколько раз прозвучал вопрос о том, как реализована параллельность сюжета в новелле Spiritual Cavern, которая уже обзавелась собственной группой на Facebook.

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

Спойлер: создание новеллы с графикой и написанием кода заняло 20 минут.

Графика: персонаж и фон

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

События происходят не посреди пустоты, потому нам потребуется фон:

Комната, где нам загадывают загадки. Мрачновата, но для примера сойдет)

Улица, куда мы можем попасть только в одной из концовок

Простейший сюжет

Подготовив материалы, мы можем приступить к составлению сюжета — в нашем примере он будет очень простым:

Суть сюжета очень проста: игрок трижды отвечает на вопросы, в зависимости от его ответа меняется настроение кубика. Кроме того, при каждом правильном ответе мы увеличиваем счетчик «Ответы», от которого в последствии зависит концовка игры: если был дан хоть 1

Итак, приступим. Я не буду описывать полный процесс написания кода в RenPy, так как уроков наэту тему довольно много. Но уделю отдельное внимание тем функциям, которые отвечают за ветвление сюжета.

Написание кода игры

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

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

Основными конструкциями в RenPy являются:

  • label имя_метки — место в коде, к которому в последствии можно перейти при помощи команды jump имя_метки
  • scene — команда загрузки фонового изображения из папки images созданного проекта. Важно знать: в случае смены фона требуется заново ввести команду по выводу изображения персонажа.
  • show — отобразить картинку (чаще всего — персонажа) поверх существующего фона.
  • say — фраза, произносимая персонажем. Чаще всего используется в следующих форматах:Вариант первый: «автор» «фраза» — здесь мы явно указываем, кто говорит фразу и как эта фраза звучит.Однако, для удобства пользователя есть возможность упростить свою жизнь, заранее указав имя автора.Для этого перед меткой начала игры применяется следующая конструкция:define anna = Character(«Anna:»)В дальнейшем достаточно написать anna «фраза», что будет воспринято системой как «Anna:» «фраза»Важно понимать, что нет прямой связи между показываемой картинкой (show) и автором фразы — кроме той, которая формируется у игрока в процессе игры.
  • menu — выбор игрока, в зависимости от которого происходит выполнение соответствующего кода, будь то простая фраза либо переход к конкретной метке.

Итак, что же происходит в первом фрагменте кода?

1) мы выводим на экран сцену «bg 1» из папки images нашего проекта
2) от имени игрока (define char = Character(«Я:») перед label start) выводим фразу «Интересно, где же кубик?»
3) плавно (with dissolve) выводим на экран изображение «cube wow»
4) от имени кубика (строка define e = Character(«Кубик:») перед label start) выводим фразу «А вот он я!»
5) после обмена фразами мы переходим к новой для нас команде — menu, отвечающей за выбор игрока и реакцию игры на этот выбор.

Ничего сложного, согласитесь? Требуется лишь следовать правилам составления сцены и корректно вводить команды.

У тех, кто был внимателен при прочтении нашего «сценария», может возникнуть вопрос: а как нам определить, хорошо или плохо заканчивается игра? Всё верно, при помощи команды menu мы можемпредоставить выбор игроку, а в данной ситуации требуется противоположное — сделать выбор на основе данных самой программы.
С этой целью мы добавляем в игру типичный для программ элемент -переменную под названием «answers» и устанавливаем её значение равным нулю.

В дальнейшем, при выборе игроком положительного (правильного) ответа, мы увеличиваем значение этой переменной следующим образом:

Таким образом, после трех вопросов её значение находится в диапазоне от 0 (если не было дано ни одного правильного ответа) до 3 (если все ответы были правильными).
Будем добрыми, и поставим простое условие: отгадал хоть одну загадку — добро пожаловать на прогулку. Для этого нам потребуется конструкция if-else, «если-иначе». Ниже — пример её применения в нашей новелле:

Если больше 1 правильного ответа — переходим к метке good ending; иначе — к метке sad_ending

Обратите внимание — каждая из меток заканчивается переходом к метке «конец истории». В противном случае возможен последовательный показ обеих сцен, что будет выглядеть.. довольно странно.

Итоги

Одна из двух возможных концовок.

Итак, мы создали новеллу, в которой присутствует как локальная (не влияющая на концовку), так и глобальная (определяющая финал истории) вариативность.

Было ли это сложно? Нет — у человека, ранее не работавшего с движком, подобная задача займет не более, чем два часа. Поиграть с Кубиком из нашего примера можно на ПК:

И на Android устройствах:

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

К примеру, в Spiritual Cavern на текущий момент присутствует порядка 1200 строк кода, реализующих первые шесть сцен игры — и, если бы не грамотная организация процесса, я давно потерялся бы в этих дебрях.

А о том, как стоит и не стоит организовывать процесс воплощения новеллы мы поговорим в следующей статье, которая выйдет в Воскресенье. Всем удачи, и приятных выходных!

How to Make a Visual Novel Game in 10 Minutes – Python Ren'Py Tutorial

Do you have a story idea that you’d like to turn into a novel? How about adding visual appeal and interactivity to that novel?

A Visual Novel might be the game genre you are looking for. And this tutorial is here to help set you up in 10 minutes, with minimal coding experience required. Let’s get started!

We will be using the Ren’Py Visual Novel Engine, which is built on top of Python 2.7. As Python itself is a scripting language, you will be able to «script» your visual novel project in Ren’Py.

Since the arrival of Python 3, Python 2.7 has been sunsetted and is no longer actively maintained. Rest assured — Python 2.7 has all the features we need to create an awesome visual novel. Moreover, the newest release of Ren’Py, Ren’Py SDK 7.4, provides a compatibility mode for Python 3. The developers also express the hope of integrating fully with Python 3 in the next release, Ren’Py 8.0.

How to Download and Set up Ren’Py

You can download the latest version of Ren’Py for your operating system (Windows, Mac, Linux) on its official site.

Once you have downloaded and installed Ren’Py, you may open the Ren’Py launcher, select one of the starter projects (Tutorial, The Question) on the left, and click on Launch Project.

Check out the Tutorial to get a sense of the full power of this engine, or The Question to see a very basic visual novel that you can make in 10 minutes.

Screen-Shot-2021-06-21-at-10.51.50

The Ren’Py Launcher

How to Create a New Project in Ren’Py

Let’s create a new project. I called mine Forest Hike🌲, featuring a simple scene where two kids explore a forest trail.

Pay attention to the resolution you choose: The default is 1280 x 720. When we add images, our background images should also conform to these dimensions.

foresthike

How to Run the Boilerplate Project

Launch the boilerplate project. Press Start from the main menu. After two brief lines of dialogue, the script ends and we are brought back to the main menu.

launch

Running the boilerplate project

How to Script Our Project

Let’s start scripting our game based on the boilerplate.

Text Editors like Sublime Text and Atom both have syntax highlighting for Ren’Py scripts ending in .rpy. Check out this Sublime Text package and this Atom package.

The two lines of dialogue we saw are located in script.rpy. Open that file and its content should look like the following. Just like in Python, lines that start with # are comments and won’t be evaluated as part of the Ren’Py script. The comments and the code below are pretty self-explanatory.

# Declare characters used by this game
define e = Character("Eileen")


# The game starts here
label start:

    # Show a background
    scene bg room

    # This shows a character sprite
    show eileen happy

    # These display lines of dialogue.
    e "You've created a new Ren'Py game."
    e "Once you add a story, pictures, and music, you can release it to the world!"

    # This ends the game.
    return
Boilerplate code

The label is used for control flow, which we will cover in the following section.

The return statement on the last line is what brought us back to the main menu.

How to Declare Characters and Add Dialogues

Let’s replace the boilerplate character declaration and dialogues with those from our story. Here is how my story goes:

define laura = Character('Laura')
define tom = Character('Tom')

label start:

    laura "Wait up, Tom!"
    laura "Tom!"
    laura "I said wait up!"
    laura "...Tom?"
    tom "Boo!"
    laura "Yikes... not again."
    tom "Are you scared?"
    laura "Not at all."
    laura "Running off like that is dangerous, you know."
    laura "We are in the forest. We could get lost."
    tom "Okay okay mom. I won't do it again."

    return

story

How to Add Images and Transitions

If you aren’t an artist yourself, you may consider looking for assets in the creative commons domain. itch.io, a marketplace for indie games, is a great place to look for assets.

I found this set of character sprites for my project. For the background images, I simply applied artistic filters to creative commons pictures, giving real-life pictures a nice watercolor aesthetic.

I put all my images inside game/images. Note that it’s okay to use whitespaces in the image file names.

Screen-Shot-2021-06-21-at-13.23.45

A conventional way of organizing image assets

Then we add to script.rpy those images as well as some transitions. Ren’Py applies transitions when it sees keywords like with and at. You can read more about transitions in Ren’Py’s ATL (Animation and Transition Language) docs.

label start:
    scene bg forest day with fade
    show laura angry
    laura "Wait up, Tom!"
    laura "Tom!"
    laura "I said wait up!"
    laura "...Tom?"
    hide laura
    scene bg forest day with vpunch
    show tom happy at right with moveinbottom
    tom "Boo!"
    show laura angry at left with moveinleft
    laura "Yikes... not again."
    tom "Are you scared?"
    laura "Not at all."
    show laura sad
    laura "Running off like that is dangerous, you know."
    laura "We are in the forest. We could get lost."
    tom "Okay okay mom. I won't do it again."

    return

images

With the addition of visuals, our story is coming together nicely.

How to Add Choices

A game with different branches and endings more than doubles the fun. Adding a choice menu to a Ren’Py script is simple:

menu:
    "Which way should we go?"

    "Left":
        tom "Let's check out the trail on the left!"
    "Right":
        tom "Right is always the right way to go!"

choice

How to Use Python Variables and Control Flow

We may define Python variables in a Ren’Py script and alter the flow of our story depending on their values. Python statements start with a $ or an indented python: block.

Adding variables to our previous choice menu:

menu:
    "Which way should we go?"

    "Left":
        tom "Let's check out the trail on the left!"
        $ is_lost = True
    "Right":
        tom "Right is always the right way to go!"
        $ is_lost = False
scene bg forest noon with Dissolve(3.0)
scene bg forest dusk with Dissolve(3.0)
show laura sad at left with moveinleft
laura "It's getting late. Are you sure we aren't lost?"
if is_lost:
    show tom sad at right with moveinleft
    tom "I hope not, but I have a bad feeling about this."
else:
    show tom happy at right with moveinleft
    tom "We are fine. Look! There's the end of the trail."
    tom "I'm the best scout around."

ezgif.com-gif-maker-2-

See the end of this post for my handcrafted resources for working with Python in Ren’Py.

How to Play Music

According to Ren’Py’s Audio docs, playing music and sound effects is as easy as the following:

play music "mozart.ogg"
play sound "woof.mp3"

How to Save and Load the Game

Ren’Py has done all the heavy lifting for us and has a built-in save and load system.

save

Saving the game
load
Loading the game

Other Customization You Can Do

Currently in our dialogue, the entire line of text is displayed at once, instead of character by character. We may change the variable preference.text_cps (CPS stands for character per second) in options.rpy like this.

default preferences.text_cps = 20

cps

Setting a custom CPS displays the text one character at a time at a given rate

There is even more that we can customize in gui.rpy (GUI stands for Graphic User Interface, which includes the textbox and menu choice items that we have seen) or screens.rpy.

What Else is Ren’Py Capable of?

Ren’Py’s capability extends way beyond displaying text and images. I may go as far as to say that Ren’Py is about as capable and versatile as Python itself.

With the Pygame module, it is possible to create complex mini games in Ren’Py. I myself have created and open-sourced a few mini games, including a chess engine that integrates with the Stockfish chess AI as well as a rhythm game engine that automatically generates the beat map for any music file.

promotion

My chess game demo
demo-4
My rhythm game demo

Ren’Py Chess Game 2.0 by r3dhummingbird

PvP and PvC Chess Game Powered by Stockfish in Ren’Py

PmZoCFStudio Madeleine Chaiitch.io

3yVKWH

Ren’Py Rhythm Game by r3dhummingbird

Play a rhythm game in your Ren’Py Visual Novel game!

hKQtvYitch.io

atQlNl

Resources

This tutorial should help get you started with Ren’Py. It’s always useful to refer to the official documentation as you learn the more advanced features to add buzz to your project.

I’ve also created some course material to help you brush up on Python fundamentals and their capabilities in Ren’Py scripts.

RuolinZheng08/python-for-renpy-dev

[Udemy Course Material] Python Basics for Ren’Py Developers — RuolinZheng08/python-for-renpy-dev

faviconRuolinZheng08GitHub

python-for-renpy-dev

Python Basics for Ren’Py Developers

Learn the Python basics to build complex components like an inventory system or a minigame in your Ren’Py project

favicon-196x196Udemy

4121288_82b7_2

Check out my course intro video on YouTube:

Thanks for reading and have fun telling your story!



Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started

How to Make a Visual Novel Game in 10 Minutes – Python Ren'Py Tutorial

Do you have a story idea that you’d like to turn into a novel? How about adding visual appeal and interactivity to that novel?

A Visual Novel might be the game genre you are looking for. And this tutorial is here to help set you up in 10 minutes, with minimal coding experience required. Let’s get started!

We will be using the Ren’Py Visual Novel Engine, which is built on top of Python 2.7. As Python itself is a scripting language, you will be able to «script» your visual novel project in Ren’Py.

Since the arrival of Python 3, Python 2.7 has been sunsetted and is no longer actively maintained. Rest assured — Python 2.7 has all the features we need to create an awesome visual novel. Moreover, the newest release of Ren’Py, Ren’Py SDK 7.4, provides a compatibility mode for Python 3. The developers also express the hope of integrating fully with Python 3 in the next release, Ren’Py 8.0.

How to Download and Set up Ren’Py

You can download the latest version of Ren’Py for your operating system (Windows, Mac, Linux) on its official site.

Once you have downloaded and installed Ren’Py, you may open the Ren’Py launcher, select one of the starter projects (Tutorial, The Question) on the left, and click on Launch Project.

Check out the Tutorial to get a sense of the full power of this engine, or The Question to see a very basic visual novel that you can make in 10 minutes.

Screen-Shot-2021-06-21-at-10.51.50

The Ren’Py Launcher

How to Create a New Project in Ren’Py

Let’s create a new project. I called mine Forest Hike🌲, featuring a simple scene where two kids explore a forest trail.

Pay attention to the resolution you choose: The default is 1280 x 720. When we add images, our background images should also conform to these dimensions.

foresthike

How to Run the Boilerplate Project

Launch the boilerplate project. Press Start from the main menu. After two brief lines of dialogue, the script ends and we are brought back to the main menu.

launch

Running the boilerplate project

How to Script Our Project

Let’s start scripting our game based on the boilerplate.

Text Editors like Sublime Text and Atom both have syntax highlighting for Ren’Py scripts ending in .rpy. Check out this Sublime Text package and this Atom package.

The two lines of dialogue we saw are located in script.rpy. Open that file and its content should look like the following. Just like in Python, lines that start with # are comments and won’t be evaluated as part of the Ren’Py script. The comments and the code below are pretty self-explanatory.

# Declare characters used by this game
define e = Character("Eileen")


# The game starts here
label start:

    # Show a background
    scene bg room

    # This shows a character sprite
    show eileen happy

    # These display lines of dialogue.
    e "You've created a new Ren'Py game."
    e "Once you add a story, pictures, and music, you can release it to the world!"

    # This ends the game.
    return
Boilerplate code

The label is used for control flow, which we will cover in the following section.

The return statement on the last line is what brought us back to the main menu.

How to Declare Characters and Add Dialogues

Let’s replace the boilerplate character declaration and dialogues with those from our story. Here is how my story goes:

define laura = Character('Laura')
define tom = Character('Tom')

label start:

    laura "Wait up, Tom!"
    laura "Tom!"
    laura "I said wait up!"
    laura "...Tom?"
    tom "Boo!"
    laura "Yikes... not again."
    tom "Are you scared?"
    laura "Not at all."
    laura "Running off like that is dangerous, you know."
    laura "We are in the forest. We could get lost."
    tom "Okay okay mom. I won't do it again."

    return

story

How to Add Images and Transitions

If you aren’t an artist yourself, you may consider looking for assets in the creative commons domain. itch.io, a marketplace for indie games, is a great place to look for assets.

I found this set of character sprites for my project. For the background images, I simply applied artistic filters to creative commons pictures, giving real-life pictures a nice watercolor aesthetic.

I put all my images inside game/images. Note that it’s okay to use whitespaces in the image file names.

Screen-Shot-2021-06-21-at-13.23.45

A conventional way of organizing image assets

Then we add to script.rpy those images as well as some transitions. Ren’Py applies transitions when it sees keywords like with and at. You can read more about transitions in Ren’Py’s ATL (Animation and Transition Language) docs.

label start:
    scene bg forest day with fade
    show laura angry
    laura "Wait up, Tom!"
    laura "Tom!"
    laura "I said wait up!"
    laura "...Tom?"
    hide laura
    scene bg forest day with vpunch
    show tom happy at right with moveinbottom
    tom "Boo!"
    show laura angry at left with moveinleft
    laura "Yikes... not again."
    tom "Are you scared?"
    laura "Not at all."
    show laura sad
    laura "Running off like that is dangerous, you know."
    laura "We are in the forest. We could get lost."
    tom "Okay okay mom. I won't do it again."

    return

images

With the addition of visuals, our story is coming together nicely.

How to Add Choices

A game with different branches and endings more than doubles the fun. Adding a choice menu to a Ren’Py script is simple:

menu:
    "Which way should we go?"

    "Left":
        tom "Let's check out the trail on the left!"
    "Right":
        tom "Right is always the right way to go!"

choice

How to Use Python Variables and Control Flow

We may define Python variables in a Ren’Py script and alter the flow of our story depending on their values. Python statements start with a $ or an indented python: block.

Adding variables to our previous choice menu:

menu:
    "Which way should we go?"

    "Left":
        tom "Let's check out the trail on the left!"
        $ is_lost = True
    "Right":
        tom "Right is always the right way to go!"
        $ is_lost = False
scene bg forest noon with Dissolve(3.0)
scene bg forest dusk with Dissolve(3.0)
show laura sad at left with moveinleft
laura "It's getting late. Are you sure we aren't lost?"
if is_lost:
    show tom sad at right with moveinleft
    tom "I hope not, but I have a bad feeling about this."
else:
    show tom happy at right with moveinleft
    tom "We are fine. Look! There's the end of the trail."
    tom "I'm the best scout around."

ezgif.com-gif-maker-2-

See the end of this post for my handcrafted resources for working with Python in Ren’Py.

How to Play Music

According to Ren’Py’s Audio docs, playing music and sound effects is as easy as the following:

play music "mozart.ogg"
play sound "woof.mp3"

How to Save and Load the Game

Ren’Py has done all the heavy lifting for us and has a built-in save and load system.

save

Saving the game
load
Loading the game

Other Customization You Can Do

Currently in our dialogue, the entire line of text is displayed at once, instead of character by character. We may change the variable preference.text_cps (CPS stands for character per second) in options.rpy like this.

default preferences.text_cps = 20

cps

Setting a custom CPS displays the text one character at a time at a given rate

There is even more that we can customize in gui.rpy (GUI stands for Graphic User Interface, which includes the textbox and menu choice items that we have seen) or screens.rpy.

What Else is Ren’Py Capable of?

Ren’Py’s capability extends way beyond displaying text and images. I may go as far as to say that Ren’Py is about as capable and versatile as Python itself.

With the Pygame module, it is possible to create complex mini games in Ren’Py. I myself have created and open-sourced a few mini games, including a chess engine that integrates with the Stockfish chess AI as well as a rhythm game engine that automatically generates the beat map for any music file.

promotion

My chess game demo
demo-4
My rhythm game demo

Ren’Py Chess Game 2.0 by r3dhummingbird

PvP and PvC Chess Game Powered by Stockfish in Ren’Py

PmZoCFStudio Madeleine Chaiitch.io

3yVKWH

Ren’Py Rhythm Game by r3dhummingbird

Play a rhythm game in your Ren’Py Visual Novel game!

hKQtvYitch.io

atQlNl

Resources

This tutorial should help get you started with Ren’Py. It’s always useful to refer to the official documentation as you learn the more advanced features to add buzz to your project.

I’ve also created some course material to help you brush up on Python fundamentals and their capabilities in Ren’Py scripts.

RuolinZheng08/python-for-renpy-dev

[Udemy Course Material] Python Basics for Ren’Py Developers — RuolinZheng08/python-for-renpy-dev

faviconRuolinZheng08GitHub

python-for-renpy-dev

Python Basics for Ren’Py Developers

Learn the Python basics to build complex components like an inventory system or a minigame in your Ren’Py project

favicon-196x196Udemy

4121288_82b7_2

Check out my course intro video on YouTube:

Thanks for reading and have fun telling your story!



Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started

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

уровня интеллекта

возраста.
Поэтому я начал серию видео-уроков по RenPy для новичков. Видео рассчитаны на людей, которые мечтают создать визуальную новеллу, но не имеют опыта программирования и не знакомы с Python. Всё, что вам нужно — это любовь к визуальным новеллам, к творчеству и желание учиться и делать ошибки.
RenPy — это просто! И вы сможете создать собственную новеллу, если приложите некоторые усилия.

Базовый курс будет состоять примерно из 40 уроков, в которых будет всё самое необходимое для создания полноценной новеллы с выборами, развилками сюжета, параметрами, разными концовками, галереей.
На данный момент есть уже более 20 видео.

После этого, я перейду на более продвинутые темы.
Добро пожаловать и приятного обучения!

Все уроки с кусочками кода собраны на отдельном сайте.

Список уроков на данный момент:
1. Что нужно для создания новеллы
2. Бесплатная музыка и другие ресурсы
3. Структура проекта RenPy
4. Основы программирования в RenPy
5. Персонажи и реплики
6. Фоны и спрайты персонажей
7. Прозрачность спрайтов и размер фона
8. Изменение расположения спрайтов
9. Быстрое изменение спрайта в диалоге
10. Переходы
11. Монологи и другие хитрости с текстом
12. Основы написания сценария
13. Процесс создания новеллы
14. Орфография в новелле
15. Выбор действий и метки
16. Логические переменные и условия
17. Иконка и «Об игре»
18. Фон и музыка в меню
19. Секретные варианты ответов
20. Ввод имени персонажа
21. Форматирование текста
22. Музыка и звуки
23. Числовые переменные

Плейлист уроков

# game/tutorial_quickstart.rpy:28 translate russian tutorial_create_27048c11: # e «When you’re ready to use Ren’Py to create your visual novel, the first step is to create a new project.» e «Когда вы будете готовы начать создание вашей визуальной новеллы, первым вашим шагом станет создание нового проекта.« # game/tutorial_quickstart.rpy:30 translate russian tutorial_create_bae6289c: # e «You can create a new project by clicking ‘Create New Project’ on the front screen of the launcher.» e «Вы можете создать новый проект, кликнув на ‘Добавить новый проект’ на главном экране лаунчера.« # game/tutorial_quickstart.rpy:32 translate russian tutorial_create_45915fcb: # e «If this is your first time using Ren’Py, it’ll ask you for the place you want to keep your projects. The best place is always somewhere that’s frequently backed up.» e «Если вы в первый раз запускаете Ren’Py, то вас спросят, где вы будете держать ваши проекты. Лучше всего держать проекты на облачных дисках, где они постоянно будут сохраняться.« # game/tutorial_quickstart.rpy:36 translate russian tutorial_create_55e30cb5: # e «After that, Ren’Py will ask for a name for your project. You’ll have to stick to English letters and numbers, as zip files can’t handle anything more than that.» e «После этого Ren’Py спросит вас об имени вашего проекта. Вам нужно ограничиться английскими буквами и цифрами, так как zip-архивы могут неправильно обрабатывать русский язык.« # game/tutorial_quickstart.rpy:40 translate russian tutorial_create_dea3e5c2: # e «The next thing Ren’Py will ask for is the resolution the visual novel will run at. This controls how large or small you’ll have to make your game’s artwork.» e «Далее Ren’Py спросит разрешение вашей визуальной новеллы. Разрешение говорит о том, на какой размер фонов, спрайтов, CG и других изображений вы должны ориентироваться.« # game/tutorial_quickstart.rpy:44 translate russian tutorial_create_3289ea75: # e «Finally, Ren’Py will ask you to select a color scheme. You can change this after the game has been created, so just pick a color that’s pleasing.» e «И наконец, Ren’Py попросит вас выбрать цветовую схему. Вы сможете изменить её после создания игры, так что выберите любую понравившуюся композицию.« # game/tutorial_quickstart.rpy:48 translate russian tutorial_create_6b9e3b96: # e «Once that’s done, Ren’Py will work for a bit and return you to the main menu with the new project selected. Now, when you click Launch, Ren’Py will start your new game.» e «Как только с этим будет покончено, Ren’Py сгенерирует проект и вернёт вас в главное меню, новый проект будет автоматически выбран. Теперь, кликнув на ‘Запустить проект’, Ren’Py запустит вашу новую игру.« # game/tutorial_quickstart.rpy:50 translate russian tutorial_create_bdf94f9b: # e «To get back here, you can choose ‘Tutorial’ to switch to this tutorial game.» e «Чтобы снова посмотреть обучение, можете выбрать ‘Обучение’ и таким образом вы переключитесь на проект обучения.« # game/tutorial_quickstart.rpy:52 translate russian tutorial_create_22f516df: # e «You’ll also need to edit the games script to make changes. To do that, click ‘script.rpy’ on the front page of the launcher.» e «Также, чтобы сделать свою игру, вам понадобится отредактировать её скрипт. Чтобы сделать это, кликните на ‘script.rpy’ в окне проекта.« # game/tutorial_quickstart.rpy:54 translate russian tutorial_create_9151025b: # e «If it’s your first time doing so, Ren’Py will ask you to select a text editor. Atom might be a safe choice, but read the descriptions to be sure.» e «Если это ваш первый раз, Ren’Py попросит вас выбрать текстовый редактор. Atom — вполне безопасный выбор, но перед выбором внимательно прочитайте описания или же используйте собственный редактор.« # game/tutorial_quickstart.rpy:56 translate russian tutorial_create_bfbd6220: # e «After the text editor is downloaded, the script will open up and you can start to change what characters are saying.» e «После того как текстовый редактор загрузится, скрипт откроется, и вы сможете начать изменять то, что говорят персонажи.« # game/tutorial_quickstart.rpy:69 translate russian tutorial_dialogue_112ff505: # e «Probably the most common thing a creator does with Ren’Py is to write dialogue for the player to read.» e «Наверное, самая распространённая для разработчиков вещь в Ren’Py — написание диалогов.« # game/tutorial_quickstart.rpy:71 translate russian tutorial_dialogue_be2be31a: # e «But before I can show you how to write dialogue, let me show you how we present script examples.» e «Но прежде чем я покажу вам, как писать диалог, давайте я сначала покажу, как мы показываем примеры скрипта.« # game/tutorial_quickstart.rpy:74 translate russian tutorial_dialogue_7b6be28e: # «Eileen» «Examples will show up in a window like the one above. You’ll need to click outside of the example window in order to advance the tutorial.» «Эйлин« «Примеры показываются так же, как и в окне выше. Для того чтобы продолжить обучение, вам нужно кликнуть за границы этого окна.« # game/tutorial_quickstart.rpy:76 translate russian tutorial_dialogue_5269d005: # «Eileen» «When an example is bigger than the screen, you can scroll around in it using the mouse wheel or by simply dragging the mouse.» «Эйлин« «Когда пример оказывается больше экрана, вы можете прокручивать его при помощи колеса мыши или просто перетаскивая мышкой.« # game/tutorial_quickstart.rpy:78 translate russian tutorial_dialogue_241c0c74: # «Eileen» «Script might seem scary at first, but if you look you’ll see it’s easy to match it up to what I’m saying.» «Эйлин« «С непривычки скрипт может показаться пугающим, но если вы присмотритесь, то увидете, как просто обозначено то, что я говорю.« # game/tutorial_quickstart.rpy:82 translate russian tutorial_dialogue_f0d66410: # e «Let’s see the simplest possible Ren’Py game.» e «Давайте поиграем в самую простейшую игру на Ren’Py.« # game/tutorial_quickstart.rpy:89 translate russian tutorial_dialogue_3e6b0068: # «Wow, It’s really really dark in here.» «Ого, здесь очень темно.« # game/tutorial_quickstart.rpy:91 translate russian tutorial_dialogue_5072a404: # «Lucy» «Better watch out. You don’t want to be eaten by a Grue.» «Люси« «Берегитесь, а то вас съест Грю.« # game/tutorial_quickstart.rpy:99 translate russian tutorial_dialogue_d39d1b2b: # e «I’ll show you the script of that example.» e «Я покажу вам код этого примера.« # game/tutorial_quickstart.rpy:101 translate russian tutorial_dialogue_f51ecf1f: # e «This script demonstrates two kinds of Ren’Py statements, labels and say statements.» e «Этот код демонстрирует два вида операторов Ren’Py: метки и операторы речи.« # game/tutorial_quickstart.rpy:103 translate russian tutorial_dialogue_bc7ec147: # e «The first line is a label statement. The label statement is used to give a name to a place in the program.» e «Первая строка — метка. Она используются для именования определённых мест в программе.« # game/tutorial_quickstart.rpy:105 translate russian tutorial_dialogue_b20db833: # e «In this case, we’re naming a place «start». The start label is special, as it marks the place a game begins running.» e «В данном случае мы назвали место «start«. Метка start — особенная, она отмечает место, где начинается игра.« # game/tutorial_quickstart.rpy:107 translate russian tutorial_dialogue_b0afbe96: # e «The next line is a simple say statement. It consists of a string beginning with a double-quote, and ending at the next double-quote.» e «Следующая строка — простой оператор речи. Он состоит из текста, заключённого в двойные кавычки.« # game/tutorial_quickstart.rpy:109 translate russian tutorial_dialogue_628c9e4c: # e «Special characters in strings can be escaped with a backslash. To include » in a string, we have to write \».» e «Особые знаки можно экранировать с помощью обратного слеша. Чтобы включить « в строку, мы пишем \«.« # game/tutorial_quickstart.rpy:116 translate russian tutorial_dialogue_3e6b0068_1: # «Wow, It’s really really dark in here.» «Ого, здесь очень темно.« # game/tutorial_quickstart.rpy:125 translate russian tutorial_dialogue_d7f0b5b7: # e «When Ren’Py sees a single string on a line by itself, it uses the narrator to say that string. So a single string can be used to express a character’s thoughts.» e «Когда Ren’Py видит на строке кода чистую строку текста, он показывает её как слова рассказчика. Её можно также использовать для мыслей персонажа.« # game/tutorial_quickstart.rpy:131 translate russian tutorial_dialogue_5072a404_1: # «Lucy» «Better watch out. You don’t want to be eaten by a Grue.» «Люси« «Берегитесь, а то вас съест Грю.« # game/tutorial_quickstart.rpy:139 translate russian tutorial_dialogue_9dd2d543: # e «When we have two strings separated by a space, the first is used as the character’s name, and the second is what the character is saying.» e «Когда два выражения в кавычках разделяет пробел, первое из них служит именем персонажа, а второе — его словами.« # game/tutorial_quickstart.rpy:141 translate russian tutorial_dialogue_64ffe685: # e «This two-argument form of the say statement is used for dialogue, where a character is speaking out loud.» e «Эта форма оператора речи используется для диалога, то есть для речи персонажа.« # game/tutorial_quickstart.rpy:143 translate russian tutorial_dialogue_97a33275: # e «If you’d like, you can run this game yourself by erasing everything in your project’s script.rpy file, and replacing it with the code in the box above.» e «Если хотите, можете сами запустить эту игру, заменив всё в своём файле script.rpy на код, находящийся в окне сверху.« # game/tutorial_quickstart.rpy:145 translate russian tutorial_dialogue_c5e70d7e: # e «Be sure to preserve the spacing before lines. That’s known as indentation, and it’s used to help Ren’Py group lines of script into blocks.» e «Убедитесь, что сохраняете пробелы перед строками. Такой отступ называется индентацией и помогает Ren’Py группировать отдельные строки кода в блоки.« # game/tutorial_quickstart.rpy:149 translate russian tutorial_dialogue_90719f73: # e «Using a string for a character’s name is inconvenient, for two reasons.» e «Однако использование такого выражения для показа имени персонажа неудобно по целым двум причинам.« # game/tutorial_quickstart.rpy:151 translate russian tutorial_dialogue_910f286a: # e «The first is that’s it’s a bit verbose. While typing «Lucy» isn’t so bad, imagine if you had to type «Eileen Richardson» thousands of times.» e «Первая состоит в том, что это долго печатать. Нет, конечно, напечатать «Люси« не так сложно, но представьте, что вам нужно набрать «Эйлин Ричардсон« несколько тысяч раз.« # game/tutorial_quickstart.rpy:153 translate russian tutorial_dialogue_9c9d59c2: # e «The second is that it doesn’t leave any place to put styling, which can change the look of a character.» e «Вторая причина — такой подход не позволяет вам использовать стили, изменяющие уникальность персонажа.« # game/tutorial_quickstart.rpy:155 translate russian tutorial_dialogue_2a2d1e51: # e «To solve these problems, Ren’Py lets you define Characters.» e «Для решения этих проблем Ren’Py позволяет объявлять объекты-персонажи.« # game/tutorial_quickstart.rpy:159 translate russian tutorial_dialogue_16e8c5fd: # e «Here’s an example Character definition. It begins with the word «define». That tells Ren’Py that we are defining something.» e «Вот пример такого объекта-персонажа. Он начинается со слова «define«, которое показывает Ren’Py, что мы хотим что-то определить.« # game/tutorial_quickstart.rpy:161 translate russian tutorial_dialogue_34fe5aa0: # e «Define is followed by a short name for the character, like «l». We’ll be able to use that short name when writing dialogue.» e «После этого слова следует короткое имя персонажа, например, «l«. Позднее мы сможем использовать это короткое имя при написании диалога.« # game/tutorial_quickstart.rpy:163 translate russian tutorial_dialogue_67f90201: # e «This is followed by an equals sign, and the thing that we’re defining. In this case, it’s a Character.» e «Затем идёт знак равенства и то, что мы определяем. В нашем случае это персонаж (Character).« # game/tutorial_quickstart.rpy:165 translate russian tutorial_dialogue_4e454a89: # e «On the first line, the character’s name is given to be «Lucy», and her name will be drawn a reddish color.» e «На первой строке мы говорим, что имя персонажа — «Люси«, и то, что имя должно быть розового цвета.« # game/tutorial_quickstart.rpy:167 translate russian tutorial_dialogue_db11f026: # e «These short names are case-sensitive. Capital L is a different name from lower-case l, so you’ll need to be careful about that.» e «Эти короткие имена регистрозависимы. Буква L и буква l отличны друг от друга, так что вам нужно вести себя с ними осторожно.« # game/tutorial_quickstart.rpy:171 translate russian tutorial_dialogue_1d161320: # e «Now that we have a character defined, we can use it to say dialogue.» e «Теперь, когда у нас есть определение персонажа, мы можем использовать его для диалога.« # game/tutorial_quickstart.rpy:178 translate russian tutorial_dialogue_3710169c: # l «Why are you trying to put words into my mouth? And who are you calling «it»?» l «Почему ты засовываешь мне в рот слова? И кого ты называешь «он«?« # game/tutorial_quickstart.rpy:180 translate russian tutorial_dialogue_6d463776: # l «What’s more, what are you going to do about the Grue problem? Are you just going to leave me here?» l «И вообще, что нам делать с Грю? Ты просто меня здесь бросишь?« # game/tutorial_quickstart.rpy:188 translate russian tutorial_dialogue_023bcd31: # e «Here’s the full game, including the two new lines of dialogue, both of which use the Character we defined to say dialogue.» e «Вот полная игра, включающая две новые строки диалога, использующие наш объект-персонаж.« # game/tutorial_quickstart.rpy:190 translate russian tutorial_dialogue_48bb9547: # e «The one-argument form of the say statement is unchanged, but in the two-argument form, instead of the first string we can use a short name.» e «Форма оператора речи в случае с одним аргументом осталась неизменной, но в случае с двумя аргументами, вместо первой строки текста мы использовали короткое имя персонажа.« # game/tutorial_quickstart.rpy:192 translate russian tutorial_dialogue_56a9936f: # e «When this say statement is run, Ren’Py will look up the short name, which is really a Python variable. It will then use the associated Character to show the dialogue.» e «При запуске оператора речи, Ren’Py пойдёт искать короткое имя, являющееся переменной Python. И потом будет использован ассоциированный с ним объект Character, показывающийся в диалоге.« # game/tutorial_quickstart.rpy:194 translate russian tutorial_dialogue_d5984a21: # e «The Character object controls who is speaking, the color of their name, and many other properties of the dialogue.» e «Объект Character управляет именем персонажа, его цветом, и другими свойствами диалога.« # game/tutorial_quickstart.rpy:198 translate russian tutorial_dialogue_a5bcac8b: # e «Since the bulk of a visual novel is dialogue, we’ve tried to make it as easy to write as possible.» e «Так как огромная часть работы над визуальной новеллой составляет написание диалога, мы постарались упростить это настолько, насколько возможно.« # game/tutorial_quickstart.rpy:200 translate russian tutorial_dialogue_6b9a42d0: # e «Hopefully, by allowing the use of short names for characters, we’ve succeeded.» e «Надеемся, что позволив вам использовать короткие имена персонажей, мы облегчили вам жизнь.« # game/tutorial_quickstart.rpy:206 translate russian tutorial_images_e09ac970: # e «A visual novel isn’t much without images. So let’s add some images to our little game.» e «Визуальная новелла не очень интересна без изображений, поэтому давайте добавим в нашу маленькую игру парочку.« # game/tutorial_quickstart.rpy:208 translate russian tutorial_images_40140793: # e «Before we can show images, we must first choose image names, then place the image files into the images directory.» e «Перед отображением изображений, сначала нам нужно выбрать для них имена, а затем их следует разместить в папке images.« # game/tutorial_quickstart.rpy:210 translate russian tutorial_images_d73388f8: # e «An image name is something like ‘bg cave’ or ‘lucy happy’, with one or more parts separated by spaces.» e «Изображение можно проименовать ‘bg cave’ или ‘lucy happy’, где одна или более частей будут разделены пробелами.« # game/tutorial_quickstart.rpy:212 translate russian tutorial_images_2d5596d4: # e «Each part should start with a lower-case letter, and then contain lower-case letters, numbers, and underscores.» e «Каждая часть имени должна начинаться с прописной буквы, и содержать только подобные буквы, цифры и подчёркивания.« # game/tutorial_quickstart.rpy:214 translate russian tutorial_images_e02c0c82: # e «The first part of an image is called the tag. For ‘bg cave’ the tag is ‘bg’, while for ‘lucy happy’ the tag is ‘lucy’.» e «Первая часть имени изображения называется тег. Для ‘bg cave’ тегом является ‘bg’, а для ‘lucy happy’ тег — ‘lucy’.« # game/tutorial_quickstart.rpy:216 translate russian tutorial_images_d5eafcf2: # e «You can open the images directory by clicking the appropriate button in the Ren’Py launcher.» e «Вы можете открыть папку images, кликнув на соответствующую ей кнопку в окне проекта.« # game/tutorial_quickstart.rpy:218 translate russian tutorial_images_e4b12fb6: # e «The files in the images directory should have the same name as the image, followed by an extension like .jpg, .png, or .webp.» e «Файлы в папке images должны иметь то же имя, что и изображение в коде, а также должны иметь поддерживаемые разрешения, например, .jpg, .png или .webp.« # game/tutorial_quickstart.rpy:220 translate russian tutorial_images_a3bd89b2: # e «Our example uses ‘bg cave.jpg’, ‘lucy happy.png’, and ‘lucy mad.png’.» e «Наш пример использует ‘bg cave.jpg’, ‘lucy happy.png’ и ‘lucy mad.png’.« # game/tutorial_quickstart.rpy:224 translate russian tutorial_images_76b954de: # e «Let’s see what those look like in the game.» e «Давайте посмотрим, как они выглядят в игре.« # game/tutorial_quickstart.rpy:230 translate russian tutorial_images_f04e72ea: # l «Now that the lights are on, we don’t have to worry about Grues anymore.» l «Ну хоть свет включили. Теперь не нужно беспокоиться о Грю.« # game/tutorial_quickstart.rpy:234 translate russian tutorial_images_d77ffa1c: # l «But what’s the deal with me being in a cave? Eileen gets to be out in the sun, and I’m stuck here!» l «Но что за дела, почему я в пещере? Эйлин греется на солнышке, а я застряла здесь!« # game/tutorial_quickstart.rpy:242 translate russian tutorial_images_6c0c938b: # e «Here’s the script for that scene. Notice how it includes two new statements, the scene and show statement.» e «Вот код этой сцены. Заметьте, что он включает в себя два новых оператора — scene и show.« # game/tutorial_quickstart.rpy:244 translate russian tutorial_images_1a4660b9: # e «The scene statement clears the screen, and then adds a background image.» e «Оператор scene очищает экран и добавляет изображение на фон.« # game/tutorial_quickstart.rpy:246 translate russian tutorial_images_672c8cb8: # e «The show statement adds a background image on top of all the other images on the screen.» e «Оператор show добавляет изображение поверх других.« # game/tutorial_quickstart.rpy:248 translate russian tutorial_images_2fc7baee: # e «If there was already an image with the same tag, the new image is used to replace the old one.» e «Если у нового изображения такой же тег, как у того, что уже показано, произойдёт замена старого изображения.« # game/tutorial_quickstart.rpy:250 translate russian tutorial_images_802825f2: # e «Changes to the list of shown images take place instantly, so in the example, the user won’t see the background by itself.» e «Изменения отображаемых изображений происходят моментально, так что в этом примере пользователь не видит фон сам по себе.« # game/tutorial_quickstart.rpy:252 translate russian tutorial_images_b246dfdd: # e «The second show statement has an at clause, which gives a location on the screen. Common locations are left, right, and center, but you can define many more.» e «Второй оператор, show, содержит функцию at, позволяющую задать местоположение на экране. Чаще всего используются left, right и center, но вы можете определить и много других.« # game/tutorial_quickstart.rpy:257 translate russian tutorial_images_82fceeb8: # e «In this example, we show an image named logo base, and we show it at a creator-defined position, rightish.» e «В этом примере, мы показываем изображение с именем logo base, и мы показываем его на определённой разработчиком позиции — rightish.« # game/tutorial_quickstart.rpy:259 translate russian tutorial_images_9defda43: # e «We also specify that it should be shown behind another image, in this case eileen. That’s me.» e «Мы также указываем что его следует показывать за другим изображением, eileen. То есть, за мной.« # game/tutorial_quickstart.rpy:264 translate russian tutorial_images_73d331f7: # e «Finally, there’s the hide statement, which hides the image with the given tag.» e «Наконец, оператор hide скрывает изображение с указанным тегом.« # game/tutorial_quickstart.rpy:266 translate russian tutorial_images_f34f62d5: # e «Since the show statement replaces an image, and the scene statement clears the scene, it’s pretty rare to hide an image.» e «Так как оператор show заменяет изображение, а оператор scene очищает экран, скрывать изображения приходится редко.« # game/tutorial_quickstart.rpy:268 translate russian tutorial_images_e06fa53a: # e «The main use is for when a character or prop leaves before the scene is over.» e «Чаще всего его используют при уходе персонажа или отдельного объекта до начала новой сцены.« # game/tutorial_quickstart.rpy:282 translate russian tutorial_simple_positions_b492e793: # e «When the standard positions that come with Ren’Py aren’t enough for you, you can create your own. Here, I’ll show you the easy way to do it.» e «Когда стандартных позиций Ren’Py становится недостаточно, вы можете создать собственные. Сейчас я покажу вам, как просто их делать.« # game/tutorial_quickstart.rpy:291 translate russian tutorial_simple_positions_04e3bc44: # e «The first way to do it is to show an image followed by a colon. Then indented on the next couple of lines are the xalign and yalign transform properties.» e «Сначала мы показываем изображение и ставим после него двоеточие. Затем, на следующих строках, учитывая индентацию, мы прописываем трансформационные параметры xalign и yalign.« # game/tutorial_quickstart.rpy:293 translate russian tutorial_simple_positions_3ecad5f8: # e «Each of the transform properties is a name followed by a value. For xalign and yalign, the values are numbers.» e «Каждая трансформация — это определённое имя со значением. Для параметров xalign и yalign, значения — это цифры.« # game/tutorial_quickstart.rpy:295 translate russian tutorial_simple_positions_61c1b124: # e «The xalign transform property is the important one, as it controls where the image is placed horizontally on the screen.» e «Параметр xalign очень важен, так как он контролирует позицию изображения по горизонтали.« # game/tutorial_quickstart.rpy:305 translate russian tutorial_simple_positions_67ebea97: # e «An xalign of 0.0 is the left side.» e «Если мы поставим xalign на 0.0, то это будет левая сторона экрана.« # game/tutorial_quickstart.rpy:315 translate russian tutorial_simple_positions_bd4f56d8: # e «0.5 is the center.» e «0.5 — центр.« # game/tutorial_quickstart.rpy:324 translate russian tutorial_simple_positions_fb2c48f2: # e «And 1.0 is the right. The decimal place is important and has to be there. Just 1 by itself won’t work the same.» e «И 1.0 — правая сторона. Десятичная дробь очень важна и обязана здесь быть. Если мы напишем просто 1 или 1,0, то результат будет не тот же, или же код вообще перестанет работать.« # game/tutorial_quickstart.rpy:333 translate russian tutorial_simple_positions_8eebc9a7: # e «Of course, you can pick any position in between.» e «Само собой, вы можете выбрать любую позицию на экране.« # game/tutorial_quickstart.rpy:335 translate russian tutorial_simple_positions_4cd917f6: # e «The yalign property is the same way, with 0.0 being the top of the screen and 1.0 being the bottom. Since most sprites stick to the bottom, it’s almost always 1.0.» e «Параметр yalign действует похожим образом, 0.0 — верх экрана, а 1.0 — его низ. Так как большинство спрайтов располагается внизу, в большинстве случаев мы оставляем их на 1.0.« # game/tutorial_quickstart.rpy:341 translate russian tutorial_simple_positions_fbd1a3eb: # e «While being able to write positions like this is useful, having to repeatedly do so isn’t. So Ren’Py lets you define a transform once, and reuse it.» e «И в то время как умение писать позиции полезно, необходимость постоянно их повторять — совсем не полезна. Поэтому Ren’Py позволяет вам один раз определить трансформацию, а затем использовать её при необходимости.« # game/tutorial_quickstart.rpy:345 translate russian tutorial_simple_positions_2377e3b3: # e «Usually transforms are defined at the top of a file, right after the characters. But it doesn’t matter to Ren’Py where you define them.» e «Обычно трансформации определяются выше основного скрипта, сразу после определения персонажей, но в Ren’Py не имеет значения, где вы будете их определять.« # game/tutorial_quickstart.rpy:347 translate russian tutorial_simple_positions_3ce7e367: # e «The transform is given a name, slightleft, and then the xalign and yalign properties.» e «Нашей трансформации даётся имя slightleft, а затем параметры xalign и yalign.« # game/tutorial_quickstart.rpy:355 translate russian tutorial_simple_positions_82d640d9: # e «Once a transform has been defined, you can use it in the at clause of the show statement.» e «Как только трансформация будет объявлена, вы можете использовать её с функцией at при операторе show.« # game/tutorial_quickstart.rpy:360 translate russian tutorial_simple_positions_16b66785: # e «Transforms are sticky. If you replace an image without using a transform, Ren’Py will keep the same transforms it had been using.» e «Трансформации очень липкие. Если вы замените изображение без новой трансформации, Ren’Py применит на неё прошлую трансформацию.« # game/tutorial_quickstart.rpy:364 translate russian tutorial_simple_positions_5d5e0cfd: # e «Of course, there’s a lot more to transforms than this. If you want to learn more, you can read the sections on Position Properties, Transforms and Animation, and Transform Properties.» e «Разумеется, существует намного больше трансформаций. Если вы хотите узнать о них побольше, можете почитать о них в секциях ‘Позиционные параметры’, ‘Анимация и Трансформация’ и ‘Параметры трансформаций’.« # game/tutorial_quickstart.rpy:366 translate russian tutorial_simple_positions_e65da9bf: # e «But for many visual novels, xalign and yalign are the only properties that matter.» e «Но для многих визуальных новелл будет достаточно пары xalign и yalign.« # game/tutorial_quickstart.rpy:381 translate russian tutorial_transitions_9b8c714c: # e «It can be somewhat jarring for the game to jump from place to place.» e «Если игра прыгает туда-сюда, у игроков может закружиться голова.« # game/tutorial_quickstart.rpy:388 translate russian tutorial_transitions_3e290ea8: # e «To help take some of edge off a change in scene, Ren’Py supports the use of transitions. Let’s try that scene change again, but this time we’ll use transitions.» e «Для того, чтобы сделать изменения сцен приятными для глаз, Ren’Py поддерживает использование переходов. Давайте попробуем повторить то же изменение сцены, но в этот раз с переходом.« # game/tutorial_quickstart.rpy:402 translate russian tutorial_transitions_9c0a86c4: # e «That’s much smoother. Here’s some example code showing how we include transitions in our game.» e «Так-то лучше. Вот пример кода, показывающий использование переходов в игре.« # game/tutorial_quickstart.rpy:404 translate russian tutorial_transitions_3e490d40: # e «It uses the with statement. The with statement causes the scene to transition from the last things shown to the things currently being shown.» e «Они используют оператор with. Оператор with вызывает переход сцены из того, что было показано раньше, к тому, что должно быть показано сейчас.« # game/tutorial_quickstart.rpy:406 translate russian tutorial_transitions_a43847df: # e «It takes a transition as an argument. In this case, we’re using the Dissolve transition. This transition takes as an argument the amount of time the dissolve should take.» e «Он принимает имя перехода как аргумент. В нашем случае используется переход Dissolve. Этот переход принимает в себя аргумент — время, которое он будет длиться.« # game/tutorial_quickstart.rpy:408 translate russian tutorial_transitions_6fcee414: # e «In this case, each transition takes half a second.» e «В нашем случае, его длительность — полсекунды.« # game/tutorial_quickstart.rpy:412 translate russian tutorial_transitions_033042cc: # e «We can define a short name for a transition, using the define statement. Here, we’re defining slowdissolve to be a dissolve that takes a whole second.» e «Мы можем определить для перехода короткое имя, используя оператор define. Здесь мы определили showdissolve — переход Dissolve, занимающий секунду.« # game/tutorial_quickstart.rpy:427 translate russian tutorial_transitions_0ba82f00: # e «Once a transition has been given a short name, we can use it in our game.» e «После объявления такого имени мы можем использовать его в игре.« # game/tutorial_quickstart.rpy:431 translate russian tutorial_transitions_51ff9600: # e «Ren’Py defines some transitions for you, like dissolve, fade, and move. For more complex or customized transitions, you’ll have to define your own.» e «Ren’Py определяет за вас несколько популярных переходов: растворение (dissolve), затухание (fade) и передвижение (move). Для более сложных переходов, вам придётся определять собственные переходы.« # game/tutorial_quickstart.rpy:433 translate russian tutorial_transitions_1528f73f: # e «If you’re interested, check out the Transitions Gallery section of this tutorial.» e «Если вас это интересует, посмотрите на галерею переходов. Для неё отведена отдельная секция обучения.« # game/tutorial_quickstart.rpy:439 translate russian tutorial_music_8b92efb7: # e «Another important part of a visual novel or simulation game is the soundtrack.» e «Другой важной частью визуальной новеллы является звук.« # game/tutorial_quickstart.rpy:441 translate russian tutorial_music_53910317: # e «Ren’Py breaks sound up into channels. The channel a sound is played on determines if the sound loops, and if it is saved and restored with the game.» e «Ren’Py разделяет звук на каналы. Канал определяет, нужно ли играть звук циклично, и необходимо ли его сохранять и восстанавливать вместе с игрой.« # game/tutorial_quickstart.rpy:443 translate russian tutorial_music_a1e37712: # e «When a sound is played on the music channel, it is looped, and it is saved when the game is saved.» e «Когда звук играет на канале music, он будет воспроизводиться циклично и сохраняться вместе с игрой.« # game/tutorial_quickstart.rpy:445 translate russian tutorial_music_d9086d22: # e «When the channel named sound is used, the sound is played once and then stopped. It isn’t saved.» e «Звуки на канале sound играют единовременно и не сохраняются.« # game/tutorial_quickstart.rpy:447 translate russian tutorial_music_3555b640: # e «The sounds themselves are stored in audio files. Ren’Py supports the Opus, Ogg Vorbis, and mp3 formats.» e «Сами звуки хранятся в аудио-файлах. Ren’Py поддерживает такие форматы как Opus, Ogg Vorbis и mp3.« # game/tutorial_quickstart.rpy:449 translate russian tutorial_music_a776b6ad: # e «Let’s check out some of the commands that can affect the music channel.» e «Давайте посмотрим на команды, влияющие на канал музыки.« # game/tutorial_quickstart.rpy:454 translate russian tutorial_music_8b606a55: # e «The play music command replaces the currently playing music, and replaces it with the named filename.» e «Команда play music заменяет текущую музыку на другой музыкальный файл.« # game/tutorial_quickstart.rpy:456 translate russian tutorial_music_18650fe7: # e «If you specify the currently-playing song, it will restart it.» e «Если вы укажете тот же файл, что играет сейчас, воспроизведение будет перезапущено.« # game/tutorial_quickstart.rpy:458 translate russian tutorial_music_413d91fc: # e «If the optional fadeout clause is given, it will fade out the currently playing music before starting the new music.» e «Если дано условие fadeout, перед воспроизведением новой музыки, текущая будет затухать.« # game/tutorial_quickstart.rpy:463 translate russian tutorial_music_a282a0e3: # e «The queue statement also adds music to the named channel, but it waits until the currently-playing song is finished before playing the new music.» e «Операция queue добавляет музыку в очередь указанного канала и воспроизводит её по завершению текущей.« # game/tutorial_quickstart.rpy:468 translate russian tutorial_music_01ca6bad: # e «The third statement is the stop statement. It stops the music playing on a channel. It too takes the fadeout clause.» e «Третий оператор — stop. Он останавливает музыку на канале и также поддерживает условие fadeout.« # game/tutorial_quickstart.rpy:473 translate russian tutorial_music_384937da: # e «Unlike the music channel, playing a sound on the sound channel causes it to play only once.» e «В отличие от канала музыки, использование канала звука позволяет воспроизвести звуковой файл один раз.« # game/tutorial_quickstart.rpy:480 translate russian tutorial_music_1d3e9fd2: # e «You can queue up multiple sounds on the sound channel, but the sounds will only play one at a time.» e «Вы можете ставить несколько звуковых эффектов в очередь, но они будут проигрываться лишь единожды.« # game/tutorial_quickstart.rpy:486 translate russian tutorial_music_aa01c19d: # e «Ren’Py has separate mixers for sound, music, and voices, so the player can adjust them as they like.» e «Ren’Py содержит отдельные миксеры для звука, музыки и голоса, так что игрок может настроить их громкость, как захочет.« # game/tutorial_quickstart.rpy:492 translate russian tutorial_menus_0426904b: # e «Many visual novels require the player to make choices from in-game menus. These choices can add some challenge to the game, or adjust it to the player’s preferences.» e «Многие визуальные новеллы требуют от пользователя принимать решения во внутриигровых меню. Эти решения могут добавить в игру сложность или же подстроить её под предпочтения игрока.« # game/tutorial_quickstart.rpy:494 translate russian tutorial_menus_431eeff0: # e «Do you like to play visual novels with choices in them?» e «Вам нравится играть в визуальные новеллы с возможностью выбора?« # game/tutorial_quickstart.rpy:509 translate russian choice1_yes_f6d95df8: # e «While creating a multi-path visual novel can be a bit more work, it can yield a unique experience.» e «Хотя создание разветвлённого сюжета требует большой логической работы, он позволяет создать для игрока уникальный игровой опыт.« # game/tutorial_quickstart.rpy:517 translate russian choice1_no_72958b50: # e «Games without menus are called kinetic novels, and there are dozens of them available to play.» e «Игры без возможности выбора называются кинетическими новеллами и их тоже очень много.« # game/tutorial_quickstart.rpy:528 translate russian choice1_done_acba9504: # e «Here, you can see the code for that menu. If you scroll down, you can see the code we run after the menu.» e «Вот код для этого меню. Если вы пролистаете его вниз, то увидите также код, выполняемый после меню.« # game/tutorial_quickstart.rpy:530 translate russian choice1_done_f9fa6889: # e «Menus are introduced by the menu statement. The menu statement takes an indented block, in which there can be one line of dialogue and multiple choices.» e «Меню начинаются с оператора menu, после которого следует индентированный блок. В этом блоке может быть одна строка диалога на каждый выбор.« # game/tutorial_quickstart.rpy:532 translate russian choice1_done_ebb2db38: # e «Each choice must end with a colon, as each choice has its own block of Ren’Py code, that is run when that choice is selected.» e «После каждого выбора должно идти двоеточие и соответствующий ему индентированный блок кода. Итого, в нашем случае это 12 пробелов или 3 индентации.« # game/tutorial_quickstart.rpy:534 translate russian choice1_done_59cac95d: # e «Here, each block jumps to a label. While you could put small amounts of Ren’Py code inside a menu label, it’s probably good practice to usually jump to a bigger block of code.» e «В нашем случае каждый блок совершает переход к метке. Внутри меню следует использовать лишь небольшое количество кода, а при наличии большого количества текста — переходить к другим меткам.« # game/tutorial_quickstart.rpy:536 translate russian choice1_done_2851a313: # e «Scrolling down past the menu, you can see the labels that the menu jumps to. There are three labels here, named choice1_yes, choice1_no, and choice1_done.» e «Ниже меню вы можете увидеть метки, к которым меню осуществляет переход. Их три — choice1_yes, choice1_no и choice1_done.« # game/tutorial_quickstart.rpy:538 translate russian choice1_done_ff761b03: # e «When the first menu choice is picked, we jump to the choice1_yes, which runs two lines of script before jumping to choice1_done.» e «Когда мы выбираем первый вариант в меню, мы переходим к choice1_yes, который выполняет две строки кода и переходит к choice1_done.« # game/tutorial_quickstart.rpy:540 translate russian choice1_done_664fe702: # e «Similarly, picking the second choice jumps us to choice1_no, which also runs two lines of script.» e «При выборе второго варианта, мы переходим к choice1_no, который также выполняет две строки кода.« # game/tutorial_quickstart.rpy:542 translate russian choice1_done_31d12b1e: # e «The lines beginning with the dollar sign are lines of python code, which are used to set a flag based on the user’s choice.» e «Строки, начинающиеся со знака доллара — код Python. Мы используем его для создания постоянных данных (переменных или флагов), хранящих выбор пользователя.« # game/tutorial_quickstart.rpy:544 translate russian choice1_done_88398d3e: # e «The flag is named menu_flag, and it’s set to True or False based on the user’s choice. The if statement can be used to test a flag, so the game can remember the user’s choices.» e «Наша переменная названа menu_flag, и мы даём ей значение True или False, в зависимости от выбора пользователя. Оператор if можно использовать для тестирования значения переменной.« e «Так вы можете позволить своей игре запоминать выборы игроков.« # game/tutorial_quickstart.rpy:549 translate russian choice1_done_2828dbfc: # e «For example, I remember that you plan to use menus in your game.» e «Например, я помню, что вы планируете использовать меню в игре.« # game/tutorial_quickstart.rpy:553 translate russian choice1_done_503786e4: # e «For example, I remember that you’re planning to make a kinetic novel, without menus.» e «Например, я помню, что вы планируете создать кинетическую новеллу без меню.« # game/tutorial_quickstart.rpy:555 translate russian choice1_done_819e234a: # e «Here’s an example that shows how we can test a flag, and do different things if it is true or not.» e «Вот пример, показывающий тестирование постоянных данных и выполнение разных последовательностей в зависимости от его значения.« # game/tutorial_quickstart.rpy:559 translate russian choice1_done_461e6a59: # e «Finally, this shows how you can show dialogue and menus at the same time. Understand?» nointeract e «И последнее, вы можете показывать и диалог и меню в одно и то же время. Понятно?« nointeract # game/tutorial_quickstart.rpy:564 translate russian choice1_done_a32e30fd: # e «Great.» e «Отлично.« # game/tutorial_quickstart.rpy:568 translate russian choice1_done_fbd1dbc1: # e «If you look at the example, before the first choice, there’s an indented say statement.» e «Если вы посмотрите на пример, то перед первым выбором будет индентированный оператор речи.« # game/tutorial_quickstart.rpy:574 translate russian menu3_done_47fa2268: # e «Although we won’t demonstrate it here, Ren’Py supports making decisions based on a combinations of points, flags, and other factors.» e «Хотя мы не будем здесь это демонстрировать, Ren’Py поддерживает сложные процессы принятия решений, основанные на сочетаниях очков, флагов и других факторов.« # game/tutorial_quickstart.rpy:576 translate russian menu3_done_826a600b: # e «One of Ren’Py’s big advantages is the flexibility using a scripting language like Python provides us. It lets us easily scale from kinetic novels to complex simulation games.» e «Одно из больших преимуществ Ren’Py — использование гибкого языка Python. Он позволяет вам быстро вырасти из обычной кинетической новеллы до сложной игры-симулятора.« # game/tutorial_quickstart.rpy:585 translate russian tutorial_input_066611c5: # e «Some games might prompt the player for input.» e «Некоторые игры могут потребовать ввода данных от игрока.« # game/tutorial_quickstart.rpy:599 translate russian tutorial_input_dc3b4560: # e «That’s done with Python, and especially the renpy.input function. The first line of this example prompts the player for some texts, and sticks it in the name variable.» e «Это делается с помощью Python, а если точнее, то с помощью функции renpy.input. Первая строка этого примера требует от игрока какой-либо текст и вкладывает его в переменную.« # game/tutorial_quickstart.rpy:601 translate russian tutorial_input_c88b3f4e: # e «Often times, you’ll want to clean the name up before you use it. The last line does that, by calling the strip method to remove whitespace, and replacing the name with a default if it’s missing.» e «Часто бывает, что вы захотите почистить имя перед использованием. Последняя строка делает именно это, вызывая алгоритм strip, убирающий проблемы, а если имя не задано, то последняя строка также задаёт стандартное имя.« # game/tutorial_quickstart.rpy:605 translate russian tutorial_input_1236e9da: # e «To interpolate a variable, write it in square brackets. Isn’t that right, [name]?» e «Чтобы вставить (интерполировать) переменную, напишите её в квадратных скобках. Не так ли, [name]?« # game/tutorial_quickstart.rpy:609 translate russian tutorial_input_c1f7a808: # e «Variable names can also be shown in character names. To do that, just include the variable in square brackets in the character’s name. Got it?» e «Переменные также могут быть именами персонажей. Для этого просто включите переменную в квадратных скобках в имя персонажа. Понятно?« # game/tutorial_quickstart.rpy:612 translate russian tutorial_input_f7757a8e: # g «I think I do.» g «Наверное.« # game/tutorial_quickstart.rpy:619 translate russian tutorial_input_0548d3e2: # e «Variable interpolation also works with other variables. Here, the answer is [answer] and the flag is [flag].» e «Интерполяция также работает и с другими переменными. Здесь answer равен [answer], а flag — [flag].« translate russian strings: # tutorial_quickstart.rpy:2 old «Lucy« new «Люси« # tutorial_quickstart.rpy:497 old «Yes, I do.« new «Да.« # tutorial_quickstart.rpy:497 old «No, I don’t.« new «Нет.« # tutorial_quickstart.rpy:589 old «What’s your name?« new «Как вас зовут?« # tutorial_quickstart.rpy:591 old «Guy Shy« new «Товарищ«

Понравилась статья? Поделить с друзьями:
  • Как написать визитную карточку города
  • Как написать визитку школы
  • Как написать визажисту про макияж свадебный
  • Как написать визажисту про макияж правильно
  • Как написать виджет для сайта