MyTetra Share
Делитесь знаниями!
Ink - движок и скриптовый язык для написания сценариев игр и текстовых квестов
Время создания: 01.09.2025 18:25
Текстовые метки: ink, inky, сценарий, история, игра, язык, скрипт, open, source, текстовый, квест, quest
Раздел: Компьютер - Программирование - Разработка игр (gamedev)
Запись: xintrea/mytetra_syncro/master/base/1756740358tjonghwoc0/text.html на raw.github.com

Достаточно давно существует готовый инструмент для написания квестовых игр. Он состоит как бы из двух частей - сам язык, который называется ink, и простейший плеер для проигрывания ink-скриптов, который называется inky.


Скачать inky с поддержкой ink можно на официальном сайте:



https://www.inklestudios.com/ink/



Интрефейс inky выглядит примерно так:





Для языка ink существуют готовые библиотеки, которые подключаются к игровым движкам (например, к Godot), и позволяют проигрывать написанные на ink истории внутри игрового движка. Происходит это так: внутри движка создается специальный объект-плеер ink-файлов (в некоторых движках это достигается подключением плагина, в других - подключением библиотеки). И у этого объекта-плеера имеются базовые методы, которые запускают его проигрывание, и останавливают проигрывание когда пользователь должен сделать выбор (событийная модель). В обработчике события выбора можно получить индекс и текст того действия, который выбрал пользователь. И все это можно показывать в том интерфейсе игрового движка, который создает разработчик игры.



Примечание: иногда для плеера нужен не сам ink-файл, а его преобразованное представление в виде *.json-файла. Все зависит от конкретной реализации.



То есть, саму историю можно написать и отладить внутри inky, а потом можно использовать эту историю уже в самой игре, написанной с использованием какого-нибудь игрового движка.


Имеется двухчасовое видео с конференции GodotCon 2025, в котором объясняется как это делать.



Основы ink


Ink - это текстовый язык, в котором текст является "главным", и в него вплетаются символы разметки.


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


Редактор Inky активно поощряют использование отступов. Inky делает отступ автоматически, когда пользователь начинает новую строку после выбора (*): в этот момент редактор автоматически добавит отступ. Это явный сигнал о том, что так и нужно делать.


Текст, размещённый на отдельных строках, образует новые абзацы. Это поведение отличается от языка Mark Down (MD), в котором подряд идущие строки образуют один абзац, а новый абзац появляется после пустой строки.



Узлы в ink


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


Узел обозначается так:



=== knot_name ===



По-сути, это метка, кудо может быть осуществлен переход потока исполнения.


Переход на заданный узел происходит командой:



-> knot_name



Вот простой пример кода на ink, с применением команд выбора (см. далее):



Я стоял на развилке дороги. Куда пойти?

* [Пойти налево]

-> go_left

* [Пойти направо]

-> go_right


=== go_left ===

Вы пошли налево и нашли сундук с сокровищами!

-> END


=== go_right ===

Вы пошли направо и встретили дракона!

* [Сражаться] -> fight

* [Убежать] -> run_away


=== fight ===

Вы были храбры, но дракон был сильнее. Он забрал у вас копьё.

-> END


=== run_away ===

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

-> END



Переход на узел END приводит к тому, что исполнение ink-скрипта перепрыгивает на самый последний символ в последней строке, после которого ничего нет. И на этом исполнение скрипта завершается.


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



=== hurry_home ===

Мы поспешили домой на Сэвил-Роу-Стрит -> as_fast_as_we_could


=== as_fast_as_we_could ===

так быстро, как только могли.



Варианты выбора


Как видно из вышеприведенного примера, с помощью символа звездочки "*" помечается строка (строки), в которых происходит выбор. Это так называемый "обычный" выбор, и он характерен тем, что имеет "одноразовое" поведение. Если игрок вернется на тот же узел повторно - этот вариант выбора уже не будет виден.


Помимо обычного выбора, в языке ink есть еще Sticky-выбор. В нем, вместо звездочки используется символ плюс "+". Отличие от выбора через звездочку в том, что такие пункты выбора показываются всегда в момент выбора, независимо от того, выбирал раньше игрок этот пункт или нет.


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


Допустимо комбинировать пункты выбора, помечаемые как "*", так и "+" в одном блоке выбора. В этом случае поведение будет точно такое же: пункты "+" будут видны всегда, пункты "*" будут отображаться только пока они еще не были выбраны игроком.



Переменные и условия


В скриптах ink возможно использование переменных. Бывают глобальные и временные (т. е. локальные) переменные.


Глобальная переменная объявляется с помощью ключевого слова VAR, и прописывается в начале скрипта, например:



VAR intelligence = 0



Временная переменная прописывается внутри узла, т. е. где-нибудь после строки === this_is_knot ===. Временная переменная существует внутри узла. Синтаксис создания временной переменной следующий:



~ temp number_of_active_things = 0



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


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


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



VAR intelligence = 0


Приветствую в магической академии. Вы проходите тест.

Сколько будет 2 + 2?

* [Будет 3]

~ intelligence -= 1

-> mag_answer

* [Будет 4]

~ intelligence += 1

-> mag_answer

* [Будет 5]

~ intelligence -= 2

-> mag_answer


=== mag_answer ===


Ваш уровень интеллекта: {intelligence}


{

- intelligence > 0:

"Не плохо, не плохо," - сказал маг. -> pass_test

- else:

"Вам нужно подучиться," - вздохнул маг. -> fail_test

}


=== pass_test ===

Тест был успешно пройден, и вы пошли в академию.

->END


=== fail_test ===

Тест был провален и злобный гоблин пинками выдворил вас на улицу.

->END



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


Для вывода значения переменной надо просто написать имя переменной в фигурных скобках {имя_переменной}.



Фигурные скобки {...} - это, по сути, блок кода, в котором вычисляется выражение. Выражение, состоящее из одного только имени переменной, возвращает значение этой переменной.



Условия так же оформляются в фигурных скобках. Сама конструкция условия (и альтернативной ветки else) должна предваряться символом "-", как это сделано в примере выше.



Внимание! Парсер ink устроен таким образом, что открывающая фигурная скобка уже говорит о том, что за ней следует вычисляемое выражение в той же строке. Поэтому условие, записанное вот так:



{ - intelligence > 0:

"Не плохо, не плохо," - сказал маг. -> pass_test

- else:

"Вам нужно подучиться," - вздохнул маг. -> fail_test

}



... будет работать неожиданно: символ "-" в данном случае будет рассмотрен не как начало конструкции условия, а как минус. И условие сравнения с нулем получится инверсным.



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



=== near_north_pole ===

~ temp number_of_warm_things = 0


{ blanket:

~ number_of_warm_things++

}


{ ear_muffs:

~ number_of_warm_things++

}


{ gloves:

~ number_of_warm_things++

}


{ number_of_warm_things > 2:

Несмотря на снег, мне было очень уютно.

- else:

В ту ночь мне было холоднее, чем когда-либо.

}




Так же в этом разделе:
 
MyTetra Share v.0.67
Яндекс индекс цитирования