Разработка интерактивного агента компьютерной игры
Анализ как один из основных этапов разработки программного обеспечения. Реалистичность поведения агента - основной фактор, от которого напрямую зависит вовлеченность игрока и время геймплея. Описание диаграммы прецедентов для интерактивного агента.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | дипломная работа |
Язык | русский |
Дата добавления | 01.09.2017 |
Размер файла | 787,6 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Рисунок 2.4. Схема узла состояния в опасности.
Рассмотрим подробнее состояние атаки. Условием перехода в данное состояние является наличие противника равного по силам или слабее. Данное условие представлено узлом типа Invertor, к которому прикреплен листовой узел-условие. Само состояние атаки является узлом типа Sequence. Как упоминалось выше, это состояние содержит в себе два других состояния - нанести удар и преследовать. Переход в эти состояния обуславливается промежуточным узлом типа Selector, который в зависимости от контекста выбирает одно из состояний. Условием перехода в состояние нанесения удара является наличие противника в зоне поражения. Условие перехода в состояние преследования обратно условию перехода в состояние нанесения удара, поэтому в узле этого состояния используется Invertor и тот же самый узел-условие что и в предыдущем состоянии. Общая схема состояния атаки представлена на рисунке 2.6.
Если же противник сильнее агента, то необходимо чтобы он перешел в состояние побега. В таком случае узел атаки вернет состояние FAILURE и промежуточный узел перейдет к проверке состояния побега. Данное состояние представляет собой простой узел типа Sequence с двумя листовыми узлами типов условие и действие. Схема состояния побега представлена на рисунке 2.5.
Рисунок 2.5. Схема узла состояния побега
Рисунок 2.6. Схема узла состояния атаки
Последним в приоритете стоит состояние бездействия. Оно должно выполняться только если все остальные узлы вернули состояние FAILURE. Так как в дереве уже использовался узел-условие, проверяющий наличие противника в зоне видимости агента, то в данном узле для проверки необходимого для состояния контекста используется промежуточный узел типа Invertor. Само состояние состоит из двух листовых узлов действий, которые соединены узлом типа Sequence. Узлы действия проигрывают анимацию бездействия агента или передвигают его в случайную точку в ограниченной области. Схема данного состояния представлена на рисунке 2.7.
Рисунок 2.7. Схема узла состояния бездействия
Данное дерево является базовым для агентов, но, поскольку каждый из разрабатываемых агентов имеет уникальное поведение, их конечные деревья поведения будут незначительно отличаться. В основном отличия будут проявляться в меньшем количестве состояний, дополнительных или измененных состояниях.
Теперь, когда дерево поведения агента определено, можно переходить к проектированию классов дерева. Связь агента с деревом будет реализована через класс BehaviourTree, который будет содержать ссылку на корневой узел и иметь лишь одну функцию Update(), которая будет вызываться при отрисовке каждого кадра и проходить по дереву.
Все типы узлов будут наследоваться от одного абстрактного класса Node. Единственным свойством этого класса будет Status, в котором будет храниться состояние узла, представленное перечисляемым типом. Помимо этого, в классе будут функции, отвечающие за старт узла, его исполнение и выход. Эти функции необходимы чтобы установить определенные параметры перед запуском узла, например, запустить анимацию, а затем при прохождении узла вернуть параметры в исходное состояние (остановить анимацию). Функции Start() и End() не возвращают никаких значений, тогда как Running возвращает состояние узла, в зависимости от которого выполнение узла продолжается или его работа останавливается. Помимо этих функций в данном классе имеется функция Update(), в которой заложена логика взаимодействия между этими функциями и которая вызывается при отрисовке каждого кадра из экземпляра BehaviourTree.
Класс Compoite помимо родительских свойств обладает списком узлов, которые ему необходимо обрабатывать. Логика их обработки заложена в его наследниках, классах Selector и Sequence.
Класс Decorator от абстрактного класса отличает наличие ссылки на узел, логику которого ему нужно изменять. От него наследуется класс Invertor, который изменяет состояние узла на противоположное.
Класс ConditionNode содержит ссылку на самого агента и функцию-условие, возвращаемое значение которой определяет состояние узла. Класс ActionNode содержит только ссылку на самого агента. Логика поведения агента будет заложена внутри функций Start(), Update(), End(). Данный класс так же является абстрактным, от него будут наследоваться классы узлов, выполняющих конкретные действия, например, движение.
3. Разработка и внедрение системы интерактивных агентов
После того как этапы анализа и проектирования завершены, можно переходить непосредственно к разработке системы. В данной главе отражены такие этапы как разработка, тестирование и внедрение. В начале производится разработка библиотеки дерева, на основе которой будут построены новые деревья поведения в игре, производится ее стабилизация. Затем на основе спроектированных индивидуальных деревьев для агентов и библиотеки создаются деревья непосредственно в игре и тестируются на основе тестовых сценариев.
3.1 Разработка библиотеки дерева
Дерево поведения является абстрактной сущностью и потому может быть разработана изолированно от игры и быть применена к любой игре использующий скриптовой язык LUA. Для разработки дерева использовалась библиотека LOOP (Lua Object Oriented Programming), позволяющая использовать при программировании на данном языке объектно-ориентированный подход.
Первым необходимо разработать класс самого дерева. Как уже упоминалось в предыдущей главе, дерево будет только хранить ссылку на корневой узел и иметь функцию Update(), которая будет вызываться при отрисовке каждого кадра. Код данного класса приведен ниже на рисунке 3.1.
Рисунок 3.1. Код класса Behaviour tree
Далее необходимо разработать базовый класс узла, от которого будут наследоваться все остальные узловые классы. Данный класс будет иметь два свойства - имя и статус. Имя необходимо будет при отладке и тестировании дерева, а статус будет хранить состояние узла - завершилась ли его работа, а если завершилась, то было ли ее завершение успешным?
Для обозначения статуса узла необходимо ввести enum класс. LUA не имеет всторенных конструкторов для создания классов такого типа, поэтому данный класс будет представлен в виде метатаблицы, которая хранит в себе три другие свойства-метатаблицы. Код данного класса приведен ниже на рисунке 3.2.
Данная организация класса позволяет использовать эту метатаблицу как обычный перечислимый класс в объектно-ориентированном языке - сначала указывается имя класса, а затем его атрибут.
Рисунок 3.2. Код класса NodeState
После того как перечислимый класс для обозначения состояния узла готов, можно возвращаться к разработке базового класса узла. Данный класс имеет 4 функции - три функции, отвечающие за исполнение функциональности узла, одну, которая вызывается из дерева при отрисовке каждого кадра и еще одна функция предназначена для обнуления статуса узла, чтобы избежать ситуации, когда в дереве узел, который должен быть перезапущен будет постоянно находиться в состоянии исполнения. Общая схема класса приведена ниже на рисунке 3.3.
Рисунок 3.3. Код класса Node
Теперь более подробно рассмотрим какой функциональностью должна обладать каждая из функций класса.
Функция Update проверяет состояние узла и в зависимости он него или запускает узел заново через функцию Start, или продолжает его выполнение через функцию Running. По окончании деятельности узла вызывается завершающая функция End. Состояние узла определяется работой функции Running. Данная функция определяет основное поведение узла и на основе него возвращает текущее состояние узла. В зависимости от состояния так же определяется выполнение самой функции Update. Данная функция будет переопределена в некоторых наследуемых классах, но в основном данное поведение узла едино для большинства дочерних классов. Код данной функции приведен ниже на рисунке 3.4.
Рисунок 3.4. Функция Update класса Node
Функции Start, Running, End в данном классе являются абстрактными и потому не обладают никакой функциональностью и должны быть переопределены в наследуемых классах.
Как и функция Update, поведение которой едино для большинства дочерних классов функция Reset так же переопределяется лишь в нескольких классах. Основное назначение этой функции - обнулить текущее состояние узла. Как уже упоминалось в предыдущей главе, узлы одного уровня в дереве располагаются в порядке приоритета. Если при проходе дерева сработал более приоритетный узел, а до этого другой узел находился в состоянии выполнения, необходимо чтобы его состояние было обнулено, чтобы при возвращении к этому узлу его исполнение начиналось заново. Код данной функции достаточно тривиален и приведен ниже на рисунке 3.5.
Рисунок 3.5. Функция Reset класса Node
После того, как базовый класс узла разработан, можно переходить к разработке дочерних классов. Самыми простыми наследуемыми классами являются классы, отвечающие за условия и действия. Их поведение во многом совпадает с поведением базового узла, в основном в данных классах просто переопределяются абстрактные функции Start, Running, End. Рассмотрим реализацию данных классов подробнее.
Начнем с рассмотрения класса Condition. Поскольку данный класс наследуется от базового, он так же обладает свойствами, присущими родительскому классу - именем и состоянием. Помимо этого, в данном классе хранится сущность класса агента и функция-условие от результата которой зависит состояние узла. Данная организация свойств класса позволяет ему быть достаточно абстрактным, чтобы можно было создавать узлы для проверки любых условий без создания дополнительных узкоспециализированных классов. Ниже, на рисунке 3.6, приведен код данного класса.
Рисунок 3.6. Код класса ConditionNode
Как видно из приведенного кода данный класс переопределяет лишь одну функцию - Running. Это обусловлено тем, что данному узлу не нужно никаких дополнительных приготовлений к запуску. Его задача состоит лишь в проверке условия и возвращении состояния на основе результата. Код наглядно демонстрирует что экземпляры данного класса не могут находиться в длительном исполняемом состоянии.
Следующим рассматриваемым классом является класс Action, отвечающий непосредственно за действия, исполняемые агентом. Чтобы сделать данный класс максимально унифицированным, он так же хранит сущность агента и набор функций, которые необходимо вызывать в процессе работы узла. Конструктор данного класса выглядит следующим образом (рисунок 3.7):
Рисунок 3.7. Функция init класса Action
Данный класс, в отличие от предыдущего, переопределяет все три абстрактные функции базового класса узла.
При старте узла, он вызывает функцию, хранящуюся в свойстве start_function, отвечающую за начало действия. Примером работы такой функции может быть, например, включение анимации, или начало движения. Помимо этого, если агент на данный момент выполняет уже какое-то другое действие, но перед началом нового, его необходимо завершить. Для этого в дереве хранится исполняемое на данный момент действие. Код данной функции приведен ниже на рисунке 3.8.
Рисунок 3.8. Функция Start класса Action
Функция Running в основном просто проверяет не было ли завершено текущее действие и на основе этой проверки возвращает состояние узла. Для проверки условия данная функция использует функцию из свойства running_function. Код данной функции приведен ниже на рисунке 3.9.
Рисунок 3.9. Функция Running класса Action
Функция End, вызывает функцию из свойства end_function, которая должна завершить выполнение действия, например, убрать оружие или отключить анимацию.
Рисунок 3.10. Функция End класса Action
Далее необходимо разработать родительский класс для узлов, которые будут изменять значение указанного узла. В данном классе будут переопределены всего две функции - конструктор, поскольку узлам данного типа необходимо хранить ссылку на изменяемый узел и функция Reset, так как помимо своего статута узлам-декораторам необходимо обнулять и статус изменяемого узла. Код данного класса приведен ниже на рисунке 3.11.
Рисунок 3.11. Код класса Decorator
Остальная логика будет определяться непосредственно дочерними классами. В дереве, спроектированном ранее используется только один тип узлов-декораторов. Это узел, который инвертирует значение изменяемого узла. Рассмотрим данный класс подробнее.
В данном классе переопределяется лишь одна функция - Update. В функциях Start, Running, End нет необходимости, поэтому в данном классе они вообще не были определены. В переопределяемой функции, вызывается та же функция у изменяемого узла, и, в зависимости от его состояния возвращается состояние инвертируемого узла. Данному узлу даже нет необходимости хранить собственное состояние, поскольку, в основном, оно используется для узлов с длительными задачами, но в данном случае в таком состоянии будет находится изменяемый узел, а узел-инвертор просто вернет состояние этого узла. Код переопределенной функции приведен ниже на рисунке 3.12.
Рисунок 3.12. Функция Update класса Invertor
Помимо узлов-декораторов, есть еще один очень важный тип узлов, упомянутый в предыдущей главе. Это узлы, комбинирующие набор заданных узлов. Всего было разработано пять подтипов узлов данного типа. Но рассмотрим сначала основной класс-комбинатор.
Данный класс, помимо родительских свойств, хранит список узлов, с которыми ему необходимо оперировать. Данный список можно задать как при создании нового экземпляра класса, так и дополнить с помощью функции добавления.
Каждый раз, при старте, необходимо чтобы операции над узлами начинались с первого узла в списке, поэтому в данном классе была переопределена функция Start, выставляющая текущий индекс на первый элемент. Так же данному классу необходимо переопределить функцию Reset, в которой будет обнуляться статус всех узлов заданного списка. Код данного класса приведен ниже на рисунке 3.13.
Рисунок 3.13. Код класса Composite
Остальная логика задается непосредственно в наследуемых классах. Первым рассмотренным классом узлов данного типа будет узел типа Selector, который перебирает список узлов, до тех пор, пока один из узлов не будет успешно завершен.
Вся логика поведения данного класса заложена в функции Update. Поскольку в данном типе узлов узлы располагаются в порядке убывания приоритета, и каждый раз при прохождении дерева необходимо проверять не сработал ли более приоритетный узел, каждый раз необходимо выставлять текущий индекс на первый узел в списке. Затем по порядку вызывается функция Update у всех узлов списка. Если узел не вернул FAILURE, то перебор узлов заканчивается, если вернул, то производится переход к следующему узлу. Если все узлы вернули FAILURE, то узел Selector так же возвращает FAILURE. Код данной функции приведен ниже.
Помимо функции Update, в данном классе имеется функция ResetOthersChildren, которая используется, когда один из узлов был выбран. Данная функция позволяет избежать проблемы “зависания” узлов в состоянии исполнения, описанной выше. В данной функции, через функцию Reset, обнуляются статусы всех узлов, кроме выбранного. Код данной функции приведен ниже на рисунке 3.14.
Рисунок 3.14. Код класса Selector
Следующим будет рассмотрен класс Sequence, который перебирает узлы из списка до тех пор, пока они возвращают состояние SUCCESS. В данном классе, как и в предыдущем переопределена функция Running. Если проверяемый узел вернул состояние отличное от SUCCESS, то перебор узлов останавливается, и возвращается состояние узла. Если узел был в состоянии исполнения, то при следующем запуске функции проверка начинается с узла, на котором была начата прошлая проверка. Если все узлы были выполнены успешно, то работа данного узла считается успешно завершенной. Код данной функции приведен ниже на рисунке 3.15.
Рисунок 3.15. Функция Running класса Sequence
Также в библиотеке данного дерева присутствует класс PreconditionSequence, который по функциональности подобен предыдущему классу, за исключением того, что в данном классе при следующем проходе дерева проверка начинается с первого узла в списке, а не продолжается с предыдущего. Данных класс необходим в тех случаях, когда перед выполнением узлов или действий нужно чтобы выполнялось определенное условие, в таком случае каждый раз при проходе дерева нужно проверять что условие выполняется. Обычный Sequence в таком случае не подходит, поэтому был создан отдельный класс. Код переопределенной функции Running этого класса приведен ниже на рисунке 3.16.
Итак, основные узлы, необходимые для базового дерева поведения разработаны, тем не менее осталось еще два типа узлов, которые необходимы для индивидуальных деревьев поведения агентов - это Probabilitor и Randomizer. Их работа во многом подобна. Первый класс случайно выбирает один узел из списка, основываясь на вероятности их выпадения, другой выбирает узел просто случайно, т.е. каждый из узлов имеет одинаковую вероятность выпадения. Рассмотрим данные классы детальнее.
Рисунок 3.16. Функция Running класса PreconditionSequence
Класс Randomizer организован достаточно примитивно. При старте узла он выбирает случайны узел из списка, затем в функции Running, он вызывает функцию Update у этого узла и возвращает его состояние. Код данного класса представлен ниже на рисунке 3.17.
Рисунок 3.17. Код класса Randomizer
Класс Probabilitor устроен несколько сложнее. Помимо набора узлов в конструктор данного класса передается набор вероятностей для них. Выбор узла осуществляет специальная функция GetCurrentIndex, которая берет случайное число и определяет, в промежуток для какого узла это число попало. Далее, как и в предыдущем классе вызывается функция Update и ее результат становится состоянием узла. Код данного класса представлен ниже на рисунке 3.18. Теперь, когда разработаны все классы дерева, можно переходить к тестированию и внедрению дерева в игру.
Рисунок 3.18. Код класса Probabilitor
3.2 Внедрение дерева поведения агенту-B
Теперь, когда библиотека дерева протестирована и стабилизирована, можно переходить к внедрению дерева в игру. Первым на новую систему будет перенесен агент-B, визуально представляющий собой инопланетного жука.
Поведение данного агента довольно простое, оно во многом повторяет поведение агента с базовым деревом, но агент-B не имеет таких состояний как преследование и побег, помимо этого игрок имеет возможность поймать агента и хранить в инвентаре, что требует создания нового состояния.
Для начала необходимо, чтобы при создании агента инициализировалось его дерево. В системе агента имеется функция OnCreate, которая вызывается при его создании. В это функции помимо выставления прочих параметров будет вызываться функция CreateTree, которая будет конструировать дерево агента. На данный момент не придуман механизм повторного использования элементов дерева, поэтому все деревья собираются вручную в данной функции.
Данная функция создает экземпляры узлов, настраивает связи между ними и связывает итоговое дерево с агентом. Код данной функции, реализующий дерево поведения для агента-B представлен ниже (рисунок 3.19).
Рассмотрим подробнее организацию узлов. Самым приоритетным состоянием у данного агента является состояние, которое вызывается при ловле агента игроком. Данное состояние представляет собой узел типа Sequence, внутри которого находятся еще два узла - Condition и Action. Узел Condition проверяет был ли агент захвачен, а узел Action предпринимает необходимые для такой ситуации действия.
Для проверки был ли захвачен агент в его классе имеется функция isGotCaught, которая возвращает значение свойства класса isCaught. При создании данного класса значение данного свойства приравнивается к false, затем, при захвате агента игроком срабатывает функция OnCaught, которая выставляет данному свойству значение true.
Рисунок 3.19. Код дерева поведения для агента-B
В Action передаются три функции - caught_start, not_running_state и caught_stop. В первой функции агент прекращает свою работу, отключается его модель, анимации и удаляется дерево. Код данной функции представлен ниже на рисунке 3.20.
Рисунок 3.20. Фрагмент кода программы
Следующая функция not_running_state, использующаяся для узла типа Action, является заглушкой для действий, которые не являются длительными и просто возвращает false, что обозначает, что действие завершено.
Последняя функция caught_stop не несет в себе никакой функциональности и тоже является своего рода заглушкой, поскольку данное действие является финальным и завершает деятельность агента.
Следующим по приоритетности состоянием является состояние смерти. Данное состояние, как и предыдущее, является узлом типа Sequence, внутри которого находятся еще два узла - Condition и Action. Однако теперь в узле Condition проверяется осталось ли у агента здоровье, а узел Action проигрывает анимацию смерти и завершает деятельность агента.
Для проверки здоровья в классе агента имеется функция isAlive, которая возвращает значение true, если здоровье агента больше нуля. Для получения текущего здоровья агента используется функция getHealth, возвращающая количество единиц здоровья агента на текущий момент.
В Action передаются функции death_start, not_running_state, death_stop. Как и в предыдущем узле функции not_running_state и death_stop являются заглушками, потому что, как и предыдущее действие, во-первых, данное действие не является длительным, во-вторых, данное действие является финальным и прекращает деятельность агента.
В функции death_start прекращается деятельность агента. Если агент двигался или проигрывал анимацию, до данные функции останавливают свою работу. Если у агента было экипировано оружие, оно убирается. Агент проигрывает анимацию смерти и остается в том же положении, в котором он был на конец анимации. Реализовать это позволяет специальная функция модуля animationsManager под названием playAnimationWithLock. Так же, если в момент смерти игрок находился в поле зрения агента, агента отбрасывает на небольшое расстояние от игрока, создавая иллюзию инерции. Как и в предыдущем состоянии в данном состоянии у агента удаляется дерево поведения, и агент становится предметом, доступным для сбора. Код данной функции приведен ниже на рисунке 3.21.
Рисунок 3.21. Код функции damaged_start
Следующим по приоритетности состоянием является состояния получения урона. Как и предыдущие, данное состояние представляет собой узел типа Sequence внутри которого находятся еще два узла - Condition и Action. В узле Condition проверяется получил ли агент урон, а в узле Action хранятся действия, которые необходимо предпринять для такой ситуации.
Для проверки условия существует функция isGetDamage, которая возвращает значение свойства isDamaged, которое принимает значение true, внутри функции OnHit, которая вызывается при получении агентом урона.
В узле Action хранятся функции damaged_start, damaged_running и damaged_stop. В данном случае, поскольку действие не является финальным, все три функции обладают определенной функциональностью. Рассмотрим их функциональность подробнее.
Функция damaged_start останавливает движение агента, начинает проигрывание анимации получения урона и подписывается на окончание данной анимации. Это необходимо, чтобы понять завершено состояние или нет. Так же в данной функции проигрывается эффект крови и инерции. Код данной функции приведен ниже.
Функция damaged_running проверяет было ли завершено проигрывание анимации, и, если оно не было завершено, что считается что узел все еще находится в состоянии исполнения. Проверка завершения анимации происходит благодаря возможности animationsManager подписаться на конец анимации. Так же в классе имеется свойство, обозначающее что анимация завершена. Данное свойство обнуляется при старте анимации, а при ее завершении становится true в функции, которая вызывается при завершении анимации. Код данной функции так же представлен ниже на рисунке 3.22.
Рисунок 3.22. Фрагмент кода программы
Ну и наконец последняя функция узла Action, которая вызывается при завершении работы узла. Чтобы агент бесконечно не зависал в состоянии получения урона, необходимо, чтобы свойство isDamaged было обнулено. Обнулением данного свойства как раз занимается эта функция, код которой приведен ниже на рисунке 3.23.
Рисунок 3.23. Код функции damaged_stop
При появлении игрока в поле зрения агента, агент переходит в состояние атаки. Переход в это состояние и его логику реализует узел типа Sequence, внутри которого находятся узлы Condition и Sequence. Узел Sequence, в свою очередь содержит два узла типа Action. Рассмотрим функциональность данного состояния поподробнее.
В узел типа Condition передается функция getCurEnemy, которая отображает наличие в данный момент противника в поле зрения агента. Данная функция принадлежит модулю senseScheduler, который отвечает за получение информации о внешнем мире.
В узле Sequence хранятся два узла типа Action. Один из этих узлов отвечает за подготовку к прыжку, другой непосредственно за сам прыжок.
В узел для подготовки к прыжку передаются функции preattack_start, preattack_running, preattack_stop. Как и в узле действия в состоянии получения урона, функция preattack_running проверяет завершена ли анимация. Так же в данном узле функция preattack_stop является заглушкой и не несет в себе функциональности, поэтому рассмотрим подробнее функцию preattack_start. В данной функции, код которой приведен ниже (рисунок 3.24), агент останавливает свое движение, и разворачивается в набавлении игрока. В это же время включается анимация подготовки к прыжку и происходит подписка на конец этой анимации.
Рисунок 3.24. Код функции preattack_start
В узел действия атаки передаются функции attack_start, attack_running, attack_stop. В данном случае все данные функции обладают определенной функциональностью. В функции attack_start агент корректирует свой разворот, на случай если игрок успел сменить позицию, пока агент был в состоянии подготовки к прыжку, затем агент с заданной силой и скоростью совершает прыжок, экипируя оружие и проигрывая в процессе анимацию нахождения в прыжке. Агент находится в состоянии атаки до тех пор, пока не приземлится. Проверка его приземления осуществляется в функции attack_running. При завершении атаки движение и отыгрыш анимации завершаются, а также убирается оружие. Код данных функций приведен ниже на рисунке 3.25.
Последним, но немаловажным состоянием агента является состояние бездействия. Корневой узел этого состояния, как и другие корневые узлы, является узлом типа Sequence. Но в отличие от других узлов он не хранит узел Condition напрямую, а использует узел Invertor и узел Condition из предыдущего состояния. Так же данный узел хранит еще один узел типа Sequence с помощью которого задается модель поведения бездействия. В данном узле находятся два узла типа Action, выполняющихся последовательно.
Рисунок 3.25. Код функции attack_start
В первом узле, отвечающем за анимацию бездействия, хранятся функции idle_start, idle_running, idle_stop. Первая функция останавливает движение агента, запускает анимацию бездействия и таймер на случайный промежуток времени. Вторая функция проверяет истекло ли отведенное на состояние время, и, если истекло, то запускается последняя, завершающая функция, которая останавливает запущенную анимацию.
Рисунок 3.26. Функции состояния бездействия
Во втором узле, отвечающем за бесцельное брожение агента, хранятся функции walk_start, walk_running, walk_stop. Первая функция запускает движение и анимацию агента. Так же она задает направление движения. Если агент вышел за пределы отведенной ему для бездействия зоны, но он разворачивается в направлении центра этой зоны, в ином случае выбирается случайное направление. Как и в предыдущем узле, в данной функции запускается таймер со случайным промежутком времени. Вторая функция так же проверяет прошло ли отведенное на состояние время, а завершающая последняя останавливает движение и анимацию.
После того собрано дерево поведения для агента и реализованы все необходимые для его работы функции, можно переходить к тестированию агента.
Рисунок 3.27. Функции состояния Walk
3.3 Внедрение дерева поведения агенту-P и агенту-H
Как и для предыдущего агента, сначала создается функция, конструирующая дерево, которая будет вызываться из функции создания агента. Данный агент имеет более сложное поведение чем предыдущий - у него имеется состояние преследования игрока, помимо этого данный агент имеет больше анимаций бездействия, ввиду чего поведение бездействия организовано более комплексно. Ниже (рисунок 3.28) представлен код функции, создающий дерево для данного агента.
Состояния получения урона и смерти по своей организации аналогичны состояниям предыдущего агента, поэтому они не будут подробно описываться в данном разделе.
Поскольку, в отличие от предыдущего агента агенты данного типа имеют состояние преследования, то данное состояние и состояние боя были объединены в одно промежуточное состояние, переход в которое осуществляется, когда в зоне видимости агента находится противник. Данное промежуточное состояние представляет собой узел типа Sequence, внутри которого находится узел типа Condition, определяющий есть ли враг и узел типа Selector, который выбирает преследовать агенту врага или атаковать его.
Наличие противника в зоне видимости агента проверяется так же, как и у предыдущего агента, так что подробности организации данного узла будут опущены.
Рисунок 3.28. Код дерева поведения для агента-P
Гораздо важнее рассмотреть организацию узла типа Selector. Данный узел хранит в себе два узла типа Sequence, каждый из которых подразделяется на узел-условие и узел-действие. Первый узел Sequence отвечает за состояние атаки, второй за преследование. Рассмотрим подробнее их организацию.
Узел, отвечающий за атаку состоит из узла Invertor, который содержит в себе узел с условием, что противник находится далеко, и действия атаки. Для проверки дальности нахождения противника в классе имеется функция isEnemyFar, которая запрашивает у модуля senseScheduler “чувство” что противник находится рядом. Если противник действительно находится рядов, то вызов данной функции возвращает true, иначе - false. Код функции isEnemyFar приведен ниже на рисунке 3.29.
Рисунок 3.29. Код функции isEnemyFar
Узел Action отвечающий непосредственно за действие атаки организован так же, как и атака у предыдущего агента, поэтому подробно рассматриваться не будет. Единственным отличием является выравнивание ориентации на противника в процессе атаки, реализуемое с помощью функции attack_running, код которой приведен ниже на рисунке 3.30.
Рисунок 3.30. Код функции attack_running
Узел преследования состоит из узла Condition, который повторно используется из предыдущего узла и узла действия. Работа узла, определяющего что противник находится далеко уже была описана, поэтому сфокусируемся на узле - действии.
В данный узел традиционно передаются три функции chase_start, chase_running, chase_stop. Рассмотрим их функциональность подробнее.
Функция chase_start начинает движение агента и включает анимацию бега. Помимо этого, она поворачивает агента в сторону противника и устанавливает данного противника как основную цель. Функция chase_running корректирует поворот агента при преследовании и всегда возвращает true. Это значит что данное состояние является потенциально бесконечным, но по сути оно таким не является, потому что при приближении к врагу сработает более приоритетное состояние атаки, а у данного состояния будет вызвана функция выхода chase_stop, которая останавливает движения и убирает противника как цель преследования. Функции данного состояния приведены на рисунке 3.31, 3.32.
Рисунок 3.31. Функции состояния Chase
Рисунок 3.32. Функции состояния Chase
Еще одним состоянием несколько отличающимся по реализации от предыдущего агента является состояние бездействия. Помимо обычных анимаций движения и бездействия данный агент имеет пару уникальных краткосрочных анимаций бездействия - например, почесывание или мотание головой. Данные анимации не должны проигрываться часто или в какой-то последовательности со стандартными состояниями брожение и бездействия, поэтому для реализации данного состояния был использован узел типа Probabilitor. Для стандартных бездействия и брожения были выставлены вероятности выбора в 40%, для коротких анимаций бездействия в 20%. Стандартные бездействия и брожения реализованы так же, как и в системе предыдущего агента, поэтому подробно рассмотрено будет только действие короткого бездействия.
Данный тип бездействия так же реализуется через узел типа Action, в который переданы функции shortIdle_start, shortIdle_running, shortIdle_stop. Рассмотрим подробнее их организацию. Агент хранит массив с набором анимаций. В функции shortIdle_start выбирается случайная анимация из этого массива и воспроизводится. Так же запускается таймер на случайное время до 1,5 секунды. Функция shortIdle_running проверяет не истекло ли время, а функция shortIdle_stop останавливает работу анимации при завершении работы узла. Код данных функций представлен ниже на рисунке 3.33.
Рисунок 3.33. Функции состояния Idle
Поведение агента-H, в целом, аналогично поведению агента-P, поэтому его разработка не будет подробно расписана. Основным различием в архитектуре является состояние бездействия, поэтому оно будет рассмотрено подробнее. Как было указано в предыдущей главе, данное состояние представлено узлом типа Randomizer, который содержит в себе узлы для бесцельного брожения и бездействия. Узел типа Action для бесцельного брожения аналогичен такому же узлу у агента-P. Узел типа Action отличается от подобного узла у агента-P функцией idle_start, в которой выбирается одна из нескольких анимаций бездействия. Код данной функции проведен ниже на рисунке 3.34.
Рисунок 3.34. Код функции idle_start
Заключение
программный интерактивный агент геймплей
Несмотря на значительный прогресс в сфере разработки игр, проблема создания иллюзии интеллектуального поведения все еще остается актуальной. От реалистичности и непредсказуемости поведения агента напрямую зависит вовлеченность игрока и время геймплея, поэтому необходимо создать агента, поведение которого будет подобно поведению игрока, но при этом необходимо чтобы системы, отвечающие за решения и действия агента, не потребляли большой объем ресурсов. В контексте данных требований данная проблема так и не была решена.
В рамках данной работы было произведена работа по улучшению реалистичности поведения интерактивных агентов в игре Exoplanet First Contact, путем создания новой системы на основе деревьев решений. Для создания новой системы следующие задачи были выполнены.
1. Была изучена система уже имеющаяся на данный момент в игре и выявлены ее недостатки.
2. Были изучены подходы к разработке интерактивных агентов, основные алгоритмы, на основе анализа был выбран подход на основе дерева поведения.
3. Была спроектирована новая система с использованием средств объектно-ориентированного проектирования.
4. Данная система была реализована и внедрена в игру.
В результате выполнения этих задач была создана новая система, которая позволила устранить проблемы, вызванные предыдущей системой, и улучшить реалистичность поведения агентов в целом. Их поведение по-прежнему остается предсказуемым, и агенты не способны выходить за рамки заданных их сценариев, тем не менее это является приемлемым в рамках данной игры.
Литература
1. Bourg M.D., Seeman G. AI for Game Developers. San Francisco: O'Relly, 2004.
2. Buckland M. Programming AI by Example. Plano: Wordware Publishing, 2005.
3. Champandard A.J. AI Game Development: Synthetic Creatures with Learning and Reactive Behaviors. Seattle: New Riders Games, 2003.
4. Champandard A.J., Dunstan P. The Behavior Tree Starter Kit. In: S. Rabin (Ed.), Game AI Pro: Collected Wisdom Of Game AI Professionals. Boca Raton : CRC Press, 2014.
5. Daw M. Real-World Behavior Trees. In: S. Rabin (Ed.), Game AI Pro: Collected Wisdom Of Game AI Professionals. Boca Raton : CRC Press, 2014.
6. Hilburn D. Simulating Behavior Trees: A Behavior Tree/Planner Hybrid Approach. In: S. Rabin (Ed.), Game AI Pro: Collected Wisdom Of Game AI Professionals. Boca Raton : CRC Press, 2014.
7. Kolhojf F., Loviscach J. Level Up for Finite State Machines: An Interpreter for Statecharts. In: S. Rabin (Ed.), AI Game Programming Wisdom. Hingham: Charles River Media, 2002.
Размещено на Allbest.ru
Подобные документы
Анализ и виды интеллектуальных агентов в системе дистанционного обучения и их характеристики. Построение интеллектуального агента глоссария на платформе Jadex с помощью XML формата. Среда разработки и описание интеллектуального агента с помощью BDI.
курсовая работа [113,6 K], добавлен 10.02.2011Обзор средств создания обучающих программ и формирование требований к электронному учебнику. Исследование этапов разработки интерактивного обучающего ресурса. Выбор инструментов реализации. Создание интерфейсной части приложения, проектирование тестов.
дипломная работа [3,2 M], добавлен 20.05.2013Технология программных агентов. Форматы метаданных, использующиеся для описания электронных ресурсов. Разработка интеллектуальных агентов. Среда разработки Jadex для построения интеллектуальных агентов. BDI модель интеллектуального агента ресурсов.
курсовая работа [279,8 K], добавлен 20.02.2011Диаграмма прецедентов взаимодействия игрока и программного продукта. Требования к пользовательскому интерфейсу. Диаграмма состояний проектируемого приложения. Выбор инструментальных средств разработки. Проектирование алгоритмов и иерархии классов.
дипломная работа [9,9 M], добавлен 20.03.2017Алгоритмическое представление и описание правил игры "Эволюция". Построение диаграммы прецедентов. Разработка графического интерфейса пользователя. Реализация интерфейса в среде Unity. Структура файла сохранения игры. Проектирование поведения компьютера.
дипломная работа [3,3 M], добавлен 18.02.2017Понятие информации и роль компьютерных и Интернет-технологий в современном мире. Плюсы и минусы внедрения ERP-систем. Языки программирования для разработки Web-приложений. Методология разработки интерактивного справочника. Расчёт эксплуатационных затрат.
дипломная работа [962,7 K], добавлен 13.10.2012Обзор методов и средств реализации поставленной задачи. Описание компьютерной игры "Японские кроссворды". Обоснование инструментария разработки программного продукта. Алгоритмический анализ задачи. Графический интерфейс и лингвистическое обеспечение.
курсовая работа [725,4 K], добавлен 27.08.2013Выбор языка программирования и средств реализации поставленной задачи. Диаграмма прецедентов использования лифта. Построение основной диаграммы классов. Создание интерфейса, с помощью которого пользователь мог бы легко понять моделирование лифта.
курсовая работа [477,2 K], добавлен 01.05.2016Проектирование интерактивного справочника магазина "Азарт", для реализации продукции посредством сети Интернет. Разработка базы данных, описание программы и составление руководства для оператора. Экспериментальное исследование разработанного продукта.
дипломная работа [3,8 M], добавлен 06.06.2014Создание электронного учебника, написанного на языке гипертекстовой разметки HTML. Характеристика программного обеспечения ЭВМ, необходимого для создания и эксплуатации информационной системы. Алгоритм функционирования системы, отладка программы.
курсовая работа [1,0 M], добавлен 22.12.2012