Руководство по ренпай

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

Добро пожаловать в Руководство для начинающих по использованию движка визуальных новелл 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. Поздравляю, создание визуальной новеллы завершено.

Добро пожаловать в краткое руководство по использованию движка визуальных новелл Ren’Py. Цель данного руководства заключается в демонстрации того, как можно с нуля создать игру на движке Ren’Py, выполнив несколько простых действий. Мы разберём процесс создания игры на движке Ren’Py на примере простой игры «Вопрос».

Ren’Py Launcher[править]

Перед тем, как начать создание игры, вам, прежде всего, потребуется некоторое время, чтобы узнать, как работает Ren’Py Launcher. Модуль запуска Ren’Py позволяет создавать, управлять, редактировать и запускать проекты Ren’Py.

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

Для начала вам нужно скачать Ren’Py. Как только вы скачали Ren’Py, вам захочется извлечь и запустить его, для этого:

  • В Windows дважды щёлкните на исполняемый файл, который вы скачали. Он извлечет Ren’Py в папку с именем renpy-<version>. Вы можете перейти в эту папку и запустить renpy.exe (файл renpy.exe может быть представлен в виде renpy, если расширения скрыты).
  • В Mac OS X дважды щелкните загруженный образ диска, чтобы подключить его в качестве диска. При открытии диска скопируйте папку с именем renpy-<version> в другое место (не имеет значение куда, но папка должна быть перемещена из образа диска, доступного только для чтения. Не перемещайте приложение renpy из папки, в которой оно находится, так как оно не будет работать в другом месте). Затем перейдите в неё и запустите приложение renpy.
  • В Linux распакуйте tar-архив, перейдите в папку renpy-<version>, а затем запустите renpy.sh.

После выполнения вышеуказанного, Ren’Py Launcher должен запуститься.

Ren’Py Launcher был переведён на многие языки. Для смены языка откройте «Настройки» в правом нижнем углу и выберите язык.

Выбор и запуск проекта

Сначала вам следует увидеть, что из себя представляет завершенная игра «Вопрос». Для этого запустите Ren’Py Launcher и выберите «Вопрос» на первоначальном экране. Нажмите «Запустить проект» для запуска «Вопроса».

Вы можете вернуться к демоверсии Ren’Py, делая то же самое, но выбрав «Обучение», вместо «Вопроса».

Главный экран Ren’Py Launcher

Создание нового проекта

Создайте новый проект, выбрав «Добавить новый проект» в Ren’Py Launcher. Если вы впервые создаете проект, Ren’Py можем попросить выбрать директорию проектов. Это директория (также называемая папкой), в которой создаются новые проекты и выполняется поиск существующих проектов. Окно выбора директории может появиться под Ren’Py, так что не забудьте поискать его там.

После этого Ren’Py Launcher запросит имя проекта. Поскольку имя «Вопрос» уже занято, введите что-нибудь другое, например «My Question», и нажмите Enter.

Присвоение имени новому проекту

После этого Ren’Py Launcher попросит выбрать разрешение для проекта. По умолчанию 1280×720 – хороший компромисс между размером игры и качеством изображения. С этой целью для данного урока мы выберем 1280×720, затем нажмите кнопку «Продолжить». После Ren’Py Launcher попросит вас выбрать цветовую схему для проекта — цвет акцента и фона для нового графического интерфейса пользователя. Неважно, что вы выберете, так что просто выберите то, что вам нравится, и нажмите кнопку «Продолжить».

Выбор разрешения для проекта

Выбор акцентного и фонового цвета для темы по умолчанию

На данном этапе Ren’Py начнет обработку, которая займет некоторое время, и выдаст простой шаблон игры. В шаблоне используется изображение-заполнитель и текст, но он работоспособен и поддерживает ожидаемые функции, такие как откат, загрузка и сохранение. Выберите «Запустить проект» для запуска.

Простая игра (A Simple Game)[править]

label start:

    "Сильвия" "Привет! Как прошёл урок?"

    "Я" "Хорошо..."

    "У меня язык не поворачивается признаться, что всю лекцию профессора Эйлин я витал в своих мыслях."

    "Я" "Идешь домой? Не хочешь прогуляться со мной?"

    "Сильвия" "Конечно!"

Это очень простая игра, созданная на движке Ren’Py. Он не содержит ни изображений, ни музыки, но все же демонстрирует разговор двух персонажей и строку повествования.

Чтобы опробовать на практике вышеуказанный пример, зайдите в Ren’Py Launcher и выберите «My Question Project», а затем «script.rpy», расположенный под «Редактировать Файл». Если вы делаете это в первый раз, то Ren’Py может попросить выбрать текстовый редактор (мы рекомендуем начинающим разработчикам использовать Editra), после чего он загрузит выбранный редактор. Когда закончится загрузка, script.rpy будет открыт в редакторе.

После того, как редактор откроется, удалите всё, что находится в script.rpy, потому что мы начнем создание с нуля, и всё, что находится в файле, нам не нужно. Скопируйте приведённый выше пример в script.rpy и сохраните файл.

Теперь вы можете запустить сохраненный пример. Вернитесь в Ren’Py Launcher и выберите «Запуск проекта». Ren’Py запустится. Обратите внимание, что без каких-либо дополнительных действий, Ren’Py предоставляет меню «Загрузки» и «Сохранения» (для загрузки и сохранения игры), а также «Настройки» (для изменения различных параметров). Когда будете готовы, нажмите «Запустить проект» и поиграйте в этот пример игры.

Используемый пример демонстрирует некоторые из часто применяемых операторов в Ren’Py.

В первой строке кода находится оператор label. Оператор label используется для того, чтобы дать имя месту в программе. В данном случае, мы создаём метку, имеющую имя start. Label start – особенная метка, поскольку переход к ней происходит тогда, когда скрипты Ren’Py запускаются, как только пользователь нажмет «Начать игру» в главном меню.

Остальные строки – операторы say. В коде используется две формы оператора say. Первая форма – строка (начинается двойными кавычками, содержит последовательность символы и заканчивается двойными кавычками) на отдельной строке, которая используется для повествования и мыслей главного героя.
Вторая форма состоит из двух последовательностей символов, заключенных в двойные кавычки («). Вторая форма оператора say применяется в диалогах, причем первая строковая последовательность является именем персонажа, а вторая – то, что персонаж говорит.

Обратите внимание, что все операторы say выводятся с отступом в четыре пробела. Это из-за того, что
они входят в блок под оператором label. В Ren’Py блоки должны иметь отступ относительно предшествующего оператора и все операторы в блоке должны иметь одинаковое количество отступов.

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

    "Сильвия" "Ты когда-нибудь слышал знаменитое изречение Линкольна, "Проблема с интернет-цитатами заключается в том, что многие из них не являются подлинными.""

Хотя в этой несложной игре почти не на что посмотреть, она является примером того, как легко что-то сделать, работая в Ren’Py. Чуть позже мы добавим изображения, но прежде, давайте посмотрим, как объявить персонажей.

Персонажи (Characters)[править]

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

define s = Character('Сильвия', color="#c8ffc8")
define m = Character('Я', color="#c8c8ff")

label start:

    s "Привет! Как прошел урок?"

    m "Хорошо..."

    "У меня язык не поворачивается признаться, что всю лекцию профессора Эйлин я витал в своих мыслях."

    m "Идешь домой? Не хочешь пойти со мной?"

    s "Конечно!"

Первая и вторая строки кода определяют персонажей. Первая строка кода определяет персонажа с коротким именем «s», полным — «Сильвия», при этом имя отображается в зеленоватом цвете (цвета красный-зелёный-голубой в формате HEX, как они используются на веб-страницах).

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

Мы так же изменили в коде операторы say, чтобы использовать объекты Character вместо строки с именем персонажа. Это сообщает Ren’Py использовать персонажей, которых мы определили.

Изображения (Images)[править]

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

define s = Character('Сильвия', color="#c8ffc8")
define m = Character('Я', color="#c8c8ff")

label start:

    scene bg meadow

    "Вскоре мы дошли до луга, расположенного недалеко от района, где мы оба живем."

    "Это живописный вид, к которому я привык. Осень здесь особенно прекрасна."

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

    m "Эй... Эммм..."

    show sylvie green smile

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

    "Я спрошу её..."

    m "Эмм... Ты согласишься..."

    m "Ты станешь художником для моей визуальной новеллы?"

    show sylvie green surprised

    "Молчание."

Эта часть скрипта вводит два новых оператора:

  • оператор scene (6-я строка) очищает все изображения на экране и отображает фоновое изображение;
  • оператор show (16-я и 26-я строка) отображает спрайт поверх фона и, соответственно, изменяет отображаемый спрайт.

В Ren’Py у каждого изображения есть имя. Имя состоит из тега и, если нужно, одного или нескольких атрибутов. И тег, и атрибуты должны начинаться с буквы и содержать буквы, цифры и подчеркивания. Например:

  • В операторе scene (6-я строка) тегом является «bg», а атрибут — «meadow». В силу сложившейся традиции фоновые изображения должны использовать тег bg.
  • В первом операторе show (16-я строка) тегом является «sylvie», а атрибутами — «green» и «smile».
  • Во втором операторе show (26-я строка) тегом является «sylvie», а атрибутами — «green» и «surprised».

Одновременно на экране может отображаться только одно изображение с заданным тегом. При отображении второго изображения с этим же тегом, что и у изображения, выведенного на экран ранее, второе заменяет первое изображение, как это происходит в 26-й строке.

Ren’Py ищет файлы изображений в директории images, которую можно найти, выбрав «images» в разделе «Открыть папку» в Ren’Py Launcher. Ren’Py не делает никакого различия между спрайтом персонажа и фоновым изображением, так как они оба рассматриваются, как изображения. Ren’Py предполагает, что спрайты персонажа будут в формате PNG или WEBP, а фоновый рисунок — в формате JPG, JPEG, PNG или WEBP. Имя файла очень важно, так как расширение удаляется, имя файла задается в нижнем регистре и используется в качестве имени изображения.

Например, следующие файлы, помещенные в директорию images, определяют следующие изображения:

  • «bg meadow.jpg» -> bg meadow
  • «sylvie green smile.png» -> sylvie green smile
  • sylvie green surprised.png» -> sylvie green surprised

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

  • «Sylvie Green Surprised.png» -> sylvie green surprised

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

Оператор hide (Hide Statement)[править]

Ren’Py также поддерживает оператор hide, который скрывает заданное изображение.

label leaving:

    s "Я сделаю это!"

    hide sylvie

    "..."

    m "Это не то, что я имел в виду!"

Довольно редко, когда вам понадобится использовать оператор hide. Когда персонаж изменяет эмоции, можно использовать оператор show, кроме того оператор scene используется, когда все элементы исчезают с экрана. Следует использовать оператор hide только тогда, когда персонаж уходит, а место действия остаётся неизменным.

Оператор image (Image Statement)[править]

В некоторых случаях разработчик, возможно, не захочет, чтобы Ren’Py автоматически определял изображения. Именно для этого используется оператор image. Он должен находиться на верхнем уровне файла (без отступа и перед label start) и может использоваться для сопоставления имени изображения с файлом изображения. Например:

image logo = "renpy logo.png"
image eileen happy = "eileen_happy_blue_dress.png"

Оператор image:

  • запускается во время инициализации, перед label start и остальным игровым скриптом, который взаимодействует с игроком.
  • также может использоваться для более сложных задач, но это обсуждается в другом месте.

Переходы (Transitions)[править]

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

Переходы изменяют то, что отображается на экране от того, каким оно было в конце последнего взаимодействия (диалога, меню или перехода — в числе других операторов) на то, как оно выглядит после выполнения операторов scene, show и hide.

label start:

    scene bg meadow
    with fade

    "Вскоре мы дошли до луга, расположенного недалеко от района, где мы оба живем."

    "Это живописный вид, к которому я привык. Осень здесь особенно прекрасна."

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

    m "Эй... Эммм..."

    show sylvie green smile
    with dissolve

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

    "Я спрошу её...!"

    m "Эмм... Ты согласишься..."

    m "Ты станешь художником для моей визуальной новеллы?"

Названия переходов прописываются в коде с помощью оператора with. Часто используемый переход это dissolve (растворение одного изображения на экране в другое, следующего за ним). Другой часто используемый переход — fade (плавный переход текущего изображения в черный, а после появление нового).

Когда переход находится после нескольких операторов scene, show и hide, то он затронет все сразу. Для этого нужно прописать:

    scene bg meadow
    show sylvie green smile
    with dissolve

Оба изображения и «bg meadow», и «sylvie green smile» будут растворяться одновременно. Чтобы растворить их по одному, необходимо прописать два оператора with:

    scene bg meadow
    with dissolve
    show sylvie green smile
    with dissolve

В примере выше, сначала растворяется фон луга (bg meadow), а после спрайт Сильвии (sylvie green smile). Если вы хотите, чтобы мгновенно отобразился луг, а затем появилась Сильвия, то пропишите так:

    scene bg meadow
    with None
    show sylvie green smile
    with dissolve

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

Позиционирование (Positions)[править]

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

     show sylvie green smile at right

Для этого добавьте функцию at к оператору show. Функция аt занимает позицию и показывает изображение на этой позиции.

Ren’Py включает в себя несколько предварительно определенных позиций:

  • left – расположение в левой части экрана;
  • right – расположение в правой части экрана;
  • center – расположение горизонтально по центру (по умолчанию);
  • truecenter – для центрирования по горизонтали и вертикали.

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

Музыка и звуки (Music and Sound)[править]

В большинстве игр музыка воспроизводится на заднем фоне. Музыка воспроизводится с помощью оператора play music.
Оператор play music принимает имя файла, который рассматривается в качестве аудиофайла для воспроизведения. Анализируются имена аудиофайлов, относящиеся к директории игры. Аудиофайлы должны быть в opus, ogg vorbis или mp3 формате. Например:

    play music "illurock.ogg"

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

    play music "illurock.ogg" fadeout 1.0 fadein 1.0

Оператор queue music воспроизводит аудиофайл после завершения воспроизведения текущего файла.

    queue music "next_track.opus"

Воспроизведение музыки может быть остановлено с помощью оператора stop music, который также, при необходимости, может использовать fadeout.

Звуковые эффекты могут воспроизводиться с помощью оператора play sound. В отличии от музыки, звуковые эффекты не зацикливаются.

Оператор pause (Pause Statement)[править]

Оператор pause вызывается Ren’Py, чтобы остановить игру, пока пользователь не нажмёт кнопку мыши.

Если после оператора pause задаётся число (количество секунд), пауза завершится, как только заданное количество секунд истечет.

Завершение игры (Ending the Game)[править]

Вы можете завершить игру, запустив оператор return без дополнительных вызовов. Прежде чем сделать это, лучше всего добавить что-то в игру, чтобы сообщить игроку, что игра заканчивается и, возможно, дать концовке номер или название.

    ".:. Хорошая концовка."

    return

Это всё, что вам нужно, чтобы сделать кинетическую новеллу – визуальную новеллу без каких-либо выборов.

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

Меню, Метки и Прыжки (Menus, Labels, and Jumps)[править]

Оператор menu позволяет игроку сделать выбор:

    s "Конечно, но что такое "визуальная новелла?""

menu:

    "Это видеоигра.":
        jump game

    "Это интерактивная книга.":
        jump book

label game:

    m "Это своего рода видеоигра, в которую можно играть на компьютере или консоли."

    jump marry

label book:

    m "Это как интерактивная книга, которую можно читать на компьютере или консоли."

    jump marry

label marry:

    "И так мы стали командой по разработке визуальных новелл."

В этом примере показано, как можно использовать меню с Ren’Py. Оператор menu вводит внутриигровой выбор. Для этого требуется блок с отступом строк, каждая из которых состоит из последовательности символов, за которой следует двоеточие. Это выборы пунктов меню, представленные игроку. Каждому выбору пункта меню требуется свой собственный блок с отступом строк, который запускается при выборе пункта меню.

В этом примере каждый из двух выборов пункта меню запускает один оператор jump. Оператор jump передает управление на метку, определенную с помощью оператора label. После перехода запускаются операторы скрипта, следующие за меткой.

В примере выше, после того, как Сильвия задала свой вопрос, перед игроком появляется меню, содержащее два варианта. Если игрок выберет вариант «Это видеоигра.», запускается первый оператор jump и Ren’Py переходит к метке game. Это приведет к тому, что POV-персонаж скажет: «Это своего рода видеоигра, в которую можно играть на компьютере или консоли.», после чего Ren’Py переходит к метке marry.

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

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

Поддержка флагов с помощью оператора default, операторы Python и if (Supporting Flags using the Default, Python and If Statements)[править]

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

Здесь мы покажем, как хранить флаг, содержащий информацию о выборе игрока. Чтобы инициализировать флаг, используйте оператор default перед label start.

# Принимает значение True, если игрок решил сравнить VN с книгой.
default book = False

label start:

    s "Привет! Как прошёл урок?"

Флаг book начинает инициализироваться со специальным значением False (как и для остальной части Ren’Py, имеет значение капитализация), что означает, что он не установлен. Если выбирается путь к книге, то мы можем установить значение True с помощью оператор присваивания в Python.

label book:

    $ book = True

    m "Это как интерактивная книга, которую можно читать на компьютере или консоли."

    jump marry

Строки, начинающиеся со знака доллара, интерпретируются как операторы в Python. Оператор присваивания здесь присваивает значение переменной. Ren’Py поддерживает другие способы включения Python, например многострочный оператор, который рассматривается в других разделах этого руководства.

Для проверки флага, используйте оператор if:

if book:

    "Наша первая игра основана на одной из идей Сильвии, но потом я тоже придумывал свои истории."

Если условие принимает истинное значение, то запускается блок скрипта. Если нет, блок пропускается. Оператор if может также принимать выражение else, которое вводит блок скрипта, который выполняется, если условие принимает ложное значение.

if book:

    "Наша первая игра основана на одной из идей Сильвии, но потом я тоже придумывал свои истории."

else:

    "Сильвия помогла со сценарием нашей первой видеоигры."

Переменные Python не обязательно должны принимать простые значения True/False. Переменные могут использоваться для хранения имени игрока, количества набранных очков и для многих других целей. Поскольку Ren’Py включает в себя возможность использования полного языка программирования Python, многое стало возможным.

Выпуск игры (Releasing Your Game)[править]

После того как вы сделали игру, есть несколько вещей, которые нужно сделать перед её выпуском:

Проверить наличие новой версии Ren’Py
Новые версии Ren’Py выпускаются на регулярной основе для исправления ошибок и добавления новых функций. Перед выпуском игры необходимо нажать кнопку «обновить» в Ren’Py Launcher, чтобы обновить Ren’Py до последней версии. Можно также загрузить новые версии и просмотреть список изменений на странице https://www.renpy.org/latest.html.
В редких случаях изменения в Ren’Py потребуют от вас внести изменения в скрипт игры. Со списком этих изменений можно ознакомиться в разделе «Несовместимые изменения».
Проверить скрипт
На главной странице Ren’Py Launcher выберите «Проверить Сценарий (Lint)». Это позволит проверить ваши игры на наличие возможных ошибок. Поскольку некоторые из этих ошибок повлияют только на пользователей других платформ, важно исправить все ошибки, даже если на вашем компьютере они не заметны.
Построить дистрибутивы
На главной странице Ren’Py Launcher нажмите «Построить дистрибутивы». Основываясь на информации из options.rpy, Ren’Py Launcher создаст один или несколько архивов, содержащих вашу игру.
Тестирование
«Проверить Сценарий» не заменит тщательное тестирование. Проверить игру до её выпуска – ваша обязанность. Подумайте о том, чтобы попросить друзей помочь с бета-тестированием вашей игры, так как часто тестер может найти проблемы, которые вы не можете.
Выпуск игры
После того как игра завершена и протестирована, вам следует разместить созданные архивные файлы в Интернете, где люди могут их увидеть (если у вас нет собственного сайта, то на https://itch.io много визуальных новелл). Поздравляю, вы выпустили свою первую визуальную новеллу!
Есть несколько мест, где можно разместить игру:

  • Список Ren’Py помогает нам отслеживать игры, созданные на Ren’Py.
  • Раздел «Завершенные игры» на форуме Lemma Soft — хорошее место, чтобы рассказать коллегам-создателям о вашей игре.

Скрипт визуальной новеллы «Вопрос» (Script of The Question)[править]

Скрипт новеллы разделен на два файла:

  • script.rpy содержит основное тело игры.
  • options.rpy содержит настройки, с помощью которых можно изменить отображение составляющих игры.

Что же нам делать дальше? (Where do we go from here?)[править]

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

Чтобы понять, на что способен Ren’Py, рекомендуем поиграть в «Обучение» и, возможно, некоторые игры, представленные на веб-сайте Ren’Py. Вы также можете прочитать остальную часть этого руководства, начиная с руководства по настройке графического интерфейса пользователя

В Энциклопедия Ren’Py в разделе «FAQ» вы можете найти ответы на самые распространенные вопросы по Ren’Py, а в разделе «Книга рецептов» – отрывки полезного кода.

Кроме того, мы рекомендуем проверить раздел Ren’Py на форуме Lemma Soft, в котором есть раздел для вопросов, а также раздел с книгой рецептов с библиотеками и примерами, предназначенными для повторного использования. На форуме Lemma Soft находится главный центр сообщества Ren’Py, где мы рады приветствовать новых создателей новелл и их вопросы.

Благодарим вас за выбор движка визуальных новелл Ren’Py. Мы с нетерпением ожидаем того, что вы с ним создадите!

Источник[править]

Статья «Quickstart» на английском языке.


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

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

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

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

Основы, картинки, диалоги

Урок 1: Подготовка, необходимые программы.

Готовимся к работе и скачиваем необходимые программы: движок, редактор кода, графические программы и другие.

Все программы бесплатные. На работе с ними мы коротко остановимся в ходе курса.

Урок 2: Бесплатные спрайты, музыка, звуки и другие ресурсы.

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

Легче всего найти подходящую музыку и звуковые эффекты. Фоны и спрайты персонажей крайне рекомендуется создавать своими силами.

Основные лицензии под которыми распространяются бесплатные ресурсы:

  • CC0 — никаких ограничений на использование;

  • CC BY — нужно указывать автора;

  • CC BY-SA — обязательно распространять свою работу под этой же лицензией;

  • CC BY-ND — нельзя изменять ресурсы;

  • CC BY-NC — запрещено продавать или зарабатывать иным образом на работе.

Подробнее о лицензиях Creative Commons можно узнать на официальном сайте.

Тест 1: Лицензии и бесплатные ресурсы

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

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

Этот тест поможет вам разобраться в лицензиях Creative Commons.

Урок 3: Первое знакомство с Ren’Py.

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

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

Знакомимся со структурой игры в Ren’Py и самыми важными файлами.

Основные файлы и папки проекта «Ren’Py»

  • Папка Game — здесь расположены папки и скрипты вашей новеллы;

  • Папка Images — картинки, спрайты персонажей, фоны;

  • Папка Gui — картинки интерфейса;

  • script.rpy — основной файл сценария;

  • options.rpy — настройки игры;

  • gui.rpy — файл для изменения интерфейса;

  • screens.rpy — галерея, внутриигровые меню и другие элементы интерфейса.

Урок 4: Создание проекта.

Наконец-то, приступаем к созданию визуальной новеллы. Сперва создадим проект и зададим его основные настройки. Заодно познакомимся с некоторыми основами работы с редактором кода, если вы до этого не программировали.

Вам понадобится программа Notepad++ или другой редактор кода.

Урок 5: Персонажи и реплики.

Визуальная новелла обычно не обходится без диалогов. Поэтому первое чему стоит научиться — это, как заставить ваших героев разговаривать.

В этом уроке мы начинаем постигать азы программирования в «Ren’Py».

# Всё после символа решетки и до конца строки — это комментарий

# Ren’Py полностью игнорирует комментарии

# Используйте их для заметок и временного отключения строк

# Создаём персонажа с именем Димон, его имя будет выводиться светло-синим цветом

define d = Character(‘Димон’, color=»#4ca6ff»)

# Новая игра начинается после этой метки

label start:

    # Укажите название переменной персонажа («d») и после пробела его слова в кавычках.

    d «Реплика Димона.»

    # Если не указывать персонажа, то получим слова автора.

    «Слова автора.»

    # Эта команда завершает игру и переходит в главное меню.

    return

На самом деле команда «return» не обязательно завершит игру. Она выходит из текущей метки и возвращает в предыдущую. Но пока не думайте об этом. Мы вернемся к этой теме в будущих уроках.

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

До этого наши герои были невидимыми. Для визуальной новеллы это явно нехорошо. Пора научиться отображать наших героев на экране. По пути заменим скучный серый фон.

# Очистить всю сцену и задать новый фон.

scene bg name

with fade # Переход через чёрный цвет

# Выведется картинка с именем «bg name».

# Отобразить спрайт. Поверх фона выведется картинка «dimon smile».

show dimon smile

# Убрать спрайт. Уберется любой спрайт, который начинается со слова «dimon».

hide dimon

В этой папке находятся спрайты персонажей и фоны, которые вы можете использовать для тренировки. Если будете распространять свою работу, то не забудьте указать авторов из файла «Автор.txt».

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

Урок 7: Размер фона. Прозрачность спрайтов.

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

Кроме того разберемся, что делать в ситуации, когда ваши герои изображены на белом фоне. Для этого нам понадобятся программы Gimp и FastStone Image Viewer.

Среди файлов из урока 6 есть папка «Неправильные картинки». Файл «agustina smile.png» в ней имеет белый фон. Если хотите, потренируйтесь убирать его. Файл «saki normal.png» и фоны «bg room.jpg» и «bg street.jpg» не соответствуют по размеру остальным картинкам. Приведите их в нормальный вид, изменив размер.

Урок 8: Изменение расположения спрайтов.

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

# Расположение картинки слева

show dimon smile

at left   # или right/top

# Точное расположение по горизонтали и вертикали

show dimon smile:

    xalign 0.5

    yalign 0.5

#  Особая метка, выполняется при старте новой игры

init: 

# Создаём своё расположение спрайта

$ mypos = Position(xalign=0.5, yalign=0.5)

# $ строка с любым кодом Python

Урок 9: Быстрое изменение спрайта персонажа в диалоге.

Обычный способ отображения картинок можно упростить в большинстве случаев. Мы научимся быстро менять спрайт персонажа прямо в диалоге.

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

# Изменение спрайта персонажа прямо в реплике.

d smile «Привет, я Димон.»

d angry «Я очень злой!»

# Изменить спрайт на одну реплику, после неё вернуть прошлый спрайт

d @ smile «Привет, я Димон.»

d @ angry «Я очень злой!»

Самый простой способ оживить новеллу — это добавить переходы при смене фона или другого изображения. В «Ren’py» есть достаточно много встроенных переходов, которые очень просто использовать.

# Показать спрайт персонажа с плавным переходом

show dimon smile

with dissolve

# Скрыть спрайт персонажа с плавным переходом

hide dimon

with dissolve

# Один переход можно указать сразу после нескольких картинок.

# Тогда эти картинки появятся с переходом одновременно.

show dimon smile

show alice angry at right

with dissolve

# Точно так же переходы можно использовать при смене сцены.

scene bg class

with fade

# Можно указать сцену и персонажей с одним переходом.

scene bg class

show dimon smile

show alice angry at right

with fade

Ссылка на информацию о переходах на официальном сайте РенПай.

Поэкспериментируйте с разными переходами самостоятельно.

Данный тест закрепит основные понятия и команды по работе с фонами и спрайтами.

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

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

Эта статья посвящена техническим моментам реализации новеллы при помощи инструментов движка 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 строк кода, реализующих первые шесть сцен игры — и, если бы не грамотная организация процесса, я давно потерялся бы в этих дебрях.

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

Понравилась статья? Поделить с друзьями:
  • Основит стяжка пола армированная инструкция по применению
  • Инструкция по электробезопасности для неэлектротехнического персонала i ой квалификационной группы
  • Лизалор инструкция по применению таблетки цена
  • Ancom a7 руководство
  • Темпалгин показания к применению инструкция таблетки взрослым