Глава 5. Погружаемся глубже

Дойдя до этой главы, вы уже усвоили базовые теоретические и практические знания по Ren’Py, Twine и TyranoBuilder. Теперь познакомимся с более сложными инструментами, которые имеются в рассматриваемом программном обеспечении. Например, разберём, как интегрировать видео в Ren’Py, как улучшить игры на Twine при помощи каскадных таблиц стилей (CSS), и как создать произвольный диалог в TyranoBuilder.

Ren’Py, контейнеры и кодеки

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

Видео (и аудио) файл обычно состоит из двух компонентов это контейнер и кодек. Контейнер — это файл, который содержит в себе видео, аудио и субтитры. В свою очередь кодек является инструментом для сжатия и распаковки видеофайла внутри контейнера. Существует многое количество кодеков, которые различаются между собой качеством и коэффициентом сжатия. Самыми популярными форматы контейнеров являются .ogg, .webm и .avi, и соответственно они имеют поддержку на многих платформах как на настольных, так и на мобильных.

Что касается кодеков, то хорошим выбором является H.264 (известный как MPEG-4), MPEG-2 и MPEG-1. Данный формат был разработан в 1988 году Хироси Ясудой и Леонардо Кьярильоне, а аббревиатура MPEG (Moving Picture Experts Group) расшифровывается как Экспертная группа по движущимся изображениям.

Давайте, используя таблицу 5-1, сравним самые распространённые кодеки:

Таблица 5-1. Наиболее распространенные видеокодеки

КодекЛицензияДата релизаОсобенности
VP9Свободная2012Поддержка видео высокого разрешения (например, 4K/8K UHD), опция сжатия без потерь
VP8Свободная2008Поддержка многопроцессорности. Свободный доступ для редактирования видео. Лучше всего использовать для видео с низким разрешением
TheoraСвободная2004Поддерживает потоковую передачу видео через Интернет, достоянная картинка при низком битрейте
H.264 (MPEG-4)Бесплатная для использования в Интернете2003Эффективен для потокового видео через Интернет, поддерживает разрешение до 8192 × 4320 (включая 8K UHD)
Xvid, бесплатная реализация MPEG-4 part 2Свободная (Универсальная общественная лицензия GNU)2001Оптимизирована производительность, широкая поддержка
MPEG-2Все американские патенты MPEG-2 истекли 13 февраля 2019 года1996Формат золотой стандарт DVD, может включать объемный звук 5.1
MPEG-1Свободная (срок действия патентов истек)1989Хорошо работает с видео с низким битрейтом (например, для небольших дисплеев), принимая во внимание как качество изображения, так и небольшой размер файла.

Теперь при помощи таблицы 5-2 сравним форматы контейнеров:

Таблица 5-2. Наиболее распространенные видео контейнеры

Формат контейнераЛицензияДата релизаПоддерживаемые кодеки
WebMСвободная2010Видео VP8 и VP9, аудио Ogg Vorbis и Opus audio
MatroskaСвободная2002Любой
OggСвободная1993Видео Theora и Dirac. Аудио Opus, Vorbis и Speex
MPEG-контейнер (не путать с MPEG-кодеками)Ограниченна1993Видео MPEG-1 и MPEG-2. Аудио MPEG-1 Layers 1, 2, и 3 (то есть mp3)
AVIОтносительно свободная1992В основном содержит несжатое видео (Full Frame), Intel Real Time (Indeo) и Cinepak

Видео в Ren’Py

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

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

$ renpy.movie_cutscene("introvideo.webm")

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

Давайте рассмотрим пример:

    image markus movie = Movie(play="markus_movie.webm", mask="markus_mask.webm")
    show markus movie
    "Разве это не весело?"
    hide markus movie

Данный код в первой строке определяет видео спрайт (movie sprite) с именем "markus", которому присваивается видеофайл markus_movie.webm. Далее командой show воспроизводиться видео. Потом выводиться диалог, а затем видео скрывается командой hide.

Как мы знаем видео имеет квадратную или прямоугольную форму и не имеет прозрачности. Чтобы скрыть нежелательные края или фон в видео спрайте во время вызова метода Movie указывается параметр mask (маска). Маска – это видео такого же размера и продолжительности, что и видео, с которым она связана.

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

Расширенные функции аудио в Ren’Py

Во время разработки игры возникает потребность одновременно воспроизводить фоновую музыку, звуковые эффекты, голоса персонажей. Для это в Ren'Py используется подход, при котором один аудиоканал используется для воспроизведения музыки, второй для звука, и ещё один для голоса. Каждый из этих каналов воспроизводит по одному аудиофайлу за раз. Если вам потребуется дополнительный аудиоканал вы можете его создать. Как это сделать будет показано далее в книге.

Нижеприведённом примере используются сразу все три канала для одновременного воспроизведения песни, звукового эффекта и диалога.

    play music "song1.ogg"
    play sound "boom.mp3"
    voice "dialogue1.ogg"
    "Привет, как ты?"

Ren’Py поддерживает .wav, .ogg, .mp3 и Opus форматы. Из них формат .wav (который является стандартным аудио форматом для компакт-дисков), пожалуй, самый громоздкий, так как не имеет сжатия, его не рекомендуется для использования в релизной версии игры. Никто не любит, когда игра занимает много дискового пространства, а особенно когда она распространяется через Интернет (то есть будет долго загружаться). Наилучшее применение .wav формата – это когда звук находится на стадии обработки, и только после выполнения всех работ, его можно сохранить в формат с элементом сжатие. Имейте виду, при сжатии идёт потеря качества аудиодорожки, в результате вы получаете копию, а не оригинал. Если это повторить несколько раз, то качество звука сильно упадёт.

Аудио очереди

Игра будет более приятно восприниматься, когда в место одной короткой повторяющейся мелодии будет плейлист с разнообразной музыкой. Это можно легко сделать в Ren’Py при помощи метода queue. В примере показано, как создается очередь из трех мелодий (в формате .ogg) для музыкального канала.

queue music [ "song1.ogg", "song2.ogg", "happysong.ogg" ]

Параметры операторов воспроизведения и остановки

Оператор play не ограничивается простым открытием файла и его воспроизведением. Например, вы можете передать в него очередь аудиофайлов, задать затухание в начале и в конце.

play music [ "song1.mp3", "song2.mp3" ] fadeout 2.0 fadein 2.0

Если вы хотите остановить воспроизведение музыкального канала с затуханием длиной в 10 секунд, то нужно в операторе stop указать следующие параметры:

stop music fadeout 10.0

Если вам когда-нибудь понадобится вставить паузу перед воспроизведением, то воспользуйтесь тегом silence (тишина) с параметром длительность тишины в секундах.

play audio [ "<silence 2.5>", "song1.mp3" ]

Воспроизводим произвольную часть аудиофайла

Ren’Py при помощи оператора from позволяет прослушать часть аудиофайла. В приведённом примере воспроизводиться аудио song1.mp3 с 2,5 секунды до 10,5 секунды.

play music "<from 2.5 to 10.5>song1.mp3"

Подробней о графике

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

Цвет диалога

В предыдущей главе мы уже встречались с инициализацией персонажа и это выглядело так:

$ m = Character('Маркус', color="#EE1100")

Но в этот раз мы указали свойство color, которое задает цвет диалога персонажа в виде шестнадцатеричного значения. Оно в свою очередь состоит из красного, зеленого и синего цвета (т. е. RGB). Первые два символа после хэштега (#) задают значение красного цвета, третий и четвертый задают значение зеленого, а остальные символы задают значение синего цвета. Так как шестнадцатеричная система в качестве цифр счисления использует цифры от 0 до 9 и латинские буквы от A до F, то мы видим буквенные значения в определении цвета.

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

Давайте познакомимся с наиболее распространёнными цветами типа RGB (см. Таблице 5-3).

Таблица 5-3. Наиболее популярные RGB цвета

ЦветШестнадцатеричное значение
Черный#000000
Белый#FFFFFF
Серый#808080
Зеленый#008000
Синий#0000FF
Желтый#FFFF00
Красный#FF0000
Фиолетовый#800080
Апельсин#FFA500
Берливуд#DEB887

Выравниваем изображения

Если вам нужно отцентрировать изображение в независимости от позиции в пикселях, то укажите следующие параметры xalign и yalign. Параметр xalign задает выравнивание по горизонтали, если указать 0,0 — изображение выровняется по левому краю, если указать 0,5 — то по центру, если 1,0 — то по правому краю. Соответственно параметр yalign выполняет выравнивание по вертикали, если указать 0,0 – выполниться выравнивание по верхнему краю, 0,5 — по центру, а значение 1,0 выровняет изображение по нижнему краю экрана.

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

image man right = Image("man.png", xalign=1.0, yalign=1.0)

Добавляем переходы

Ren’Py имеет набор переходов между сценами. Чтобы их применить используется функция with. Если нужно настроить переходы для глобальных элементов игры используйте конфигурационные файлы gui.rpy или options.rpy.

Рассмотрим пример использования перехода:

show bg garage
with dissolve
"Черт возьми! Эрни! Мы в гараже!"

Как видим скрипт выводит фоновое изображение гаража, используя классический переход dissolve (растворение).

Теперь давайте рассмотрим какие есть ещё переходы в Ren’Py (см. Таблицу 5-4).

Таблица 5-4. Основные визуальные переходы в Ren’Py

ПереходОписание
dissolveИзображение исчезает или показывается с затуханием
fadeИзображение, темнея исчезает или появляется из темноты
pixellateИзображение пикселизируется при исчезновении или депикселизируется при появлении
blindsСмена сцены при помощи эффекта "вертикальные жалюзи"
zoominНовое изображение появляется с эффектом увеличения
zoominoutИзображение исчезает с эффектом уменьшения
moveАнимирует перемещение изображения изменившее местоположение
slideawayleft, slideawayright, slideawayup, slideawaydownСдвигает старую сцену в заданном направлении
slideleft, slideright, slideup, slidedownСдвигает новую сцену в заданном направлении
VpunchПри вызове этого перехода встряхивает экран в вертикальном направлении
HpunchПри вызове этого перехода встряхивает экран в горизонтальном направлении
easeinleft, easeinright, _easeintop, easeinbottomПохож на move, за исключением того, что скорость движения строится на основе кривой косинуса
irisin, irisoutПоказывает новое изображение или скрывает текущее с помощью прямоугольника
SquaresПереход изображения квадратами
wipeleft, wiperight, wipeup, wipedownСтирает изображение в указанном направлении
moveoutright, moveoutleft, moveouttop, moveoutbottomПеремещает изображение за пределы экрана
moveinright, moveinleft, moveintop, moveinbottomПереход выводит изображения на экран с соответствующей стороны

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

Настройка графического интерфейса в Ren’Py

Все элементы графического интерфейса (GUI) в Ren’Py можно настроить, отредактировав файл gui.rpy. Доступ к нему можно получить через лаунчер в разделе "Редактировать файл". Этот текстовый файл содержит множество настроек, давайте рассмотрим некоторые из них (см. Таблицу 5-5).

Таблица 5-5. Полезные опции в файле gui.rpy

ОпцияОписание
define gui.text_sizeРазмер текста диалога. По умолчанию: 22
define gui.text_colorУстанавливает цвет текста диалога. По умолчанию: белый (#ffffff)
define gui.textbox_heightУстанавливает высоту диалогового окна. По умолчанию: 185 пикселей
define gui.name_text_sizeЗадаёт размер имен персонажей. По умолчанию: 30
define gui.text_fontОпределяет внутриигровой шрифт. Файл со шрифтом должен находиться в папке с игрой. По умолчанию: DejaVuSans.ttf
define gui.main_menu_backgroundЗадаёт фоновое изображение главного меню игры. По умолчанию: "gui/main_menu.png"
define gui.game_menu_backgroundЗадаёт фоновое изображение игровом меню игры. По умолчанию: "gui/game_menu.png"
define gui.title_text_sizeУстанавливает размер заголовка игры. По умолчанию: 50
define gui.interface_text_sizeУстанавливает размер текста в пользовательском интерфейсе. По умолчанию: 22
define gui.label_text_sizeУстанавливает размер заголовка в пользовательском интерфейсе. По умолчанию: 24

Файл gui.rpy — это не единственный файл, который позволяет настроить работу Ren’Py, ещё имеется options.rpy. Его так же можно открыть через лаунчер в разделе "Редактировать файл". Давайте разберём некоторые настройки из этого файла и что они значат для визуальной новеллы (см. Таблицу 5–6).

Таблица 5-6. Полезные опции в файле options.rpy

ОпцияОписание
define gui.show_nameОпределяет, показывать ли заголовок игры, на экране главного меню. По умолчанию: True
define config.has_soundПоказывает или скрывает звуковой микшер. По умолчанию: True
define config.has_musicПоказывает или скрывает микшер музыки. По умолчанию: True
define config.has_voiceПоказывает или скрывает микшер голоса в игре. По умолчанию: True
default preferences.text_cpsКонтролирует скорость вывода текста. По умолчанию: 0 — мгновенно. Число больше нуля — это количество печатаемых символов в секунду.
default preferences.afm_timeУстанавливает задержку при авточтении. По умолчанию: 15. Допустимый диапазон значений от 0 до 30.
define config.window_iconИконка значка на панеле задач и рабочем столе. По умолчанию: "gui/window_icon.png"
define config.intra_transitionЗадаёт типом перехода между экранами игрового меню. По умолчанию: dissolve
define config.enter_transition, define config.exit_transitionУправляет типом визуального перехода при входе и выходе из игрового меню. По умолчанию: dissolve
define config.after_load_transitionУстанавливает эффект перехода после загрузки игры. По умолчанию: None

Продвинутые функции TyranoBuilder

Хотя TyranoBuilder кажется простым инструментом разработки, но на самом деле под его капотом скрываются мощные методы для создания игр. Давайте познакомимся с некоторыми из них.

Плагины

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

Примечание:

Поддержка плагинов в TyranoBuilder была добавлена с версии 182.

Познакомится с плагинами можно на странице: https://plugin.tyrano.jp/open in new window или на https://plugin.tyrano.jp/enopen in new window (английская версия).

Чтобы добавить плагин в TyranoBuilder, сперва его нужно загрузить с указанного выше ресурса. Затем в главном меню выбираем Project\Plugins (Проект\Плагины), после чего откроется диалоговое окно «Плагины». Далее нажимаем кнопку Add New (Добавить новый), и выбираем ранее загруженный файл с расширением .tbp.

Для того чтобы воспользоваться встроенными и загруженными плагинами в главном меню выбираем Project\Customize Tool Area (Проект\Настройка области инструментов). В появившемся диалоговом окне на вкладке Components (Компоненты) находим Plugins и ставим флажок. После нажатия кнопки Apply (Применить) на вкладке с компонентами появится новый раздел Plugins (Плагины).

Хотя плагинов может существовать разное множество, давайте познакомимся с некоторыми из них (см. Таблицу 5-7).

Таблица 5-7. Плагины TyranoBuilder и назначение

ПлагинОписаниеURL
Background Mask TransitionsПлагин позволяет выполнять смену фона с использованием маски. Содержит в себе множество различных переходовhttps://plugin.tyrano.jp/item/20007
Mask TransitionsПлагин позволяет добавить переходы на основе масок для персонажей. В комплекте 200 масок.https://plugin.tyrano.jp/item/20014
Sleep and AwakeПозволяет игроку сохранить её и начать другой квест, а затем по своему желанию вернуться к исходному приключению.https://plugin.tyrano.jp/item/20001
Custom Save Game ThumbnailПозволяет установить миниатюры для сохранений игры вместо захваченных TyranoBuilder.https://plugin.tyrano.jp/item/20002
Auto Save and LoadДобавляет компоненты, которые могут в игре выполнять автозагрузку и автосохранение.https://plugin.tyrano.jp/item/20003
Show DialogueВыводит диалоговое окно подтверждения поверх всех элементов игры.https://plugin.tyrano.jp/item/20009
Change TitleПозволяет изменить заголовок окна игры. Например, можно использовать для отображения названия каждой главы новеллы.https://plugin.tyrano.jp/item/20010
Screen FiltersДобавляет компонент, который накладывает фильтр на экран игры. Включает в себя эффект размытия, прозрачности, оттенки серого, сепии, инверсию тонов.https://plugin.tyrano.jp/item/20005
Open WebsiteПозволяет запустить браузер и открыть в нем веб-сайт.https://plugin.tyrano.jp/item/20008
Sprite SheetsОбеспечивает поддержку спрайтовой анимации. Имеет настройки: скорость воспроизведения, зацикливание и размер спрайтов.https://plugin.tyrano.jp/item/20012

Переменные

Переменные — это важная часть любой визуальной новеллы. Благодаря им вы можете хранить количество золота, которое имеет игрок или определить какое впечатление производит он или она друг на друга на основе их интересов. Для работы с переменными в TyranoBuilder имеется Variables Manager (Диспетчер переменных). Чтобы его открыть, выберите в главном меню Project\Variables (Проект\Переменные) (см. рис. 5-1). Сами переменные записываются в сохранении игры.

Рисунок 5-1. Диспетчер переменных в TyranoBuilder

Рисунок 5-1. Диспетчер переменных в TyranoBuilder

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

Чтобы выполнить операции над переменными в TyranoBuilder имеется группа компонентов System (Система), в которую входят три компонента: Process Variable (Операция над переменной), Input Box (Поле ввода) и Commit Input (Зафиксировать ввод). Основным компонентом является Process Variable (см. рис. 5-2), который позволяет выполнить арифметические операции над переменной или присвоить ей случайное значение.

Рисунок 5-2. Панель параметров компонента Process Variable

Рисунок 5-2. Панель параметров компонента Process Variable

Теперь, чтобы переменные повлияли на историю, достаточно воспользоваться компонентом Jump из группы компонентов Story, и в параметрах компонента поставить флажок возле "Jump if conditional is TRUE". Как показано на рисунке 5-3.

Рисунок 5-3. Панель параметров компонента Process Variable

Рисунок 5-3. Панель параметров компонента Process Variable

Как видим в добавленном компоненте перехода настроен переход на сцену scene2 к метке Room, но он выполниться только при условии, что переменная gold (золото) будет больше 10.

Для создания поля ввода, например, получить имя игрока, используются компонент Input Box и Commit Input. Для этого нужно перетянуть Input Box на сцену, а в параметрах задать имя переменной в поле Assign Variable (Назначить переменную). Используя инструмент позиционирования, можно задать нужное положения поля на сцене. Чтобы зафиксировать ввод данных и присвоить их переменной, перетяните на сцену компонент Commit Input.

Случайный диалог

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

Для этого нам понадобиться компоненты iScript и TyranoScript.

Откройте проект с прошлой главы или создайте новый. Далее, перетащите компонент iScript поверх компонента "Text". Затем добавим компонент TyranoScript между созданным компонентом iScript и компонентом Text. Теперь скопируем следующий код в iScript.

itemArray = [
  "яблоко",
  "ключи",
  "огурец",
  "федору",
  "наушники",
  "картошку"
];

randomItem = itemArray[Math.floor(Math.random()*itemArray.length)];

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

Далее мы определяем переменную randomItem, и помещаем в неё случайно выбранное значение из массива itemArray. Для этого благодаря функции Math.random() генерируем случайное число от 0 до 1 и умножаем на длину массива itemArray.length, в результате получим значение от 0 до 5 в виде цифры с плавающей запятой, например, "4,34234". Чтобы это исправить воспользуемся функцией Math.floor, которая округлит число до 4.

Теперь, вставим следующую строку кода в компонент TyranoScript:

[eval exp="randomitem_text=randomItem"]

Как видим, используя тег eval exp создаётся переменная randomitem_text, которой присваивается значение randomItem. То есть мы приводим неизвестный тип переменной randomItem к строковому типу. Это делается из-за того, что экран отображает только строки.

И в конце мы вставляем randomitem_text в компонент Text, как показано в примере ниже:

Привет!
О, нет! Я потерял [emb exp="randomitem_text"]!

Запускаем проект, и на экране отобразится что-то вроде «Привет! О, нет! Я потерял огурец!».

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

Если задуматься об оптимизации скрипта, то шаг с компонентом TyranoScript можно пропустить, а в компоненте Text указать следующий код:

Привет!
О, нет! Я потерял [emb exp="randomItem"]!

iScript в сравнении с JavaScript

Глобальные переменные в JavaScript объявляются при помощи ключевого слова var, а так как это язык с динамической типизацией, то тип определяется автоматически.

var myName = "Тим";
var myAge = 54;

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

f.myName = "Тим";
f.myAge = 54;

Запомните, iScript — это, по сути, JavaScript, а TyranoScript – это язык разметки с возможностью интеграции тегов.

Кликабельные области изображения

В TyranoBuilder на фоновом изображении можно создать кликабельные области. Например, у вас на изображении есть дверь, по которой выполнив щелчок, игрока перенесёт на другую сцену. Так как кликабельные области не имеют своего изображения они всегда используются в комбинации с компонентом "Background Image (Фоновое Изображение)".

Рисунок 5-4. Инструмент позиционирования кликабельной области. В данном случае книжная полка определена как кликабельная область.

Рисунок 5-4. Инструмент позиционирования кликабельной области. В данном случае книжная полка определена как кликабельная область.

Чтобы добавить кликабельную область, перетащите компонент "Clickable Area (Кликабельная область)" под компонент фонового изображения. Далее, в параметрах вызываем инструмент позиционирования (см. Рисунок 5-4), и используя маску, имеющую вид затемнённого прямоугольника, задаём позицию и размер активной области.

После указания маски задаём параметр "Location (Местоположение)" то есть имя сцены и "Target (Цель)" то есть метку на сцене куда перенесётся игрок при клике по активной области.

Пользовательские шрифты

TyranoBuilder по умолчанию имеет в наборе четыре варианта шрифта. Этот список можно расширить, установив дополнительно пользовательские шрифты.

Основной формат шрифта поддерживаемый в TyranoBuilder – это TrueType (.ttf). Так как данный формат очень популярный, то соответственно существуют тысячи бесплатных и стильных шрифтов, которые можно скачать, например с: https://fonts.google.com/open in new window, https://www.fontsquirrel.com/open in new window, https://www.1001freefonts.com/open in new window или https://www.dafont.com/open in new window.

Чтобы установить шрифт в свой проект, выбираем пункт меню "Project\Custom Fonts (Проект\Пользовательские шрифты)". В появившемся окне нажимаем кнопку "Add Font (Добавить шрифт)", и указываем файл с расширением .ttf. Теперь, чтобы применился добавленный шрифт, щелкаете по иконке в виде шестерёнки, после чего откроется окно "Game Settings (Настройки игры)". Далее нужно перейти на вкладку "Font Style (Тиль шрифта)" и в поле "Font Type (Тип шрифта)" выбираем загруженный шрифт, после чего нажимаем кнопку "Apply (Применить)".

Для того чтобы назначить другой шрифт для части диалогов вашей игры, необходимо воспользоваться компонентом "Change Font Style (Изменить стиль шрифта)", который находится на вкладке "Messages (Сообщения)" панели компонентов. Его нужно разместить перед компонентом "Show Text (Показать текст)", и на панеле параметров указываем шрифт.

Магия Twine

Несмотря на то, что Twine выглядит так просто, он имеет достаточно мощные инструменты для создания визуального и звукового сопровождения истории. Это достигается благодаря поддержки JavaScript и каскадных таблиц стилей (CSS).

Примечание:

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

Как мы уже знаем, Twine имеет разные форматы историй, но у них во всех есть общая особенность это использование HTML тегов. Давайте познакомимся с некоторыми из них (см. Таблицу 5-8).

Таблица 5-8. HTML-теги, которые также работают в Twine

HTML-тегОписаниеПример
<p>Абзац<p>Это абзац</p>
<br>Перенос строкиСтрока.<br>Новая строка.
<button>Создаёт кликабельную кнопку<button>Нажмите здесь</button>
<ul>Используется для создания маркированного списка, используя тег <li> для каждого пункта.<ul>
<li>Пункт</li>
<li>Ещё один пункт</li>
</ul>
<table>Создает таблицу, используя тег <tr> для указания строки, <th> для указания заголовка и <td> для ячейки таблицы.<table>
<tr> <th>Имя</th> <th>Фамилия</th> <th>Занятие</th> </tr>
<tr> <td>Ben</td> <td>Бен</td> <td>Варвар</td> </tr>
<tr> <td>Джил</td> <td>Вордсмит</td><td>Шахтер</td> </tr>
</table>
<b>Жирный текстЧто-то выделено <b>жирным шрифтом</b>
<i>Курсивный текст<i>Это предложение выделено курсивом</i>
<u>Нижнее подчеркиваниеНемного <u>подчеркнутого текста</u>
<h1>Заголовок первого уровня, то есть самый большой заголовок<h1>Это довольно большой заголовок</h1>
<h6>Заголовок шестого уровня, то есть самый маленький заголовок<h6>Это сложно прочитать</h6>

Вызов JavaScript в Twine

Если нужно вставить в параграф простой JavaScript, а история создаётся в формате SugarCube, то достаточно воспользоваться тегом script, как показано в следующем примере.

<<script>> alert("Посмотри на меня!"); <</script>>

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

Анимация раскрытия текста в CSS

Выбрав на панеле инструментов вкладку Story (История) и нажав на Stylesheet (Таблица стилей), откроется редактор стилей "Story Stylesheet (История Таблица стилей)". Где, используя всю мощь CSS, вы можете задать глобальные стили для игры.

Ниже приведен пример эффекта раскрытия текста. Классом .css-reveal нужно будет пометить тег, к которому нужно применить плавную анимацию отображения текст слева направо.

.css-reveal
{
    width: 30em;
    white-space:nowrap;
    overflow:hidden;
    -webkit-animation: type 5s steps(75, end);
    animation: type 5s steps(75, end);
}
@keyframes type{
    from { width: 0; }
}
@-webkit-keyframes type{
    from { width: 0; }
}

Чтобы увидеть, как работает эффект нужно добавить в параграф строку, обернутую тегом <p>, с классом css-reveal.

<p class="css-reveal">Привет!</p>

Запускаем игру Build\Play (Собрать\Запустить). В результате, после показа параграфа, текст начнёт плавно появляться, как будто печатается печатной машинкой.

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

Оживляем текст

Есть желание украсить вашу историю? Воспользуйтесь нижеприведёнными примерами CSS. Для этого скопируйте и вставьте их в Story\Stylesheet. После чего назначьте данные стили элементам в параграфе. В результате класс .shadow добавить тексту тень, .redglow добавит тексту красное свечение,.upside-down перевернёт текст верх ногами, а класс .mirror отобразит текст зеркально.

.shadow {
    text-shadow: 0.07em 0.07em 0.07em #000;
}

.redglow {
    text-shadow: 0 0 0.2em #F00;
}

.upside-down {
    display: inline-block;
    transform: scaleY(-1);
    -webkit-transform: scaleY(-1);
}

.mirror {
    display: inline-block;
    transform: scaleX(-1);
    -webkit-transform: scaleX(-1);
}

Следующий пример CSS создаёт эффект дрожащего текста. Первые два правила устанавливают ключевые кадры для анимации, а сам эффект определяется в классе .shake, который нужно назначить элементу в параграфе.

@-webkit-keyframes shake {
    50% {
        -webkit-transform: translateX(0.2em);
        transform: translateX(0.2em)
    }
}

@keyframes shake {
    50% {
        -webkit-transform: translateX(0.2em);
        transform: translateX(0.2em)
    }
}

.shake {
    -webkit-animation: shake linear 0.1s 0s infinite;
    animation: shake linear 0.1s 0s infinite;
    display: inline-block;
}

Вот еще один пример правил CSS, который заставляет текст непрерывно исчезать и появляться. Если вы захотите изменить скорость анимации укажите в параметре «4s (4 секунды)» другое значение.

@-webkit-keyframes fade-in-out {
    0%,
    to {
        opacity: 0
    }
    50% {
        opacity: 1
    }
}

@keyframes fade-in-out {
    0%, to {
        opacity: 0
    }
    50% {
        opacity: 1
    }
}

.fade-in-out {
    text-decoration: none;
    animation: fade-in-out 4s ease-in-out infinite alternate;
    -webkit-animation: fade-in-out 4s ease-in-out infinite alternate;
}

В следующем примере, набор правил в стиле позволяет изменять размер текстового слоя. Для этого добавляем стили в Story\Stylesheet:

.resizable-textbox {
    border: 2px solid;
    padding: 20px;
    width: 300px;
    resize: both;
    overflow: auto;
}

После чего добавляем элемент в параграф:

<div class="resizable-textbox">
    First line<br>
    Second line<br>
    Third line
</div>

Запускаем игру.

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

Знакомимся с форматом истории Harlowe

Формат истории SugarCube отлично подходит для создания игр. Но как вы знаете Twine имеет и другие альтернативные форматы, например: Harlowe и Snowman.

Давайте сначала познакомимся с Harlowe. Для этого создадим новую историю в этом формате.

Harlowe и сила jQuery

В Twine есть встроенная библиотека jQuery — это замечательный JavaScript Framework, созданный Джоном Резигом в 2006 году. Данная библиотека предоставляет множество функций для работы с любым элементом в параграфе Twine, что позволяет улучшить визуальный вид игры. Так же с её помощью можно создать анимационные эффекты.

Давайте познакомимся с основами jQuery.

Если игра использует формат истории Harlowe, то для интеграции jQuery и JavaScript используется тег <script>. В нижеприведённом примере создаются две кнопки имеющие идентификатор button1, button2 и слой виде серого прямоугольника с идентификатором superbox. В свою очередь кнопкам назначается событие клика, которое выполняет анимацию для superbox (серий прямоугольник). Как вы заметили в примере почти все строки начинаются с символа доллара ($) – это и есть библиотека jQuery.

Запустим игру и проверим как работает пример. Если нажать кнопку под названием "Действие (Action)", то прямоугольник увеличиться по ширине до 300 пикселей, а если нажать кнопу "Сброса (Reset)", то прямоугольник вернётся к исходной размеру в 100 пикселей.

<script>
    $(document).ready(function () {
        $("#button1").click(function () {
            $("#superbox").animate({width: "300px"});
        });
        $("#button2").click(function () {
            $("#superbox").animate({width: "100px"});
        });
    });
</script>

<button id="button1">Action</button>
<button id="button2">Reset</button>
<div id="superbox" style="background:#777; height:100px; width:100px;"></div>

В предыдущем примере был рассмотрен только один эффект — это animate, но jQuery имеет и другие методы эффектов. Давайте рассмотрим некоторые из них и способы их применения (см. Таблицу 5-9).

Таблица 5-9. Методы эффектов в jQuery

ЭффектОписаниеПример
fadeInОтображает элемент, увеличивая его прозрачность. Можно указать скорость эффекта 'slow', 'fast' или значение в миллисекундах. По умолчанию значение равно "swing".$("button").click(function(){
$("#bluebox").fadeIn("fast");
$("#redbox").fadeIn("slow");
});
<button>Нажмите!</button><br>
<div id="bluebox" style="width:60px; height:60px; display:none; background-color:red;"></div><br>
<div id="redbox" style="width:60px; height:60px; display:none; background-color:red;"></div>
fadeOutСкрывает элемент, уменьшая его прозрачность. Имеет те же значения, что и для fadeIn.$("button").click(function(){
$("#bluebox").fadeOut(1000);
$("#redbox").fadeOut("fast");
});
slideToggleЭффекты скольжения позволяют плавно скрыть или раскрыть элемент. Можно указать скорость эффекта 'slow', 'fast' или значение в миллисекундах. По умолчанию значение равно "swing".$(document).ready(function(){
$("#button").click(function(){
$("#panel").slideToggle(2000);
});
});
<style>
#panel, #button {
padding: 3px;
text-align: center;
background-color: #red;
border: solid 1px #yellow;
}
#panel {
padding: 40px;
display: none;
}
</style>
<div id="button">Нажмите здесь</div>
<div id="panel">Скрытое послание!</div>

Больше об анимации

Давайте вернемся к методу jQuery animate, с которым мы познакомились ранее в этой главе. Данный метод способен на гораздо большее, нежели просто изменять ширину элемента, а именно, анимировать сразу несколько атрибутов одновременно. Следующий код, наглядно это показывает. После запуска игры, следите, как изменится слой happybox.

<style>
    .happybox {
        width: 100px;
        height: 100px;
        background: red;
        margin-top: 30px;
        border-style: solid;
        border-color: black;
    }
</style>
<script>
    $(document).ready(function () {
        $("button").click(function () {
            $(".happybox").animate({
                width: "200px", 
                height: "200px", 
                marginLeft: "30px", 
                borderWidth: "10px",
            });
        });
    });
</script>

<button type="button">Посмотри на меня!</button>
<div class="happybox"></div>

Знакомимся с Snowman

Формат истории "Snowman (Снеговик)" имеет очень простой подход для создания игр в Twine, и использует язык разметки Markdown. Данный язык, созданный Джоном Грубером, основан на использовании минимального количества специальных символов для форматирования текста (см. пример ниже).

# Header1
## Header2
### Header3
#### Header4
##### Header5
###### Header6
*Emphasized*
**Bold**
~~Deletion~~

После вставки данного кода в параграф Snowman мы получим результат, как показан на рисунке 5-6.

Рисунок 5-5. Пример форматирования текста в формате истории "Snowman"

Рисунок 5-5. Пример форматирования текста в формате истории "Snowman"

Snowman, JavaScript и Underscore

Формат истории «Snowman» был разработан с учётом поддержки JavaScript и CSS, поэтому в нем нет макросов как в SugarCube и Harlowe.

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

Чтобы интегрировать JavaScript и Underscore используются теги <% и %>, а для вывода данных используются теги <%= и %>. Пример использования данных тегов показан ниже.

<% alert( 'Привет, мир!' ); %>
Интересно, сколько будет 1+2? Это будет <%= 1+2 %>.

Snowman и переменные

В этом формате истории переменные определяются при помощи глобальной переменной с именем s.

<%
     s.myAge = 52;
     s.myName = "Джимми";
%>
Привет, меня зовут <%= s.myName %> и мне <%= s.myAge %>!
<% ++s.myAge %>
Постойте. На самом деле мне <%= s.myAge %>.

Бросаем кубик в Snowman

Теперь, давайте сделаем что-то практическое, например бросание игровых костей. Для получения случайного значения будем использовать метод _.random из библиотеки Underscore, где символ нижнее подчеркивание (т. е. _) и есть вызов самой библиотеки.

Бросаем четырёхгранный кубик: <%= _.random(1,4) %><br>
Бросаем шестигранный кубик: <%= _.random(1,6) %><br>
На самом деле мой реальный возраст <%= s.myAge=_.random(53,100) %> лет. 

Как видим в примере, метод _.random принимает два параметра для указания диапазона выборки случайного значения для бросаемого кубика, а в последней строке примера мы присваиваем ранее определённой переменной myAge случайное значение от 53 до 100.

Underscore и массивы

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

Пример создания массива:

<% var Friends = []; %>
<% var Items = ["Меч","Банан","Шляпа"]; %><br>
Ваш инвентарь: <%= Items %><br>
<% Items.pop() %>
Черт возьми, кажется, ты потерял свою шляпу!<br><br>
Ваш инвентарь: <%= Items %>

Первая строка создает пустой массив под названием "Friends (Друзья)". На второй строке создается массив с именем "Items (Предметы)", которому присваивается список, состоящий из трёх предметов, а третья строка просто выводит содержимое этого массива на экран.

В четвёртой строке при помощи JavaScript метода pop удаляем последний элемент массива. Если вам нужно добавить новые элементы в массив используйте метод push, как показано в примере <% Items.push("Новая шляпа", "Апельсин"); %>.

Теперь давайте познакомимся с наиболее полезными методами библиотеки Underscore (см. Таблицу 5-10).

Таблица 5-10. Методы библиотеки Underscore для работы с массивами

МетодОписаниеПример
_.findПросматривает каждое значение в массиве, пока не найдет первое, прошедшее проверку на истинность<% var DiscoverFedora = _.find(["Шляпа","Банан", "Шляпа"], function(string){ return string == "Шляпа"; }); %>
<%= DiscoverFedora %>
_.lastВозвращает последний элемент массива<% var Items = ["Меч", "Банан", "Шляпа"]; %>
<% var Inventory = _.last(Items); %>
<%= Inventory %>
_.firstВозвращает первый элемент массива<% var Items = ["Меч", "Банан", "Шляпа"]; %>
<% var Inventory = _.first(Items); %>
<%= Inventory %>
_.eachПеребирает массив, вызывая для каждого элемента функцию, которая принимает три аргумента элемент, индекс, список<% var Items = ["Меч", "Банан", "Шляпа"]; %>
<% _.each(Items, function(value, index, list) { %>
<%= index + ": " + value+"<br>" %>
<%= "Полный список: "+list %>
<% }); %>
_.mapСоздает новый массив значений путем сопоставления каждого значения в списке с помощью функции преобразованияИсходные значения: 1, 4 и 5
<% var Example = _.map([1, 4, 5], function(num){ return num * 5; }); %>
В результате получим каждый элемент умноженный на пять: <%= Example %>
_.sampleПроизводит случайную выборку из массива. Принимает значение, какое количество элементов вернуть из массива. В противном случае будет возвращен один случайный элемент<% var Items = ["Меч", "Банан", "Шляпа"]; %>
<% var Sampling = _.sample(Items, 2) %>
<%= Sampling %>
_.sizeВозвращает количество элементов в массиве<% var Items = ["Меч", "Банан", "Шляпа"]; %>
<% var Size = _.size(Items) %><%= Size %>
_.cloneКлонирует массив<% var Items = ["Меч", "Банан", "Шляпа"]; %>
<% var Cloned = _.clone(Items); %>
<%= Cloned %>
_.filterПросматривает каждое значение в массиве, возвращая массив значений, прошедших проверку.<% var Filtered = _.filter(["Меч", "Банан", "Шляпа"], function(string) { return string == "Банан" }); %>
<%= Filtered %>

Snowman и аудиовизуал

Поскольку Snowman не включает в себя макросы, то аудиовизуальные элементы в игру добавляются при помощи HTML и JavaScript. Соответственно, чтобы добавить изображение и аудиофайл нам нужно написать следующие строки:

<p>Вот изображение:</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/6/6e/Napoleon_crop.jpg" width="50%" height="50%"></p>
<p>А теперь ещё и аудио!</p>
<p><audio controls>
    <source src="https://upload.wikimedia.org/wikipedia/commons/b/b4/United_States_Navy_Band_-_O_Canada.ogg" type="audio/ogg">
</audio></p>

Что касается jQuery с его визуальными возможностями, то он так же используется и в Snowman.

Заключение

В этой главе было предоставлено много информации о Ren’Py, TyranoBuilder и Twine.

Про Ren’Py:

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

Про TyranoBuilder:

  • Что такое плагины и как они интегрируются
  • Как создать случайный диалог и использовать пользовательские шрифты
  • Как работать с переменными и как их использовать в сценарии

Про Twine:

  • Какие различия между форматами историй SugarCube, Harlowe и Snowman
  • Как использовать каскадные таблицы стилей (CSS)
  • Какие основные элементы HTML используются для создания игр
  • Как работать с JavaScript библиотеками jQuery и Underscore.

В следующей главе мы рассмотрим, как развернуть игры созданные в Ren’Py, TyranoBuilder и Twine на различных платформах.