# Сценарии Naninovel

Скрипты Naninovel – это текстовые документы (c расширениеv .nani), где вы контролируете то, что происходит в сценах. Ассеты сценариев создаются с помощью контекстного меню ассета Create -> Naninovel -> Naninovel Script. Вы можете открывать и редактировать их с помощью встроенного визуального редактора или с помощью внешнего текстового редактора, например блокнот, TextEdit или VS Code.

Каждая строка в сценарии Naninovel представляет собой инструкцию, которая может быть командой, обычным текстом, меткой или комментарием. Тип оператора определяется литералом, который помещается в начале строки:

Литерал Тип инструкции
@ Команда
# Метка
; Комментарий

Если в начале строки нет ни одного из вышеуказанных литералов, она воспринимается как строка общего текста.

# Командные строки

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

# Идентификатор команды

Сразу после литерала команды ожидается идентификатор команды. Это может быть либо имя класса C#, реализующего команду, либо псевдоним команды (если он применяется через атрибут CommandAlias).

Например, команда @save (используемая для автоматического сохранения игры) реализуется через C# класс AutoSave. Реализующий класс также имеет атрибут [CommandAlias("save")], поэтому для вызова этой команды в скрипте можно использовать операторы @save и @AutoSave.

Идентификаторы команд не чувствительны к регистру; все следующие операторы действительны и вызовут одну и ту же команду AutoSave:

@save
@Save
@AutoSave
@autosave

# Параметры команд

Большинство команд имеют ряд параметров, определяющих действие команды. Параметр – это выражение типа "ключ-значение", определяемое после командного литерала, разделенного столбцом (:). Идентификатор параметра (ключ) может быть либо именем соответствующего поля параметра класса реализации команды, либо псевдонимом параметра (если он определен через свойство alias атрибута CommandParameter).

@commandId paramId:paramValue 

Рассмотрим команду @hideChars, которая используется для скрытия всех видимых персонажей в сцене. Её можно использовать следующим образом:

@hideChars

Вы можете здесь использовать параметр time типа Decimal для того, чтобы указать, как долго персонажи будут растворяться, прежде чем полностью скроются (исчезнут из сцены):

@hideChars time:5.5

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

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

@hideChars time:5.5 wait:false
@hidePrinter

Так текстовый принтер скроется сразу после того, как персонажи начнут исчезать. Если wait будет иметь значение true (или значение не будет указано), принтер будет скрыт только тогда, когда @hideChars завершит выполнение.

# Типы значений параметров

В зависимости от собственного типа параметры могут ожидать один из следующих типов значений:

Тип Описание
String Простое строковое значение, например: LoremIpsum. Не забудьте заключить строку в двойные кавычки, если она содержит пробелы, например: "Lorem ipsum dolor sit amet.".
Integer Число без дробей; целочисленное значение, например: 1, 150, -25.
Decimal Десятичное число с дробью, отделённой точкой, например: 1.0, 12.08, -0.005.
Boolean Имеет два доступных значения: true or false (нечуствительна к регистру).
Named<> Строка имени, связанная со значением одного из вышеперечисленных типов. Часть имени отделена точкой. Например, для Named<Integer>: foo.8, bar.-20.
List<> Список из значений одного из вышеперечисленных типов, разделённых точками. Например, для List<String>: foo,bar,"Lorem ipsum.", для List<Decimal>: 12,-8,0.105,2

# Безымянные параметры

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

Например, команда @bgm ожидает безымянный параметр, указывающий имя музыкального трека, который нужно проиграть:

@bgm PianoTheme

"PianoTheme" здесь – это значение типа String параметра "BgmPath".

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

# Опциональные и обязательные параметры

Большинство параметров команды являются опциональными. Это означает, что они либо имеют предопределенное значение, либо просто не требуют никакого значения для выполнения команды. Например, если команда @resetText используется без указания каких-либо параметров, она сбросит текст в принтере по умолчанию, но вы также можете установить определенный ID принтера следующим образом: @resetText printer:Dialogue.

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

# Справочик команд API

Для списка всех доступных сейчас команд с описанием, параметрами и примерами использования см. справочник команд API.

# Универсальные текстовые строки

Чтобы сделать написание скриптов с большим объемом текста более удобным, используются универсальные текстовые строки. Строка считается универсальным текстовым оператором, если она не начинается ни с одного из вышеуказанных литералов операторов:

Lorem ipsum dolor sit amet, consectetur adipiscing elit.

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

Felix: Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Чтобы сэкономить время и текст при постоянном изменении внешности персонажей, связанных с печатным текстом, вы также можете указать внешность после ID персонажа:

Felix.Happy: Lorem ipsum dolor sit amet.

Строка выше аналогична следующим двум:

@char Felix.Happy wait:false
Felix: Lorem ipsum dolor sit amet.

# Встраивание команд

Иногда вам может потребоваться выполнить команду во время отображения (печати) текстового сообщения, сразу после или перед определенным символом. Например, актор изменил бы свою внешность (выражение) при выведении определенного слова; или воспроизведение определенного звукового эффекта в ответ на какое-то событие, описанное в середине печатного сообщения. Функция встраивания команд позволяет обрабатывать подобные ситуации.

Все команды, (как встроенные и пользовательские) могут быть встроены в универсальные текстовые строки с использованием квадратных скобок ([,]):

Felix: Lorem ipsum[char Felix.Happy pos:0.75 wait:false] dolor sit amet, consectetur adipiscing elit.[i] Aenean tempus eleifend ante, ac molestie metus condimentum quis.[i][br 2] Morbi nunc magna, consequat posuere consectetur in, dapibus consectetur lorem. Duis consectetur semper augue nec pharetra.

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

Под капотом универсальные текстовые строки разбираются на отдельные команды, идентифицируемые встроенным индексом; текст печатается с помощью команды @print. Например, следующая универсальная текстовая строка в скрипте Naninovel:

Lorem ipsum[char Felix.Happy pos:75 wait:false] dolor sit amet.

— в действительности обрабатывается движком как последовательность отдельных команд:

@print "Lorem ipsum" waitInput:false
@char Felix.Happy pos:75 wait:false
@print "dolor sit amet."

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

Some text \[ text inside brackets \]

— напечатает "Some text [ text inside brackets ]" в игре.

# Строки-метки

Метки используются в качестве "якорей" при навигации по сценариям Naninovel с помощью команд @goto. Чтобы объявить метку, используйте литерал # в начале строки, за которым следует имя метки:

# Epilogue

Теперь можно использовать команду @goto, чтобы "прыгнуть" к этой строке:

@goto ScriptName.Epilogue

В случае, если вы используете команду @goto из того же сценария, в котором находится искомая метка, вы можете опустить имя сценария:

@goto .Epilogue

# Строки-комментарии

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

; Следующая команда выполнит автосохранение игры.
@save

# Условия исполнения

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

; Если значение `level` – это номер, и он больше 9000, добавить выбор.
@choice "It's over 9000!" if:level>9000

; Ессли переменная `dead` – булева и имеет значение `false`, выполнить команду вывода текста.
@print text:"I'm still alive." if:!dead

; Если переменная `glitch`– булева и имеет значение `true`, или функция вывода случайного числа в диапазоне от 1 до 10 
; возвращает 5 или больше, выполнить команду `@spawn`.
@spawn GlitchCamera if:"glitch || Random(1, 10) >= 5"

; Если `score` имеет значение от 7 до 13, или переменная `lucky`– булева 
; и возвращает `true`, загрузить скрипт `LuckyEnd`.
@goto LuckyEnd if:"(score >= 7 && score <= 13) || lucky"

; Вы также можете использовать условия во встроенных командах.
Lorem sit amet. [style bold if:score>=10]Consectetur elit.[style default]

; Если в самом выражении используются двойные кавычки, 
; не забывайте дважды экранировать их.
@print {remark} if:remark=="Saying \\"Stop the car\\" was a mistake."

Также возможно указывать многострочные условные блоки с помощью команд @if, @else, @elseif и @endif.

@if score>10
	Good job, you've passed the test!
	@bgm Victory
	@spawn Fireworks
@elseif attempts>100
	You're hopeless... Need help?
	@choice "Yeah, please!" goto:.GetHelp
	@choice "I'll keep trying." goto:.BeginTest
	@stop
@else
	You've failed. Try again!
	@goto .BeginTest
@endif

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

То же самое работает и для универсальных текстовых строк:

Lorem ipsum dolor sit amet. [if score>10]Duis efficitur imperdiet nunc. [else]Vestibulum sit amet dolor non dolor placerat vehicula.[endif]

Для дополнительной информации о форматах условных выражений и доступных операторах см. гайд по выражениям сценариев.

# Визуальный редактор

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

Чтобы добавить новую строку в сценарий, щелкните правой кнопкой мыши в месте, куда вы хотите вставить строку, или же нажмите Ctrl+Space (вы можете изменить привязки клавиш по умолчанию в меню конфигурации ввода) и выберите нужную строку или тип команды. Чтобы изменить порядок строк, перетащите их за номерные метки. Чтобы удалить строку, щелкните ее правой кнопкой мыши и выберите пункт "Удалить".

Когда вы изменили сценарий с помощью визуального редактора, вы увидите звездочку (*) над именем сценария в заголовке инспектора. Это означает, что ассет был изменён и должен быть сохранен; нажмите Ctrl+S, чтобы сохранить ассет. Если вы попытаетесь выбрать другой ассет после изменения сценария, появится диалоговое окно, позволяющее либо сохранить, либо отменить изменения.

Визуальный редактор автоматически синхронизирует отредактированный сценарий, если вы обновите его внешне, так что вы можете легко работать со сценариями как в текстовых, так и в визуальных редакторах. В случае, если автосинхронизация не работает, убедитесь, что функция Auto Refresh включена в меню редактора Unity Edit -> Preferences -> General.

В режиме воспроизведения вы можете использовать визуальный редактор, чтобы отслеживать, какая строка сценария воспроизводится в данный момент, и щелкнуть правой кнопкой мыши строку, чтобы перемотать сценарий назад. Эта функция требует, чтобы сценарий имел одинаковый ID ресурса (при назначении в меню менеджера ресурсов) и имя ассета.

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

Вы можете настроить поведение редактора и внешний вид в меню конфигурации сценариев.

# Схемы сценариев

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

Чтобы открыть окно схемы, испольуйте меню редактора Naninovel -> Script Graph. Вы можете закрепить это окно как любую другую панель редактора, если хотите.

Инструмент автоматически построит графическое представление всех сценариев Naninovel (в виде узлов), назначенных через ресурсы редактора (Naninovel -> Resources -> Scripts), и связей между ними.

Соединения генерируются на основе команд @goto и @gosub. Если команде назначено условное выражение (параметр if), соответствующий порт в узле будет выделен желтым цветом, и вы сможете увидеть выражение при наведении курсора на порт.

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

Вы можете изменять положение узлов по своему усмотрению, и их позиции будут автоматически сохранены при закрытии окна схемы или выходе из Unity; затем позиции будут восстановлены при повторном открытии окна. Вы также можете сохранить положения вручную, нажав кнопку "Save". Нажатие кнопки "Auto Align" приведет к сбросу всех позиций.

При изменении сценариев или добавлении новых нажмите кнопку "Rebuild Graph", чтобы синхронизировать изменения.

# Горячая перезагрузка

Можно редактировать сценарии в режиме воспроизведения (как с помощью визуальных, так и внешних редакторов) и сразу же применять изменения, не перезапуская игру. Эта функция управляется через свойство Hot Reload Scripts в конфигурации сценариев и включена по умолчанию.

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

В случае, если горячая перезагрузка не работает, убедитесь, что включено свойство Auto Refresh, а Script Changes While Playing установлено на Recompile And Continue Playing. Оба свойства можно найти в меню редактора Unity Edit -> Preferences -> General.

Чтобы вручную инициировать горячую перезагрузку текущего сценария Naninovel (например, при редактировании файла скрипта вне проекта Unity), используйте консольную команду reload. Команда предназначена только для редактора (не будет работать в сборках).

# Поддержка IDE

Такие функции IDE, как подсветка синтаксиса, проверка ошибок, автозавершение и интерактивная документация, могут значительно повысить производительность при написании сценариев. Мы сделали расширение для бесплатного open-source VS Code (доступен для Windows, MacOS и Linux), которое обеспечивает существенную поддержку IDE для синтаксиса NaniScript.

Посмотрите следующий видеогайд по активации и использованию расширения.

Поддержка других редакторов возможна в будущем; проверьте тему на GitHub для получения дополнительной информации.

# Отладка сценариев

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

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

rewind 12

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

Чтобы узнать, какой сценарий и какая строка воспроизводятся в данный момент, используйте окно отладки: введите debug в консоли разработки и нажмите Enter, чтобы открыть окно.

Отладка сценариев

В заголовке окна отображаются имя текущего воспроизводимого сценария, номер строки и индекс команды. Если включена функция [автоозвучивания](/ru/guide/voicing.md#автоматическое озвучивание), то также будет отображаться имя соответствующего голосового клипа. Вы можете изменить положение окна, перетащив его по заголовку. Кнопка "Stop" остановит выполнение скрипта; когда проигрыватель скриптов остановлен, кнопка "Play" возобновит выполнение. Вы можете закрыть окно отладки, нажав кнопку "Close".

Окно отладки доступно как в редакторе, так и в сборках.

Обновлено: October 13, 2020