Добро пожаловать, berk
Twitter Группа Steam Страница Вконтакте Группа C.O.R.E. Dragon Age Контакте Новостная лента RSS
/ Статьи / Общеролевые статьи / Аналитика

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

Автор: Guido Henkel | Добавил: Tinuviel, 13.02.2018 | Просмотры: 780

Гвидо Хенкель

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

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

Часть первая

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

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

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

Когда простой генерации текста недостаточно (1)

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

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

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

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

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

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

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

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

Когда простой генерации текста недостаточно (2)

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

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

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

Прим. Главвреда 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”).

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

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

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

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

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

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

Когда простой генерации текста недостаточно (3)

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

«Меч получен!
Получен предмет: Меч.
Паук: 5 урона!
Друид не попал по: Орк»

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

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

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

Когда простой генерации текста недостаточно (4)

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

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

Грамотные тексты и переводить легче и проще…

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

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

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

Часть вторая

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

Когда простой генерации текста недостаточно (5)

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

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

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

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

Прим. Главвреда 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.: Стоит отметить, что для русского языка ситуация осложняется необходимостью задания не только окончаний, связанных с количеством объектов, но и падежных окончаний. Недостаточно просто внести в базу «женщин.а.ы» и «орк.и». Там ещё и во множественном и единственном числе окончания разные!

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

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

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

Когда простой генерации текста недостаточно (6)

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

[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 для первого объекта, программа проверит, действует один орк или же их много. Затем добавит окончание в зависимости от проставленного «флажка».

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

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

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

The orc grabs a sword.
Орк схватил меч.

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

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

Часть третья

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

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 делает за вас всё самое сложное: сопоставляет артикли, окончания, местоимения, падежи и прочая, чтобы получился правильный и красивый текст. На волшебство похоже, на самом деле.

Для такой гибкости модулю 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 и оставьте в прошлом уродливые безликие заглушки вместо текстов!

Благодарим за поддержку нашего спонсора Harvi Denchik.


Обсудить статью на форуме

Обновления форума

Копирайты

  • C.O.R.E. © 2009 – 2017
Система Orphus Creative Commons License
Войти на сайт?
Логин:
Пароль: