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

Работа с магией и заклинаниями

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

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

Графика заклинания

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

ПРИМЕЧАНИЕ
Каждая сетка отображается отдельно. Две сетки заклинания никогда не отображаются одновременно. Когда одна сетка завершает цикл, она освобождается, и новая сетка занимает ее место.

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

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

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

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


Рис. 12.8. Заклинатель выпустил шаровую молнию

Рис. 12.8. Заклинатель выпустил шаровую молнию в намеченную цель. Сетка перемещается с установленной скоростью, пока не достигнет места назначения


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

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


Рис. 12.9. Заклинание земляного вала

Рис. 12.9. Заклинание земляного вала из демонстрационной программы Chars демонстрирует технику растягивающегося/масштабируемого позиционирования сетки. Независимо от расстояния между заклинателем и целью, сетка масштабируется так, чтобы всегда начинаться возле заклинателя и касаться цели


Демонстрационную программу Chars вы найдете на прилагаемом CD-ROM в каталоге \BookCode\Chap12\Chars. Хотя и незаметное на первый взгляд, заклинание земляного вала, описанное на рис. 12.9, показывает небольшую коричневую сетку, растянутую от заклинателя к целевому персонажу.

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

Функция заклинания

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

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


Таблица 12.5. Возможные эффекты заклинаний



Эффект Описание

Изменение здоровья Увеличивает или уменьшает на заданное количество очки здоровья цели
Изменение маны Увеличивает или уменьшает на заданное количество очки маны цели
Снятие статуса Снимает с цели конкретный дополнительный статус (такой, как отравление или замедление)
Установка статуса Устанавливает дополнительный статус цели (отравление и т.д.)
Воскрешение мертвых Возвращает PC обратно к жизни
Мгновенная смерть Немедленно убивает целевой персонаж
Расколдовывание Снимает все дополнительные статусы целевого персонажа
Телепортация Телепортирует PC в заданное местоположение на карте


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

enum SpellEffects {
    ALTER_HEALTH = 0,
    ALTER_MANA,
    CURE_AILMENT,
    CAUSE_AILMENT,
    RAISE_DEAD,
    INSTANT_KILL,
    DISPEL_MAGIC,
    TELEPORT
};

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

switch(SpellEffect) {
    case ALTER_HEALTH:
        // Обработка изменения здоровья

    case ALTER_MANA:
        // Обработка изменения маны
    ...
}

Каждый эффект заклинания достаточно прямолинеен. Теперь давайте пристальнее взглянем, что каждый из этих эффектов делает.

Изменение здоровья и маны

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

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

Установка и снятие статусов

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

Когда вы меняете (устанавливаете или снимаете) дополнительный статус, можно использовать для определения статусов битовое представление. Используя битовые флаги можно сразу установить или снять сразу несколько статусов. У каждого статуса есть связанное макроопределение, выглядящее так (все они находятся в файле Chars.h):

#define AILMENT_POISON          1
#define AILMENT_SLEEP           2
#define AILMENT_PARALYZE        4
#define AILMENT_WEAK            8
#define AILMENT_STRONG         16
#define AILMENT_ENCHANTED      32
#define AILMENT_BARRIER        64
#define AILMENT_DUMBFOUNDED   128
#define AILMENT_CLUMSY        256
#define AILMENT_SUREFOOTED    512
#define AILMENT_SLOW         1024
#define AILMENT_FAST         2048
#define AILMENT_BLIND        4096
#define AILMENT_HAWKEYE      8192
#define AILMENT_SILENCED    16384

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

Воскрешение и мгновенная смерть

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

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

Расколдовывание

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

Телепортация

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

Нацеливание заклинаний, стоимость и шансы

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

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

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

Простое произнесение заклинания не означает, что оно сработает; есть шансы на ошибку. Шанс, что заклинание сработает или провалится называется шансом эффекта заклинания, и находится этот шанс в диапазоне от 0 процентов (никогда не работает) до 100 процентов (работает всегда).

Главный список заклинаний

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

typedef struct sSpell
{
    char Name[32];          // Название
    char Description[128];  // Описание

    long DmgClass;          // Класс, которому заклинание
                            // наносит двойной ущерб
    long CureClass;         // Класс, который заклинание лечит

    long Cost;              // Цена заклинания в MP

    float Distance;         // Максимальное расстояние до цели

    long Effect;            // Эффект заклинания
    long Chance;            // % получения эффекта
    float Value;            // Различные значения

    long Target;            // Цель заклинания
    float Range;            // Дистанция (в игровых единицах)

    long MeshNum;           // Номер используемой сетки
    long MeshPos;           // Позиционирование сетки
    float MeshSpeed;        // Скорость перемещения сетки
    long MeshSound;         // Звуковой эффект для воспроизведения
    BOOL MeshLoop;          // Циклическая анимация
} sSpell;

 

ПРИМЕЧАНИЕ
Структура sSpell определена во включаемом файле msl.h, расположенном в каталоге проекта Char (посмотрите в каталоге \BookCode\Chap12\Chars на прилагаемом к книге CD-ROM). Дополнительную информацию о проекте Chars вы найдете в конце главы.

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

Ранее в этой главе я упоминал классы персонажей. Они оказывают эффект на заклинания. Отдельные заклинания могут наносить двойной ущерб слабо защищенным против них персонажам, и в этом причина появления переменной sSpell::DmgClass. Если класс персонажа и переменная DmgClass совпадают, заклинание наносит двойной ущерб.

С другой стороны, если класс персонажа совпадает с классом заклинания, заклинание может лечить персонаж. Представьте себе заклинание замораживания для ледяного дракона. Вместо того, чтобы повредить дракону, оно исцелит его на половину величины наносимого заклинанием ущерба. Таким образом, назначение sSpell::CureClass становится очевидным; если класс персонажа и CureClass совпадают, заклинание лечит, а не повреждает.

Перемещаясь дальше вы увидите стоимость произнесения заклинания (sSpell::Cost), измеряемую в очках маны. Чтобы сотворить заклинание, персонаж должен иметь в резерве как минимум указанное количество маны (Cost). После произнесения заклинания значение переменной Cost вычитается из маны персонажа.

Вспомните, что у заклинания есть назначенные диапазон и дистанция; диапазон (sSpell::Range) это расстояние от заклинателя, на котором заклинание может поразить цель, а дистанция (sSpell::Distance) — это размер области вокруг точки попадания, в которой имеет место эффект заклинания.

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

enum SpellTargets {
    TARGET_SINGLE = 0,
    TARGET_SELF,
    TARGET_AREA
};

Эффект заклинания (sSpell::Effect) имеет связанный с ним шанс на успех, хранящийся в sSpell::Chance. Каждое значение имеет в своем распоряжении трио переменных (sSpell::Value). Первое значение в массиве — это наносимое или излечиваемое количество повреждений, либо используемые битовые значения статусов.

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

Завершающую группу переменных (MeshNum, MeshPos, MeshSpeed, MeshSound и MeshLoop) вы используете для графической составляющей заклинания. Вместо того, чтобы ссылаться на сетки заклинания по имени, более эффективно использовать номера. MeshNum хранит номер сетки, используемый движком управления заклинаниями для рисования графики заклинания.

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

enum AnimPositions {
    POSITION_NONE = 0,
    POSITION_CASTER,
    POSITION_TOTARGET,
    POSITION_TOCASTER,
    POSITION_TARGET,
    POSITION_SCALE
};

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

В вычислениях скорости MeshSpeed определяет расстояние в трехмерных единицах, которое сетка проходит за одну секунду. Для времени переменная MeshSpeed конвертируется в значение long, представляющее количество миллисекунд в течении которых сетка остается на месте.

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

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

Список заклинаний

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

Оглянувшись назад, вы увидите, что я определил для каждого персонажа возможность использовать 64 заклинания, так что MSL должен хранить только 64 структуры данных sSpell, каждая из которых представляет отдельное заклинание, доступное для использования всеми персонажами.

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

Определение заклинаний с MSL Editor

Определение заклинаний вашей игры путем ручного конструирования множества структур sScript быстро наскучит вам. вместо этого вам необходим редактор, который лучше подходит для быстрого изменения каждого аспекта заклинаний вашей игры. Встречайте MSL Editor!

MSL Editor (находящийся на прилагаемом к книге CD-ROM в папке \BookCode\Chap12\MSLEdit), обладает прямолинейным интерфейсом, показанным на рис. 12.10.


Рис. 12.10. Главный экран MSL Editor

Рис. 12.10. Главный экран MSL Editor содержит список загруженных заклинаний, а также кнопки, управляющие добавлением, удалением, сохранением и загрузкой файлов MSL


В MSL Editor предусмотрено место для 64 заклинаний (ограниченное только флагами, используемыми для хранения известных персонажу заклинаний). Запустив редактор вы можете выполнять следующие шаги для создания или редактирования ваших заклинаний:

  1. Дважды щелкните по заклинанию в списке, чтобы открылось диалоговое окно Modify Spell.

  2. В диалоговом окне Modify Spell (рис. 12.11) введите данные заклинания. Щелкните OK, чтобы закрыть диалоговое окно Modify Spell и вернуться к диалоговому окну Master Spell List Editor.


    Рис. 12.11. Диалоговое окно Modify Spell

    Рис. 12.11. Диалоговое окно Modify Spell набито графикой и всей информацией, необходимой вам для описания эффектов заклинаний


  3. Чтобы сохранить ваш список заклинаний, щелкните по кнопке Save.

  4. В диалоговом окне Save MSL File введите имя файла и щелкните OK. Чтобы загрузить файл заклинаний, щелкните по кнопке Load в диалоговом окне Master Spell List Editor, введите имя загружаемого файла заклинаний и щелкните OK.

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

Заклинание шаровой молнии поражает единственную цель огненным шаром. Стоящее 5 MP, это заклинание не имеет назначенных классов, которые будет излечивать или наносить двойной ущерб (на это указывает значение –1). Заклинание изменяет здоровье на –10 (что указано в текстовом поле Value1), и имеет 100-процентную вероятность оказания на цель ожидаемого эффекта.

Целями заклинания могут быть персонажи, находящиеся в пределах 256 единиц от заклинателя, и заклинание поразит только одну цель (первого персонажа, найденного в области, размером 30 единиц).

Используются две сетки; первая сетка с номером 0 перемещается от заклинателя к цели со скоростью 256 единиц в секунду. Эта сетка циклически анимируется, пока не достигнет назначенной цели, и в этот момент ее место занимает вторая сетка. Вторая сетка имеет номер 1 и парит поверх цели 400 миллисекунд. Третья сетка не используется (для этого вы выбираете N/A в соответствующем поле). Сейчас заклинание завершено.

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

Создание контроллера заклинаний

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

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

Класс контроллера заклинаний cSpellController использует вспомогательные структуры, упрощающие отслеживание сеток и анимаций заклинаний. Это структуры sSpellMeshList и sSpellTracker.

ПРИМЕЧАНИЕ
Класс cSpellController, структура sSpellMeshList и структура sSpellTracker находятся в файлах spell.h и spell.cpp на прилагаемом к книге CD-ROM (посмотрите в каталоге \BookCode\Chap12\Chars).

Сетки и sSpellMeshList

Просмотрев структуру данных заклинания sSpell, вы увидите, что мы ссылаемся на сетки по номеру, а не по имени. Этот номер сетки является в действительности индексом в массиве сеток. Вы храните этот массив сеток в коллекции структур sSpellMeshList:

typedef struct sSpellMeshList {
    char       Filename[MAX_PATH]; // Имя файла сетки/анимации
    long       Count;  // Кол-во использующих сетку заклинаний
    cMesh      Mesh;      // Объект сетки
    cAnimation Animation; // Объект анимации

    // Конструктор и деструктор для подготовки и освобождения данных
    sSpellMeshList() { Count = 0; }
    ~sSpellMeshList() { Mesh.Free(); Animation.Free(); }
} sSpellMeshList;

Для каждой используемой в вашем движке сетки у вас есть соответствующая структура sSpellMeshList. Каждый экземпляр структуры хранит имя используемого файла сетки, объекты cMesh и cAnimation для сетки, и переменную (Count), которая отслеживает количество используемых в данный момент экземпляров сетки.

Для каждого заклинания, которому нужна сетка, соответствующий файл .X загружается в объекты сетки и анимации (оба используют одно и то же имя файла, а анимация использует единственный анимационный набор с именем anim).

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

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

Отслеживание заклинаний с использованием sSpellTracker

В то время, как структура sSpellMeshList поддерживает используемые заклинаниями сетки, сам список активных заклинаний поддерживается структурой sSpellTracker. Структура sSpellTracker выделяется и вставляется в связанный список таких же структур каждый раз, когда произносится заклинание:

typedef struct sSpellTracker
{
    long SpellNum; // Номер заклинания

    sCharacter *Caster; // Персонаж, произнесший заклинание
    long       Type;    // Тип персонажа для воздействия

    long CurrentAnimation;           // Анимация: 0-2
    float SourceX, SourceY, SourceZ; // Координаты источника
    float TargetX, TargetY, TargetZ; // Координаты цели

    float XPos, YPos, ZPos; // Текущие координаты
    float XAdd, YAdd, ZAdd; // Значения перемещения
    float Distance;         // Расстояние до цели

    union {
        float Speed; // Скорость перемещения
        long Time;   // Время показа
    };

    cObject Object; // Графический объект

    sSpellTracker *Prev, *Next; // Связанный список

    sSpellTracker() { Caster = NULL; Prev = Next = NULL; }
    ~sSpellTracker() { delete Next; }
} sSpellTracker;

Для каждого произнесенного заклинания структура sSpellTracker используется для хранения информации, отслеживающей сетку, анимацию, передвижение, время и какой персонаж произнес заклинание. Структура начинается с номера заклинания (SpellNum), который ссылается прямо на MSL.

Чтобы позднее помочь определить эффект заклинания, поддерживается также указатель на персонаж (Caster) и тип персонажей, на которые действует заклинание (PC, NPC или MC). Вы можете определить каждый тип персонажей следующим образом:

#define CHAR_PC      0
#define CHAR_NPC     1
#define CHAR_MONSTER 2

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

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

Чтобы отслеживать перемещение сеток заклинаний (если они действительно двигаются), вы используете набор переменных (XAdd, YAdd и ZAdd), которые сообщают контроллеру заклинаний, в каком направлении перемещается сетка при каждом обновлении. Что касается используемого в данный момент местоположения сетки, переменные XPos, YPos и ZPos содержат текущие координаты, в которых визуализируется сетка.

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

Завершает sSpellTracker Object — графический объект, используемый для визуализации сеток, и Prev и Next, используемые для поддержки связанного списка структур.

Класс cSpellController

Поскольку контроллер заклинаний необходим только для отслеживания сеток заклинаний и анимаций, определение класса относительно мало:

class cSpellController
{
  private:
    cGraphics *m_Graphics; // Родительский графический объект
    cFrustum  *m_Frustum;  // Пирамида видимого пространства

    sSpell m_Spells[NUM_SPELL_DEFINITIONS]; // Данные заклинаний
    sSpellTracker *m_SpellParent;  // Список активных заклинаний

    long           m_NumMeshes; // Кол-во используемых сеток
    sSpellMeshList *m_Meshes;   // Список сеток

    char m_TexturePath[MAX_PATH]; // Путь к текстурам сеток

    cCharacterController *m_Chars; // Контроллер персонажей

    // Установка перемещения сетки
    BOOL SetAnimData(sSpellTracker *SpellPtr, long Num);

    // Переопределяемая функция для воспроизведения звуков
    virtual BOOL SpellSound(long Num) { return TRUE; }

Прежде чем исследовать открытые функции, рассмотрим закрытые данные контроллера заклинаний. Контроллер заклинаний использует графический объект и объект пирамиды видимого пространства. Графический объект (m_Graphics) должен быть заранее инициализирован для использования в классе, в то время как объект пирамиды видимого пространства (m_Frustum) может предоставляться из внешнего кода или вычисляться внутри функции визуализации заклинаний.

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

За MSL следует указатель связанного списка m_SpellParent, отслеживающий заклинания, которые были произнесены и теперь отображаются. Далее идут m_NumMeshes (где хранится количество используемых сеток) и m_Meshes (список сеток).

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

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

Класс cSpellController содержит две закрытые функции: SetAnimData и SpellSound. Функция SetAnimData устанавливает используемую сетку, а также параметры перемещения сетки. SpellSound вызывается каждый раз, когда используется сетка заклинания; ваша задача переопределить эту функцию, чтобы воспроизводился соответствующий звук, как указано в списке аргументов функции.

Рассмотрев закрытые данные и функции, вы можете перейти к открытым функциям класса (конструктор, деструктор, Init, Shutdown, Free, GetSpell, Add, Update и Render):

  public:
    cSpellController();  // Конструктор
    ~cSpellController(); // Деструктор

    // Функции для инициализации/выключения класса контроллера
    BOOL Init(cGraphics *Graphics, char *DefinitionFile,
              long NumSpellMeshes, char **MeshNames,
              char *TexturePath,
              cCharacterController *Controller);
    BOOL Shutdown();

    // Освобождение класса
    BOOL Free();

    sSpell *GetSpell(long SpellNum);

    // Добавление заклинания к списку
    BOOL Add(long SpellNum,
             sCharacter *Caster, long TargetType,
             float SourceX, float SourceY, float SourceZ,
             float TargetX, float TargetY, float TargetZ);

    // Обновление всех заклинаний на основании
    // прошедшего времени
    BOOL Update(long Elapsed);

    // Визуализация всех сеток заклинаний внутри
    // пирамиды видимого пространства
    BOOL Render(cFrustum *Frustum=NULL, float ZDistance=0.0f);
};

Поскольку глава становится очень длинной, я просто перечислю каждую функцию и опишу, что она делает. Чтобы следить за изложением вы можете загрузить код класса с прилагаемого к книге CD-ROM (загляните в \BookCode\Chap12\Chars\Spell.cpp).

cSpellController::cSpellController и cSpellController::~sSpellController

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

cSpellController::Init и cSpellController::Shutdown

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

cSpellController::Free

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

cSpellController::GetSpell

Внешнему коду может потребоваться доступ к MSL и GetSpell удовлетворяет эту потребность. По предоставленному номеру заклинания функция возвращает указатель на массив с загруженным MSL.

cSpellController::Add

Теперь начинается настоящая забава! Функцию Add вы будете использовать больше всего, поскольку она инициализирует заклинание. Список аргументов включает номер заклинания в MSL (от 0 до 63), указатель на структуру сотворившего заклинание персонажа, тип целевого персонажа, координаты источника и цели.

cSpellController::SetAnimData

Закрытая функция SetAnimData инициализирует три используемые заклинанием сетки. Если одна из сеток не используется (на это указывает значение POSITION_NONE в MeshPos), выполняется переход к следующей из трех сеток. После того, как использованы все три сетки, запускается эффект заклинания.

cSpellController::Update

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

cSpellController::Render

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

Определение жертвы и обработка эффекта заклинания

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

Использование контроллера заклинаний

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

// Graphics = ранее инициализированный объект cGraphics
// Используем две сетки
char *g_SpellMeshNames[] = {
    { "Fireball.x" },
    { "Explosion.x" }
};

Затем создадим экземпляр контроллера заклинаний и инициализируем его.

cSpellController Controller;

// Инициализация контроллера
Controller.Init(&Graphics, "default.msl",
                sizeof(g_SpellMeshNames)/sizeof(char*),
                g_SpellMeshNames, "..\\", NULL);

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

Controller.Add(0, NULL, CHAR_MONSTER,
               0.0f, 0.0f, 0.0f, 100.0f, 0.0f, 100.0f);

Заклинание теперь будет перемещаться от координат 0, 0, 0 к координатам 100, 0, 100, используя заданные в MSL Editor параметры. Когда закончите работу с контроллером заклинаний, убедитесь, что вызвали функцию контроллера Shutdown.

Controller.Shutdown();

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

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