netlib.narod.ru< Назад | Оглавление | Далее >

Игроки, персонажи, враги — о боже!

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

Вы должны работать с персонажем игрока (который для краткости будет называться PC, player character), который находится под управлением игрока, независимыми персонажами (их называют NPC, non-player character), которые ходят вокруг, населяя мир, и ведут собственную жизнь, и врагами (называемыми MC, monster character), которые только и хотят убить вас.

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

ПРИМЕЧАНИЕ
Персонаж игрока (player character) — это альтер-эго игрока. Независимые персонажи — это персонажи, над которыми игрок не имеет непосредственного контроля. Часто NPC — это горожане, торговцы, банкиры, лекари и враги. Да, даже враги являются NPC, но в этой книге я провожу четкое различие между NPC и MC. NPC не атакуют игрока, а MC — атакуют.

Поговорим о затруднениях! Как обрабатывать каждый крошечный аспект этих типов персонажей? Вы можете сначала разбить эти аспекты на следующие категории, а затем определить, как обрабатывать каждую из них:

Определение персонажей в вашей игре

Развертывание персонажей в вашей игре — одна из наиболее трудных работ, с которой вы встретитесь. Даже на базовом уровне код, необходимый для управления игровыми персонажами, может быть весьма замысловатым. Что же делать скромному программисту? Не торопясь начать работу с самого начала, и все будет хорошо.

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

Способности персонажа

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

Типичная ролевая игра присваивает способностям числовые значения — чем больше значение, тем лучше персонаж в терминах конкретной способности. Использование чисел также упрощает вычисления. Например, типичная способность в ролевой игре — сила. Скажем, сила измеряется от 0 (хиляк) до 999 (супергерой). Тогда у среднего человека сила должна находиться в диапазоне от 100 до 150.

Сделаем следующий шаг и предположим, что персонаж должен обладать определенной силой, чтобы выполнить какое-то действие. Например, выбивание заклинившей двери требует, чтобы сила человека была 100 или больше. Подъем огромного булыжника требует силы 500. Улавливаете идею?

Какими еще способностями должен обладать персонаж? Это важный для вашей игры вопрос, так что тщательно обдумайте его. Что будет происходить в вашей игре требующее назначения способностей персонажу? В общем случае можно начать со способностей, показанных в таблице 12.1.


Таблица 12.1. Способности персонажа



Способность Описание

Атака Количество повреждений, которое может наносить персонаж. Значение атаки базируется на том, сколько повреждений может нанести персонаж голыми руками, но добавление оружия увеличивает значение атаки (в зависимости от типа оружия)
Защита В полном комплекте доспехов вы чувствуете себя неуязвимым, но когда на вас только джинсы и футболка вы беззащитны. Защита определяет уровень вашей уязвимости — чем больше значение, тем меньше наносимый вам ущерб. У каждого персонажа есть природная защита, будь это кожа или панцирь. Использование брони или других элементов экипировки увеличивает защиту
Проворность Способность быстро и ловко перемещаться. Проворный персонаж быстрее передвигается и даже может легко уклониться от атаки
Интеллект Способность персонажа контролировать свой разум. Полный контроль над разумом необходим для произносящих заклинания магов и подобных персонажей. Чем выше интеллект, тем больше шансов, что произнесенное персонажем заклинание поразит намеченную цель
Сопротивляемость Защита помогает уменьшить физический вред от физических атак, а сопротивляемость уменьшает вред от магических атак. Чем выше сопротивляемость, тем меньший ущерб наносят вам заклинания. Стопроцентная сопротивляемость делает персонаж невосприимчивым к магии!
Меткость Некоторые персонажи хорошо целятся, у других это вызывает затруднения. Меткость персонажа определяет насколько успешно он поражает намеченную цель во время атаки. Эта способность увеличивается или уменьшается в зависимости от типа используемого оружия или другой экипировки


Каждой способности в вашей игре назначается числовое значение, и каждая способность по-разному использует это число. Атака — это количество повреждений, наносимых невооруженным персонажем. Если ее значение 100, персонаж атакуя может нанести до 100 очков повреждений. Здесь позвольте мне сказать, что персонаж может получить достаточно много очков повреждений, прежде чем погибнет.

СОВЕТ
Чтобы игра была интереснее можно использовать некий уровень повреждений, когда слабый персонаж атакует хорошо защищенного, не имея возможности причинить ему ущерб. Позже, в разделе «Сражения и персонажи», вы увидите, как модифицировать способность атаковать у слабого персонажа, чтобы он мог нанести какие-то повреждения персонажу с высокой способностью защищаться.

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

Вы можете изменять способности атаки и защиты с помощью предметов. Отдельные предметы, такие как оружие, имеют собственные значения, которые добавляются к значению атаки персонажа (снова, чтобы больше узнать об этих модификаторах, обратитесь к разделу «Сражения и персонажи»).

В терминах проворности, у персонажа есть врожденный шанс уклониться от атаки, который увеличивается по мере того, как персонаж становится сильнее. Проворность измеряется от 0 до 999 — чем больше значение, тем выше шанс уклониться от атаки. Чтобы определить шансы уклонения от атаки берется случайное число и сравнивается со значением проворности. Если случайное число меньше или равно значению способности проворства, персонаж уклонился от атаки.

Способности интеллекта находятся в диапазоне от 0 и выше. Интеллект определяет шансы того, что заклинание достигнет своей цели. У заклинания есть собственные шансы на успех, но когда вы добавляете способности интеллекта, шансы увеличиваются. Если заклинатель имеет значение интеллекта 100, у заклинания на 100 процентов больше шансов сработать.

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

И, наконец, меткость — эта способность измеряется числом в диапазоне от 0 до 999. Снова случайное число сравнивается со значением меткости; если случайное число больше значения способности, значит атака не попала в цель.

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

Атрибуты персонажа

Атрибуты персонажей похожи на способности, за исключением того, что атрибуты определяют различные параметры персонажа. Физическое здоровье — это атрибут; от его изменения зависит, сколько ран может выдержать персонаж. Эти раны могут быть вылечены и в таком случае здоровье персонажа увеличивается.

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


Таблица 12.2. Атрибуты персонажа



Атрибут Описание

Здоровье Здоровье персонажа может находиться в диапазоне от нуля и выше. Чем больше значение, тем больше ранений может вынести персонаж. Нулевое значение означает, что персонаж мертв
Мана Мана определяет количество магической энергии, имеющейся в запасе у персонажа. Каждое заклинание требует определенного количества маны, расходуемого при каждом произнесении
Очки опыта Достаточно часто способности и атрибуты персонажей увеличиваются. Уровни опыта — это ступени увеличения. Уровень опыта определяется по количеству очков опыта, как будет показано позже в разделе «Увеличение опыта и силы»
Уровень опыта Достаточно часто способности и атрибуты персонажей увеличиваются. Уровни опыта — это ступени увеличения. Уровень опыта определяется по количеству очков опыта, как будет показано позже в разделе «Увеличение опыта и силы»


Здоровье персонажа измеряется в очках здоровья (health points, HP), а мана измеряется в очках маны (mana points, MP). MP нужны персонажу для произнесения заклинаний. Каждый раз при произнесении заклинания тратится определенное количество MP; если маны недостаточно, заклинание не может быть произнесено.

Опыт и уровни опыта позволяют игрокам следить за прогрессом и увеличением силы их персонажей. Опыт относится только к персонажам игроков и обсуждается в разделе «Увеличение опыта и силы» далее в этой главе.

Дополнительные статусы персонажа

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

Эти дополнительные статусы магические по своей природе и помогают повернуть ход игры в критических точках. В таблице 12.3 перечислены некоторые дополнительные статусы, используемые во многих играх (и в этой книге), и описано действие, производимое ими на персонажа.


Таблица 12.3. Дополнительные статусы и их действие



Статус Эффект

Отравление Отравление медленно уменьшает здоровье персонажа, пока не будет принято противоядие. В этой книге отравленный персонаж теряет два очка здоровья каждые четыре секунды
Сон Спящий персонаж ничего не может делать, пока не проснется. Разбудить спящего может удар другого персонажа, или придется ждать, пока эффект не пройдет сам (с 4-х процентной вероятностью)
Паралич Парализованный персонаж ничего не может делать точно так же, как и спящий. Но парализованный персонаж может снова начать двигаться только если кто-то снимет с него заклятье или эффект не пропадет сам (с двухпроцентной вероятностью)
Слабость Способности к атаке и защите у ослабленного персонажа уменьшаются наполовину
Усиление Способности персонажа к атаке и защите увеличиваются на 50 процентов
Зачарованность Сопротивление к магии у зачарованного персонажа снижается наполовину
Барьер Сопротивляемость защищенного магическим барьером персонажа увеличивается на 50 процентов
Ошеломление Ошеломленные персонажи теряют половину интеллектуальных способностей, пока не пройдет ошеломление
Неуклюжесть Проворность неуклюжих персонажей уменьшается на 25 процентов; это уменьшает возможность уклоняться от физических атак
Устойчивость Твердо стоять на ногах — вот значение этого статуса, и у таких персонажей проворность увеличивается на 50 процентов
Замедление Обычно у персонажа есть установленная скорость перемещения (измеряемая в блоках за секунду), но у замедленных персонажей эта скорость наполовину уменьшается
Ускорение Увеличивает скорость перемещения персонажа на 50 процентов
Слепота У ослепленного персонажа на 25 процентов больше шансов промахнуться мимо цели при атаке
Орлиный глаз Персонажи с орлиным глазом на 50 процентов чаще попадают в цель при атаке
Немота Немые персонажи не могут произносить заклинания, пока заклинание не будет снято


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

Классы персонажей

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

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

Классифицировать персонажа только как человека недостаточно. Вместо этого подходящей классификацией в ряде случаев могла бы быть — человек-воин. Это применимо ко всем персонажам. Например, вы можете классифицировать дракона как ледяного дракона, огненного дракона, каменного дракона и т.д.

Причины необходимости определения класса коренятся в проекте вашей игры (обратитесь к главе 11, «Определение и использование объектов», за дополнительной информацией об использовании классов по отношению к предметам и персонажам). Предметам в вашей игре назначена переменная использующего их класса, состояние которой определяет, какие классы могут воспользоваться конкретным предметом. Широкий меч может быть на вооружении только у персонажа, являющегося человеком-воином или гномом, а свиток с заклинаниями может использовать только класс магов.

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

Классы персонажей назначаются по номерам и полностью зависят от вашего проекта.

Действия персонажей

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

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


Таблица 12.4. Действия персонажа



Действие Описание

Атака Размахивая оружием, персонаж набрасывается на другого персонажа, находящегося перед ним. В этой книге реализован только один тип атаки, но в своей игре вы можете добавить другие типы атак.
Произнесение заклинания Демонстрируется древнее искусство магии, когда персонаж выполняет небольшие ритуалы с целью вызвать разрушающее или исцеляющее заклинание
Перемещение Ходьба, полет, бег — все это способы перемещения по вашему игровому миру. Вашему персонажу необходим стандартный метод передвижения, и в этой книге таким методом будет ходьба
Ожидание Когда персонаж стоит, он находится в ожидании. Персонаж может выглядеть скучающим, настороженным или постоянно озираться вокруг, но, независимо от того что он делает, его считают ожидающим
Использование предмета Когда персонаж решает начать использовать какой-нибудь из собранных им предметов, начинается действие этого предмета
Разговор Общение с игровыми жителями — это действие разговора. Игроки хотят, чтобы их персонажи не стояли при этом неподвижно, а показывали, как они взаимодействуют друг с другом — двигали руками, шевелили губами или что-нибудь другое
Ранение Персонаж, задетый атакой, неважно магической или физической, обычно требует несколько секунд на восстановление. Период восстановления называется действием ранения и в это время игрок не может выполнять никакие другие действия
Смерть Получив достаточное количество повреждений персонаж умирает. Однако недостаточно просто умертвить его; удаление персонажа из игры сопровождается драматической анимацией гибели


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

Возьмем, к примеру, действие атаки. Предположение, что это действие может выполняться в любой момент игры, не означает, что игрок может сидеть и непрерывно рубить. Вы используете ограничивающий фактор, называемый перезарядкой (charge), который задает временную задержку между атаками. Фактически, все действия за исключением ожидания и перемещения не работают во время перезарядки персонажа.

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

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

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

Персонажи игроков

Мир вращается вокруг ваших персонажей игроков (PC), поэтому значительная часть разработки игры связана с управлением ими. У персонажей игроков больше всего действий и для них открыто больше всего возможностей. Кроме ранее упомянутых действий персонажей, у PC есть возможность управлять их игровыми ресурсами, в которые входят предметы и магические заклинания. Кроме того, идет построение персонажа, чтобы сделать его более сильным.

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

Передвижение игрока

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

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

Управление ресурсами

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

В главе 11 было рассмотрено создание предметов, но вопросы их использования не были полностью раскрыты. Фактически, предметы бесполезны, если нет кого-то, кто мог бы воспользоваться ими, так что сейчас самое время рассмотреть, как игроки взаимодействуют с этими предметами. Эликсиры можно выпивать, оружие и броню носить, а золото тратить — и все это относится к тому, как проектировать эти предметы (глава 11) и как игроки могут использовать их!

Ресурсы это не только предметы, но и магические заклинания. Заклинания исключительно полезные инструменты, и чтобы сделать что-нибудь в мире ваш персонаж должен изучить столько заклинаний, сколько возможно. Как игрок учит используемые заклинания? Через непрерывное увеличение опыта!

Увеличение опыта и силы

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

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

У персонажей есть очки опыта (experience points) и уровень опыта (experience level). Каждый эпизод увеличивает количество очков опыта персонажа. Через заданные интервалы очков опыта у персонажа повышается уровень опыта. При повышении уровня опыта персонаж получает дополнительные преимущества, которые обычно включают увеличение способностей и заклинания.

В этой книге очки опыта персонажи получают за убийство врагов. Количество получаемых очков закодировано в описаниях персонажей. Убийство глупого мелкого демона может дать вашему персонажу ничтожные 10 очков опыта, в то время как победа над красным драконом двухсотого уровня принесет персонажу колоссальные 20 000! Вы определяете количество опыта, извлекаемого из убийства врагов.

Установка уровней опыта и получаемых преимуществ — это часть работы проектировщика. Персонажу первого уровня может требоваться 500 очков опыта, чтобы достичь уровня 2, а переход на третий уровень может требовать 1 200 очков опыта.

Определяя количество очков опыта, необходимое для конкретного уровня, вы можете использовать в качестве критерия повышения уровня среднее количество опыта, получаемого от убийства врагов в конкретной области игры. Например, если ваш персонаж находится на «демонической пустоши», каждый убитый демон приносит ему 10 очков опыта, и вы хотите, чтобы для перехода на второй уровень игрок убил не менее 20 монстров, это означает, что уровень 2 требует 200 очков опыта.

Помимо дополнительных преимуществ, с увеличением уровня опыта увеличиваются способности и атрибуты. У персонажа увеличиваются максимальные значения очков здоровья и маны; игрок становится сильнее и может нанести больше повреждений. Произносить заклинания становится легче, и с увеличением количества очков маны ваш персонаж может произнести больше заклинаний, прежде чем его силы иссякнут. Что на самом деле будет происходить с персонажем игрока при увеличении уровня, определяется проектом игры. Я не буду слишком углубляться в этот вопрос (но вы увидите, как я увеличиваю способности персонажа при увеличении количества очков опыта в примере игры из главы 16, «Объединяем все в законченную игру»).

Независимые персонажи

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

Пересмотрев относящиеся к персонажам аспекты, вы увидите, что для NPC требуется достаточно мало управления:

Другие важные аспекты управления, такие как управление ресурсами, неприменимы к NPC. Предметы, которые несут NPC, записаны в проекте персонажа. Изредка у них есть свобода выбора, что нести или использовать. Также, поскольку игрок не управляет NPC, у них есть разработанная «цельная жизнь», поэтому развитие персонажа не влияет на них.

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

Например, созданная Sega популярная игра Phantasy Star Online использует минимум взаимодействий с NPC. Есть несколько лавочников, продающих предметы, банк для хранения денег и других предметов, лечащие раны целители и гильдии, отправляющие вас на поиски. Игровые монстры по многим стандартам просто тупы; они бродят вокруг, пока не увидят персонажа игрока, а затем бросаются на него в атаку. Такой вариант управления NPC замечательно подходит для быстроидущих ролевых игр, но недостаточно хорош для более серьезных игроков.

С другой стороны, такие игры, как Ultima Online от Origin заполнены NPC. Как сравнить эти две игры? NPC в Ultima Online достаточно ограничены в терминах их искусственного интеллекта — они могут только ходить вокруг, следовать за персонажем, оставаться неподвижными, атаковать, лечить, охранять и действовать как банкиры и лавочники. Настоящая магия Ultima Online — используемые NPC скрипты. У каждого NPC может быть присоединенный скрипт, позволяющий NPC выполнять дополнительные действия. Например, разговор с NPC не приведет ни к чему важному, пока игрок не даст NPC специальный предмет, в результате чего NPC завещает игроку магический меч.

Каким путем пойти? Если вы хотите сверхсложный искусственный интеллект, пусть будет так, но ваш игровой мир должен быть буквально напичкан сотнями NPC, и чем проще с ними обращаться, тем лучше.

В этой книге я моделирую схему управления NPC из Ultima Online. NPC назначаются определенные движения, и для них разрешено использовать скрипты. Для простоты PC должны взаимодействовать с NPC перед активацией скрипта.

Персонажи врагов

Враги — это скрытые NPC. MC программируются точно так же, как NPC, но обладают двумя дополнительными формами интеллекта — охотой и атакой персонажей игроков.

Враги могут ходить вокруг, следовать за кем-то, укрываться, патрулировать по маршруту и даже стоять неподвижно — точно так же, как и обычные NPC. Однако, когда персонаж игрока попадает в пределы досягаемости атаки MC (физической или магической), начинается игра по правилам. Враг будет последовательно атаковать ближайшего PC, пока этот персонаж не умрет или не покинет пределы досягаемости атак врага.

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

Враги — это единственный тип персонажей, на которые игрок может напасть (или должен быть в состоянии напасть). Поскольку PC получает опыт только убивая врагов, вы должны обеспечить наличие в вашей игре необходимого количества врагов.

Графика персонажей

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

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

Как вы узнаете в последующем разделе, «Создание класса управления персонажем», сетка каждого персонажа загружается в объект cMesh, а каждая анимация загружается в объект cAnimation. Эти два объекта графического ядра уникальны для каждого персонажа в вашей игре (несколько экземпляров одного и того же персонажа используют только одну сетку и объект анимации). Ваша задача — загрузить эти сетки и анимации и визуализировать персонажи на экране в каждом кадре.

ПРИМЕЧАНИЕ
Две замечательные модели с поддержкой анимации действий находятся на прилагаемом к книге CD-ROM (посмотрите папки \Resources\Models\Yodan и \Resource\Models\Spawn). Мы будем использовать эти сетки в последующих главах. Уделите некоторое время, чтобы загрузить эти модели в MilkShape 3D и посмотреть, как каждая из моделей анимируется, как вы можете модифицировать ее и как можно использовать эти модели и анимации в своих проектах.

Перемещение персонажей

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

Если вы смотрели демонстрационные программы к главам 8 и 9 с прилагаемого к книге CD-ROM, то уже знакомы с созданной мной системой управления игроком (которую я предпочитаю называть непосредственным управлением). Эти демонстрационные программы позволяют вам перемещать персонажа игрока, используя клавиши управления курсором и мышь.

Для демонстрационных программ с видом от первого лица (таких, как NodeTree из главы 8), нажатие на клавишу перемещения курсора вверх передвигает персонаж вперед, нажатие на клавишу перемещения курсора вниз передвигает игрока назад и нажатие на клавиши перемещения курсора вправо и влево перемешают игрока вправо и влево соответственно. Передвижение мыши вращает точку просмотра. Для демонстрационных программ с видом от третьего лица (таких, как 3Din2D из главы 9), клавиши управления курсором перемещают персонаж в соответствующем направлении (нажмите клавишу перемещения курсора вверх для перемещения вверх, клавишу перемещения курсора влево для перемещения влево и т.д.).

ПРИМЕЧАНИЕ
Термины «вид от первого лица» и «вид от третьего лица» описывают местоположение точки зрения игрока. Для игр с видом от первого лица точка зрения игрока находится в глазах персонажа. Для игр с видом от третьего лица используется другая точка зрения — сзади, сбоку или другая перспектива.

При работе с PC редко применяется что-либо более сложное, чем эти системы управления. Однако, вещи становятся более сложными, когда игрок начинает ходить, сталкиваясь с другими персонажами и объектами (такими, как двери). Вы уже видели, как выполняется обнаружение столкновений, так что это не должно составить для вас большой проблемы.

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

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

Управление персонажем игрока

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

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

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

Управление направлением

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


Рис. 12.1. При управлении направлением персонаж перемещается в направлении, определяемом нажатой клавишей

Рис. 12.1. При управлении направлением персонаж перемещается в направлении, определяемом нажатой клавишей. Клавиша перемещения курсора вверх перемещает персонаж на экране вверх (от камеры), клавиша перемещения курсора влево перемещает персонаж влево и т.д. Этот метод управления при перемещении персонажа принимает во внимание местоположение камеры, так что клавиша перемещения курсора вверх всегда перемещает персонаж вверх (вдаль), независимо от угла вида камеры


Для перемещения персонажа при использовании управления направлением вам необходимо знать угол наклона камеры (относительно оси Y), местоположение персонажа и направление, в котором персонаж должен перемещаться (0.0 означает вверх или от камеры, 1.57 означает вправо, 3.14 означает вниз и 4.71 означает влево).

Предположим, что координаты персонажа представлены парой переменных:

float XPos, ZPos; // YPos = высота, она не требуется

Угол наклона камеры (относительно оси Y) представляется переменной:

float CameraYAngle; // Угол вида

И, наконец, у вас есть направление и расстояние, на которое вы хотите переместить персонаж:

float Direction; // Направление перемещения
float Distance; // Как далеко переместиться

 

ПРИМЕЧАНИЕ
Использование значения расстояния может вызвать проблемы, поскольку вы не контролируете скорость обновления кадров в вашем движке. Когда игра работает на компьютере с полной скоростью и обновляется 100 раз в секунду, игровой персонаж может перемещаться гораздо быстрее, чем на компьютере с частотой обновления 30 раз в секунду. Вам необходимо ограничить количество обновлений игры в каждом кадре, как я делаю позже в разделе «Использование cCharacterController» и главе 16.

Затем создадим пример сцены, в котором персонаж находится в начале координат и хочет переместиться вверх (относительно вида камеры); камера располагается справа от начала координат вдоль оси X и направлена влево, как показано на рис. 12.2.

Рис. 12.2. Камера направлена влево, а персонаж перемещается вверх

Рис. 12.2. Камера направлена влево, а персонаж перемещается вверх


Вот устанавливаемые переменные и проводимые вычисления:

XPos = ZPos    = 0.0f;   // Местоположение персонажа
CameraYAngle   = -1.57f; // Угол камеры
Direction      = 0.0f;   // Направление перемещения
float Distance = 4.0f;   // Расстояние перемещения

// Новое направление (угол) для перемещения
float NewAngle = CameraYAngle + Direction - 1.57f;

// Перемещение персонажа
XPos += (float)cos(NewAngle) * Distance;
ZPos += (float)-sin(NewAngle) * Distance;

К этому моменту персонаж переместился на 4 единицы от камеры, а значит он сейчас находится в точке X = –4.0, Z = 0.0, куда вы и хотели его поместить. Остается только задача определения высоты персонажа (для чего используется трехмерный движок).

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

Управление поворотом

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

В ряде аспектов управление поворотом лучше чем управление направлением, поскольку вычисления перемещения более просты. Персонажу здесь нужно хранить значение направления, однако, оно представляет направление, в котором обращено лицо персонажа (0.0 означает направление вдоль положительного направления оси Z, 1.57 — вдоль положительного направления оси X, 3.14 — вдоль отрицательного направления оси Z и 4.71 — вдоль отрицательного направления оси X). Подразумевается, что используется следующая переменная направления:

float Facing = 0.0f; // Направление лица персонажа

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

// Используем ядро ввода:
// Keyboard = ранее инициализированный
//            объект cInputDevice для клавиатуры
Keyboard.Read();

// Поворот влево?
if(Keyboard.GetKeyState[KEY_LEFT] == TRUE)
    Facing -= 0.01f; // Поворот влево на .01 радиан

// Поворот вправо?
if(Keyboard.GetKeyState[KEY_RIGHT] == TRUE)
    Facing += 0.01f; // Поворот вправо на .01 радиан

С учетом угла из переменной Facing перемещение вперед и назад выглядит так:

// Перемещение вперед?
if(Keyboard.GetKeyState[KEY_UP] == TRUE) {
    XPos += (float)cos(Facing-1.57f) * Distance;
    ZPos += (float)-sin(Facing-1.57f) * Distance;
}
// Перемещение назад?
if(Keyboard.GetKeyState[KEY_DOWN] == TRUE) {
    XPos += (float)-cos(Facing-1.57f) * Distance;
    ZPos += (float)sin(Facing-1.57f) * Distance;
}

Управление от первого лица

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

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

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

float XAngle = 0.0f, YAngle = 0.0f; // Углы просмотра персонажа

Две показанные переменные хранят углы просмотра, которые модифицируются, когда игрок перемещает мышь. Вот код для изменения этих углов:

// Подразумевается использование ядра ввода
// Mouse = ранее созданный cInputDevice для мыши
// То же относится и к клавиатуре
Mouse.Read();
Keyboard.Read();

// Поворот персонажа на основе угла мыши
XAngle += Mouse.GetYDelta() / 200.0f;
YAngle += Mouse.GetXDelta() / 200.0f;

// Перемещение персонажа
if(Keyboard.GetKeyState[KEY_UP] == TRUE) {
    XPos += (float)cos(YAngle-1.57f) * Distance;
    ZPos += (float)-sin(YAngle-1.57f) * Distance;
}
if(Keyboard.GetKeyState[KEY_DOWN] == TRUE) {
    XPos += (float)-cos(YAngle-1.57f) * Distance;
    ZPos += (float)sin(YAngle-1.57f) * Distance;
}
if(Keyboard.GetKeyState[KEY_LEFT] == TRUE) {
    XPos += (float)cos(YAngle-3.14f) * Distance;
    ZPos += (float)-sin(YAngle-3.14f) * Distance;
}
if(Keyboard.GetKeyState[KEY_RIGHT] == TRUE) {
    XPos += (float)cos(YAngle) * Distance;
    ZPos += (float)-sin(YAngle) * Distance;
}

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

Управление независимыми персонажами

Как вы можете предположить после прочтения предыдущих разделов, управлять игроком относительно просто. Теперь начинается трудная часть — управление игровыми NPC. Этот раздел покажет вам различные методы перемещения ваших игровых NPC.

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

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

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

Ожидание

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

Блуждание по области

Такие игры, как Ultima Online позволяют NPC бродить по заданной области, которая может быть целым уровнем или задаваемой вами его отдельной частью. Чтобы сохранить вещи простыми, вы можете задать диапазон, в котором хотите разрешить блуждания персонажа, в виде заданного диапазона координат (как показано на рис. 12.3). Эти координаты хранятся в следующих переменных:

float WanderMinX, WanderMinY, WanderMinZ;
float WanderMaxX, WanderMaxY, WanderMaxZ;

Рис. 12.3. Бродя по округе персонажу нужно знать ограничения

Рис. 12.3. Бродя по округе персонажу нужно знать ограничения. Задав небольшую область на карте (как показано здесь) вы ограничиваете перемещения персонажа


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

// Координаты персонажа
float CharXPos, CharYPos, CharZPos;

// Дальность перемещения - пропущено перемещение YMove
float XMove, ZMove;

// Расстояние, на которое перемещается персонаж
float Distance;

// Определяем случайное направление перемещения,
// продолжаем цикл пока не найдем
while(1) {
    float Direction = 6.28f / 360.0f * (float)(rand() % 360);
    XMove = cos(Direction) * Distance;
    ZMove = sin(Direction) * Distance;

    // Проверяем допустимо ли перемещение, игнорируя высоту
    if(CharXPos+XMove >= WanderMinX &&
       CharXPos+XMove <= WanderMaxX &&
       CharZPos+ZMove >= WanderMinZ &&
       CharZPos+ZMove <= WanderMaxZ) {

        // Перемещение разрешено, обновляем координаты
        CharXPos += XMove;
        CharZPos += ZMove;
        break; // Выходим из цикла
    }
}

 

ПРИМЕЧАНИЕ
Не стоит случайным образом перемещать персонаж в каждом кадре, или ваши персонажи будут выглядеть, будто у них истерика. Вместо этого обновляйте направление движения персонажа только раз в несколько секунд или около этого.

Ходьба по маршруту

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

Использование маршрутных точек

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

typedef struct sRoutePoint {
    float XPos, ZPos;
} sRoutePoint;

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

sRoutePoint Route[5] = {
    { -200.0f, -100.0f },
    { 100.0f, -300.0f },
    { 300.0f, -200.0f },
    { 200.0f, 100.0f },
    { 0.0f, 400.0f }
};
long NumRoutePoints = 5; // Чтобы проще было узнать количество точек

Рис. 12.4. На воображаемой карте показано пять маршрутных точек

Рис. 12.4. На воображаемой карте показано пять маршрутных точек. Персонаж начинает с точки 1 и идет прямо к точке 2, а затем к точек 3 и так до тех пор, пока не достигнет точки 5. После этого персонаж возвращается к точке 1 и начинает обход маршрута снова


Ходьба от точки к точке

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

Начнем с предположения, что координаты персонажа хранятся в следующих переменных (вместе со скоростью перемещения персонажа):

float CharXPos, CharZPos; // Координата Y не нужна
float WalkSpeed; // Скорость ходьбы за кадр

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

float RouteXPos, RouteZPos; // Снова без координаты Y

Теперь начинаем перемещение персонажа, вычисляя переменные передвижения:

// Вычисляем расстояние от персонажа до маршрутной точки
float XDiff = (float)fabs(RouteXPos - CharXPos);
float ZDiff = (float)fabs(RouteZPos - CharZPos);
float Length = sqrt(XDiff*XDiff + ZDiff*ZDiff);

// Вычисление перемещения к пункту назначения
float MoveX = (RouteXPos - CharXPos) / Length * WalkSpeed;
float MoveZ = (RouteZPos - CharZPos) / Length * WalkSpeed;

Теперь, всякий раз, когда вы обновляете местоположение персонажа в кадре, вам необходимо прибавить MoveX и MoveZ к координатам персонажа:

CharXPos += MoveX;
CharZPos += MoveZ;

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

Быстрее Пифагора

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

float Distance = sqrt(Length1*Length1 + Length2*Length2);
float Distance = Length1*Length1 + Length2*Length2;

 

ПРИМЕЧАНИЕ
Теорема Пифагора, возможно, самая известная теорема в геометрии. Она утверждает, что квадрат длины гипотенузы в прямоугольном треугольнике равен сумме квадратов длин катетов. Фактически, это означает, что квадратный корень длин двух сторон (они должны быть возведены в квадрат и сложены) равен длине третьей стороны прямоугольного треугольника. Правда, просто?

Обратите внимание, что показанные две строки кода практически идентичны, за исключением того, что во второй строке опущена функция sqrt, из-за чего она выполняется гораздо быстрее. Недостаток в том, что вы не получаете точную длину, но это не представляет проблемы.

Например, вы измеряете расстояние между двумя точками, и хотите видеть, меньше ли оно 40. Если координаты этих двух точек 0, 0 и 30, 20, более быстрое вычисление даст вам расстояние 1 300 (поскольку длины двух сторон 30 и 20, соответственно).

Как теперь определить расстояние? Вычислив квадрат (произведение на само себя) расстояния, вот как! Умножив 40 на 40 вы получаете 1 600. Сравнив вычисленное расстояние между точками, 1 300, вы видите, что оно меньше, чем 1 600 и, следовательно, меньше, чем оригинальное расстояние 40, которое вы проверяете.

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

BOOL TouchedRoutePoint(float CharXPos, // Координаты персонажа
                       float CharZPos,
                       float RouteXPos, // Координаты маршрутной точки
                       float RouteZPos, 
                       float Distance) // Проверяемое расстояние
{
    // Вычисляем квадрат расстояния для проведения быстрой проверки
    Distance *= Distance;

    // Вычисляем рассторяние
    float XDiff = (float)fabs(RouteXPos - CharXPos);
    float ZDiff = (float)fabs(RouteZPos - CharZPos);
    float Dist = XDiff*XDiff + ZDiff*ZDiff;

    // Возвращаем результат
    if(Dist <= Distance) // Внутри проверяемого диапазона
        return TRUE;

    return FALSE; // Вне диапазона расстояний
}

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

Прохождение маршрута

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

sRoutePoint Route[5] = {
    { -200.0f, -100.0f },
    {  100.0f, -300.0f },
    {  300.0f, -200.0f },
    {  200.0f,  100.0f },
    {    0.0f,  400.0f }
};
long NumRoutePoints = 5;

// Координаты персонажа и переменные перемещения
float CharXPos = Route[0].XPos;
float CharZPos = Route[0].ZPos;
float MoveX, MoveZ;
float Speed; // Скорость ходьбы персонажа

// Начинаем отслеживание до второй точки
long TargetRoutePoint = 1;
SetupMovement(TargetRoutePoint);
// Бесконечный цикл перемещения
// и проверки достижения маршрутной точки
while(1) {
    // Персонаж находится в пределах маршрутной точки?
    if(TouchedRoutePoint(TargetRoutePoint, 32.0f) == TRUE) {

        // Переходим к следующей маршрутной точке
        TargetRoutePoint++;
        if(TargetRoutePoint >= NumRoutePoints)
            TargetRoutePoint = 0;

        SetupMovement(TargetRoutePoint);
    }
    // перемещаем персонаж
    CharXPos += MoveX;
    CharZPos += MoveZ;
}

// Функция проверки нахождения в пределах маршрутной точки
BOOL TouchedRoutePoint(long PointNum, float Distance)
{
    Distance *= Distance;
    float XDiff = (float)fabs(CharXPos - Route[PointNum].XPos);
    float ZDiff = (float)fabs(CharZPos - Route[PointNum].ZPos);
    float Dist = XDiff*XDiff + ZDiff*ZDiff;
    if(Dist <= Distance)
        return TRUE;
    return FALSE;
}

// Функция вычисления переменных перемещения
void SetupMovement(long PointNum)
{
    float XDiff = (float)fabs(CharXPos - Route[PointNum].XPos);
    float ZDiff = (float)fabs(CharZPos - Route[PointNum].ZPos);
    float Length = sqrt(XDiff*XDiff + ZDiff*ZDiff);
    MoveX = (Route[PointNum].XPos - CharXPos) / Length * Speed;
    MoveZ = (Route[PointNum].ZPos - CharZPos) / Length * Speed;
}

Следование за другим персонажем

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

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


Рис. 12.5. Один персонаж выбирает другого, чтобы тот следовал за ним

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


Зная координаты каждого персонажа (персонажа, за которым будут следовать, и персонажа, который будет следовать) вы можете сконструировать единую функцию, которая будет определять, в каком направлении должен перемещаться «следующий» персонаж:

void CalculateFollowMovement(
           float CharXPos,   // Координаты персонажа
           float CharZPos,
           float WalkSpeed,  // Скорость ходьбы персонажа
           float FollowXPos, // Координаты преследуемого
           float FollowZPos, // персонажа
           float FollowDistance, // Дистанция следования
           float *MoveX,     // Переменные для перемещения
           float *MoveZ)
{
    // Для быстрой проверки расстояния
    FollowDistance *= FollowDistance;

    // Получаем расстояние между персонажами
    float XDiff = (float)fabs(FollowXPos - CharXPos);
    float ZDiff = (float)fabs(FollowZPos - CharZPos);
    float Length = XDiff*XDiff + ZDiff*ZDiff;

    // Если расстояние между персонажами меньше допустимого,
    // просто стоим
    if(Length < FollowDistance) {
        *MoveX = *MoveZ = 0.0f;
        return;
    }

    // Вычисляем шаг перемещения
    // на основе скорости ходьбы персонажа
    Length = sqrt(Length);
    *MoveX = (CharXPos - FollowXPos) / Length * WalkSpeed;
    *MoveZ = (CharZPos - FollowZPos) / Length * WalkSpeed;
}

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

Избегание персонажа

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

void CalculateEvadeMovement(
           float CharXPos,   // Координаты персонажа
           float CharZPos,
           float WalkSpeed,  // Скорость ходьбы персонажа
           float FollowXPos, // Координаты избегаемого персонажа
           float FollowZPos,
           float EvadeDistance, // Дистанция избегания
           float *MoveX,     // Переменные для передвижения
           float *MoveZ)
{
    // Для быстрой проверки расстояния
    FollowDistance *= FollowDistance;

    // Получаем расстояние между персонажами
    float XDiff = (float)fabs(FollowXPos - CharXPos);
    float ZDiff = (float)fabs(FollowZPos - CharZPos);
    float Length = XDiff*XDiff + ZDiff*ZDiff;

    // Если расстояние между персонажами больше заданного,
    // остаемся стоять 
    if(Length > EvadeDistance) {
        *MoveX = *MoveZ = 0.0f;
        return;
    }

    // Вычисляем шаг перемещения
    // на основе скорости ходьбы персонажа
    Length = sqrt(Length);
    *MoveX = -((CharXPos - FollowXPos) / Length * WalkSpeed);
    *MoveZ = -((CharZPos - FollowZPos) / Length * WalkSpeed);
}

Автоматическое управление персонажами

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

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

  1. Смените тип персонажа с PC на NPC.

  2. Смените параметры искусственного интеллекта PC (который теперь NPC) на следование по маршруту (или другой тип перемещения).

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

  4. Переключите тип персонажа обратно на PC.


netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz