Когда простой генерации текста уже мало

Когда простой генерации текста уже мало.

Автор: | Тип статьи: Перевод | Переводчик: Tinuviel | Редактор: m00n1ight | Размещение: Tinuviel, 23:23 (обновлено: 2023-02-26 23:20) | Слов: 3324 | Время чтения: 0 ч 13 м | Аудитория: Разработчики | Уровень читателя: Опытный | Просмотры: 3309

Часть первая

Один из ключевых инструментов в арсенале разработчика ролевой или приключенческой игры — автоматизированный «умный» генератор текстов, позволяющий создавать динамически изменяемые «на лету» строчки, в которые разработчик вставляет заранее прописанные названия предметов, чудовищ, имена персонажей и прочая (и они автоматически заменяются при изменениях во всех упоминаниях). Своего рода система единого источника. Ведь это просто и удобно! Что ж, на самом деле не всё так просто. Рассмотрим примеры.

Даже в современных высокотехнологичных ролевых играх часто встречаются тексты вида:

«Меч взят!»

«Получен предмет: Меч».

Процедурная генерация текста на примере Dragon Age: Inquisition

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

Динамическая генерация текста придаёт повествованию глубину

А ведь можно было бы сделать поинтереснее, подав информацию в виде художественного текста:

«Сэм подобрал меч, бегло осмотрел его и спрятал в ножны».

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

«Вы подобрали меч».

Конечно, от игры к игре требования к длине внутриигровых текстов разнятся. Некоторые разработчики предпочитают короткие формулировки, дабы текст не мешал игровому процессу, но в жанрах, где повествованию уделяется большое значение, не обойтись без связных и относительно распространённых абзацев текста. Классические ролевые игры к этому и стремятся. И ведь это придаёт игровому миру глубину и ощущение проработанности. Мало кто будет с этим спорить. Именно «вкусные» тексты привнесли в игры серии Realms of Arkania, например, ощущение интересного и богатого на события приключения. Так почему же немногие разработчики идут по этому пути? Неужели они считают, что современники не любят читать? Вряд ли. В игровой индустрии не найти больших книжных червей, чем любители ролевых и приключенческих игр, это всем известно.

Прим. Главвреда C.O.R.E.: В английском языке этого же эффекта можно достичь вводом определённого или неопределённого (по ситуации) артикля — и вот уже безликое «Sword taken!» превращается в аккуратное «You pick up a sword», например.

Процедурная генерация текста в логе сражения

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

«Сэм подобрал монеты, бегло осмотрел их и спрятал в карман».

Нетрудно заметить, что предложение изменилось. Может показаться, что изменения незначительны, но на они важны и их немало. Существительное уже не в единственном числе, а во множественном, из-за чего пришлось изменить и связанное с ним местоимение («их» вместо «его»).

Прим. Главвреда C.O.R.E.: Кроме того, если в английском языке достаточно обойтись конструкцией вида «stowe something away», не вдаваясь в подробности, куда именно Сэм прячет найденный предмет, в предложении на русском языке такой фокус в большинстве случаев не пройдёт, так что потребуется вводить уточнения в виде обстоятельств.

Ещё больше усложнится ситуация, если нужно описать, что:

«Орк подобрал меч, бегло осмотрел его и спрятал — и всё это под пристальным взглядом Сэма».

Местоимения и артикли (или окончания) — сущий ад для процедурной генерации текстов

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

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

Посмотрите внимательно:

Samwise watches an orc pick up a sword, as he gives it a quick look-over before stowing it away under Sam’s watchful eye.

«Орк» и «меч» в этом случае должны быть с неопределённым артиклем («an orc» и «a sword»), причём для слова «orc», начинающегося с гласного звука, артикль ещё и примет вид «an», а не «a». Местоимение «it» придётся изменить, если понадобится, чтобы орк поднял с пола больше одного предмета. Ну и отдельная проблема с притяжательным падежом («Sam’s»), ведь он тоже может иметь разные формы в зависимости от того, к какому именно слову относится, а также может требовать употребления артикля: если, например, речь пойдёт о взгляде безымянного персонажа, того же орка, скажем — тогда нужно будет использовать определённый артикль («the orc’s eye»).

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

«Эльфийка подобрала монеты, бегло осмотрела их и спрятала — и всё это под пристальными взглядами орков».

Структура предложения не изменилась, но сколько изменений окончаний [а в английском языке — артиклей] и местоимений!

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

А ведь английская грамматика ещё относительно простая...

В английском языке, на самом деле, всё ещё не так плохо: грамматика относительно проста, а порядок слов в предложениях прямой и строго фиксированный, так что процедурная генерация текстов в принципе применима (особенно если везде ставить определённые артикли). А вот если рассматривать романские языки, славянские, азиатские, да хотя бы даже другие языки германской группы, станет ещё печальнее, ведь там есть грамматические конструкции, правила и исключения, которых нет в современном английском языке. В некоторых из них, например, для ссылки на объект и субъект действия используются разные местоимения. Так что без должной подготовки попытка процедурной генерации текстов обречена на провал и потребует колоссальных затрат на программирование, вычитку и редактуру текстов, а также приведёт к избыточности кода и — не приведи бог — к необходимости вручную прописывать сотни абзацев текста для каждого варианта.

Скриншот Planescape: Torment

Вот почему разработчики продолжают использовать безликие и банальные заготовки вроде приведённых в начале этой статьи. Это просто и безопасно. Что может пойти не так, если постоянно говорить заготовками и только «по пунктам», не вступая толком в диалог? Другое дело, что погружению такие тексты точно не способствуют. Согласитесь, перед глазами так и предстают пейзажи Средиземья, Тамриэля или Азерота, стоит только прочесть...

«Меч получен!

Получен предмет: Меч.

Паук: 5 урона!

Друид не попал по: Орк»

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

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

В стародавние времена, когда мы разрабатывали Realms of Arkania, мы стремились добиться глубины и проработанности повествования, хотели сделать так, чтобы ситуации, сюжетные повороты, отдельные предложения можно было легко варьировать, сопоставлять и видоизменять. Игравшие наверняка вспомнят и порадуются. А вот нам пришлось всё долго и упорно продумывать, ведь в игре можно было собрать команду до семи персонажей, которые могли пользоваться обширным арсеналом предметов, а в мире то и дело встречались самые разные существа и происходили разные события. Мало того, мы изначально собирались перевести игру на несколько языков!

Скриншот Realms of Arkania

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

Когда несколько лет назад я разрабатывал ролевую игру Deathfire, я подошёл к вопросу иначе, ведь игра должна была основываться на Psycho Engine, программном модуле, который позволил бы динамически изменять игровой процесс и поведение игровых объектов. Из-за этой особенности мне потребовалась максимально возможная гибкость текстов, да ещё и хотелось впоследствии относительно просто локализовать игру на самые разные языки. Глядя на всё свежим взглядом и имея какой-никакой опыт в разработке игр, я придумал новую систему.

Грамотные тексты переводить проще...

Есть такой объектно-ориентированный язык программирования, Inform, позволяющий описывать игру в виде иерархии объектов с определёнными свойствами и атрибутами, и специально «заточен» под разработку текстовых приключений. А в них обозначенная здесь проблема стоит весьма остро. Ключевым отличием языка Inform и моей системы создания грамотных текстов в том, что Inform «знает», какой именно объект упоминается в тексте, потому что всё в игровом мире задано в виде ссылочных объектов. Тогда как система процедурной генерации текстов не знает, какие именно объекты стоят за словами в тексте, а потому должна откуда-то получить необходимую информацию.

...грамматика должна быть в текстах, а не в программном коде!

Я так и не реализовал свою систему тогда — всё осталось лишь на бумаге — но недавно у меня нашлось свободное время и я решил написать её реализацию на C# для движка Unity3D. Держа в уме всё изложенное выше, я сел и написал программный модуль, который позволяет определять контекст и с лёгкостью создавать динамически меняющиеся предложения, подчиняющиеся правилам грамматики.

Часть вторая

Скриншот The Witcher 3

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

Орк подобрал меч.

Его можно представить отдельными элементами (как мы выделяем части предложения):

[субъект действия] [глагол] [объект действия] или [существительное] [сказуемое] [дополнение].

Прим. Главвреда C.O.R.E.: С английским предложением можно сделать то же самое:

The orc picks up a sword → [определённый артикль] [субъект действия] [глагол] [неопределённый артикль] [объект действия]

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

Фродо схватил кинжал.

Frodo grabs a dagger.

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

Или вот другой пример:

Орки похватались за мечи.

The orcs grab some swords.

Структура всё та же, но в ход идут новые правила грамматики. Так как «орки» — существительное во множественном числе, изменилась и форма глагола. Ну и мечей теперь много, что тоже оказывает влияние.

Прим. Главвреда C.O.R.E.: В русском языке это нужно отразить с помощью окончания, а в английском, как видите, роль неопределённого артикля в этом случае играет some.

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

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

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

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

wom.an.en

orc..s

Чтобы сказать об одной женщине, я добавляю к основе wom окончание единственного числа an. Когда мы говорим о многих, к той же основе прибавляется окончание множественного числа en, получается women.

С орками так же: для слова в единственном числе берётся основа окончание единственного числа. Его значение оставлено пустым, так что слово не изменится — orc. А во множественном числе добавится классическое окончание множественного числа s.

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

Прим. Главвреда C.O.R.E.: Стоит отметить, что для русского языка ситуация осложняется необходимостью задания не только окончаний, связанных с количеством объектов, но и падежных окончаний. Недостаточно просто внести в базу «женщин.а.ы» и «орк.и». Там ещё и во множественном и единственном числе окончания разные!

женщин.а.ы.е.у.ой.е — для единственного числа

женщин.ы..ам..ами.ах — для множественного

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

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

Ещё немного текста на скриншоте Realms of Arkania

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

[The-name] pick[s] up [a-name]

[Имя] подобрал[а.о.и] [имя]

Вы наверняка уже оценили простоту и элегантность подхода. Стоит посмотреть на эту строчку — и всё понятно. Но остались и белые пятна.

В предложении есть элементы с определённым и неопределённым артиклем [The-name] и [a-name]. Но как программа понимает, в каком случае какой артикль использовать, где субъект действия, а где объект? Для этого как раз и нужные данные из GrammarAttr. Я задаю список ссылочных объектов в качестве параметра функции. На выходе получаем объект из GrammarAttr для слова orc, и другой объект для sword.

А дальше всё через ссылки. Чтобы сообщить программе, какой объект использовать, мы просто расширяем метку и пишем такой код:

[1.The-name] grab[s] [2.a-name]

[1.Имя] схватил[а.о.и] [2.имя]

У нас появился префикс (снова через точку), который помогает задать грамматический контекст. Для английского текста про схватившего меч орка это тот факт, что название объекта 1 (orc) должно употребляться в этом предложении с определённым артиклем и с заглавной буквы, а объекта 2 (sword) — со строчной и с неопределённым артиклем.

Модуль прочтёт предложение, считает атрибуты и извлечёт название первого объекта. Затем добавит перед ним определённый артикль (если в атрибутах не задано, что это имя собственное) — и сформирует результат.

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

Прим. Главвреда C.O.R.E.: Для русскоязычного текста схожим образом можно попытаться задать окончания, зависящие от числа, рода и падежа. Ну и строчные и заглавные буквы, конечно же.

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

В следующей метке у нас снова префикс, создающий новый контекст. Мы ссылаемся на второй объект в списке параметров Grammar.

Прим. Главвреда C.O.R.E.: В английском языке у него неопределённый артикль, строчная буква, единственное число. В русском — ещё и винительный падеж. Для меча это несущественно, конечно, ведь у этого слова нулевое окончание в винительном падеже, но вот если орк схватит хоббита, то у того по правилам должно появиться окончание «-а».

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

The orc grabs a sword.

Орк схватил меч.

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

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

Часть третья

Теперь, когда мы знаем, как задавать контекст предложения и ссылаться на объекты системы Grammar в списке параметров, давайте рассмотрим более сложные сценарии.

The nazgûl draws his sword and plunges it deep into Frodo’s shoulder.

Назгул достал меч и с силой вонзил в плечо Фродо.

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

Разобьём предложение на составные части.

На английском языке: [Определённый артикль] [субъект действия] [глагол] [притяжательное местоимение] [объект действия] and [глагол] [личное местоимение] deep into [определённый артикль] [объект действия-дополнение] shoulder.

На русском: [Субъект действия] [глагол] [объект действия] и с силой [глагол] в плечо [объект действия-дополнение].

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

Много всего в одном предложении, верно? Но в Grammar всё это довольно просто.

[The-name] draw[s] [his-her] [2.name] and plunge[s] [he-she] deep into [3.the-name][‘s] shoulder.

[Имя] достал[а.о.и] [2.имя] и с силой вонзил[а.о.и] в плечо [3.имя].

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

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

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

The nazgûl draw their swords and plunge them deep into Frodo’s shoulder.

Назгулы достали мечи и с силой вонзили в плечо Фродо.

Окончания и местоимения изменились как полагается (у нас ведь теперь несколько мечей и действующих объектов). И это не потребовало ни одной дополнительной строчки кода!

А можно написать такое, например...

Frodo draws his dagger and plunges it deep into the orc’s shoulder.

Фродо достал кинжал и с силой вонзил в плечо орка.

Или задать имя знаменитого клинка.

Frodo draws Sting and plunges it deep into the orc’s shoulder.

Фродо достал Жало и с силой вонзил в плечо орка.

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

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

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

[Der-name] zieh[t-en] [seinen-ihr] [2.name] und stösst [ihn-sie] tief in [3.name]s Schulter.

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

Из кода выше в Grammar сгенерируется вот такая красота:

Der Ork zieht sein Schwert und stösst es tief in Frodos Schulter.

Из-за сложности грамматики немецкого для интерпретации требуется обширная словарная база, но для писателя (или локализатора) это не имеет значения, ведь все предложения и метки написаны естественным языком. Аналогично это работает на испанском. Можно сделать подобное и для других языков.

Если вы заинтересовались модулем Grammar, приобрести его можно в онлайн-магазине Unity Asset Store. Сейчас модуль поддерживает английский, немецкий и испанский языки. Модуль можно легко расширить и дополнить, как метками и элементами словарной базы, так и в плане поддержки других языков. Да хоть клингонского или эльфийского, если понадобится!

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

И вот так просто и элегантно мы решили проблему, которая более тридцати лет ограничивала разработчиков игр. Если вы разработчик, приобретите Grammar script package и оставьте в прошлом уродливые безликие заглушки вместо текстов! 

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

Поиск по сайту

Категории материалов

Сообщения на форуме | новые

[В разработке] Avowed на форуме Avowed.
Последнее сообщение оставил m00n1ight (2024-11-22 в 00:54). Ответов: 78.
Бордель услаждения интеллектуальных страстей на форуме Оффтопик - Разное.
Последнее сообщение оставил m00n1ight (2024-11-22 в 00:50). Ответов: 9138.
Fallout: New Vegas на форуме Fallout: New Vegas.
Последнее сообщение оставил Saylone (2024-11-22 в 00:30). Ответов: 483.
Colony Ship RPG на форуме Colony Ship RPG.
Последнее сообщение оставил Хоттабыч (2024-11-21 в 23:57). Ответов: 951.
Ещё две студии от выходцев из ZA/UM на форуме [Архив] Новостной форум | Инди.
Последнее сообщение оставил Бобёр (2024-11-21 в 20:18). Ответов: 7.
[В разработке] Flint: The Treasure of Oblivion на форуме Тактические и стратегические.
Последнее сообщение оставил Хоттабыч (2024-11-21 в 17:33). Ответов: 17.
Expeditions: Rome на форуме Всё остальное | Инди.
Последнее сообщение оставил Хоттабыч (2024-11-21 в 14:11). Ответов: 108.
[В разработке] Urban Strife на форуме Всё остальное | Инди.
Последнее сообщение оставил m00n1ight (2024-11-21 в 13:21). Ответов: 49.
Возвращение во Врата Балдура на форуме Baldur’s Gate.
Последнее сообщение оставил FromLeftShoulder (2024-11-20 в 18:10). Ответов: 14.
[В разработке] Blades for Hire на форуме Тактические и стратегические.
Последнее сообщение оставил m00n1ight (2024-11-20 в 12:13). Ответов: 0.

Ожидаемое | таблица

Новости C.O.R.E.

Статьи C.O.R.E.

Новости RPG Codex

Новости RPG Watch

Новости RPG Nuke

Оставьте свой отзыв: QR-код для отзывов в «Яндексе».