netlib.narod.ru | < Назад | Оглавление | Далее > |
Я отчаянно роюсь в своем мешке. Я клал вчера в него целебный эликсир; куда он подевался? Нашел время терять — в середине битвы, получая удары со всех сторон, и вот, уличив момент, я не могу поправить здоровье!
Посмотрим, вот мой кинжал, дополнительная защита, горстка золотых монет, что-то непонятное и — ох, вот он — мой целебный эликсир! И как я смог собрать все это барахло? Ладно, разберусь позже; сейчас я должен глотнуть эликсира и вернуться к убийству монстров.
К счастью, это испытание не угрожало моей жизни; достаточно было на минуту приостановить игру, отсортировать список моего имущества и найти необходимый эликсир. Восстановив здоровье и возобновив игру, я продолжил сражение как истинный воин!
В ходе игры вы собираете различные предметы (называемые также объекты), у каждого из которых есть уникальное назначение. Создавая игру вы должны перечислить все объекты, указав для чего каждый из них используется. Оружие, броня, лекарства — все должно быть описано. Для этого нужна форма и функция.
Форма и функция — два главных слова в определении объектов. Форма ссылается на представление и идентификацию — как выглядит объект, на что он похож, насколько большой, сколько весит и т.д. Функция описывает назначение; у каждого объекта есть назначение — монеты для покупки, меч для сражений, эликсиры для лечения.
В этом разделе вы узнаете, как описать форму и функцию объектов в формате, удобном для использования в вашем игровом проекте.
Хотя для нас форма является основополагающей для идентификации объекта, для компьютера она ничего не значит, и достаточно, чтобы объект был представлен в виде изображения или трехмерной модели. В главе 2, «Рисование с DirectX Graphics», вы узнали как просто загрузить растровое изображение или X-файл, содержащий трехмерную сетку, так почему бы не использовать эти растры или сетки для описания формы объекта?
Предположим, вы хотите создать оружие, или, более конкретно, меч. В трехмерной игре (например, той, которую я описывал в начале главы), вы захотите, чтобы игрок видел, что его персонаж несет меч и мог исследовать этот меч, взглянув на него поближе. Кроме того, чтобы показать экипировку (что несет персонаж), вам надо отображать на экране растровое изображение меча. Для представления меча вам потребуется одна сетка и одно растровое изображение (рис. 11.1).
Рис. 11.1. Показанную слева сетку вы можете использовать в игровом экране (с держащим ее персонажем) и в списке имущества (для исследования меча). Растровое изображение справа отображается во время игры, чтобы показать, что игрок держит меч
Знаю, что вы подумали, — какой большой и тяжелый меч на рис. 11.1! В настоящем мире меч «большой и тяжелый». Впрочем, вы же не хотите, чтобы никто не смог поднять его. Меч может весить около трех килограммов и быть чуть больше метра длинной. Если в вашей игре учитываются физические свойства объектов, каждому предмету в игре должны быть назначены размеры и вес. Я предпочитаю измерять размер в кубических дециметрах.
Теперь можно начать встраивать информацию о предметах в игру. Начнем с создания структуры, которая будет хранить информацию о мече (да и о любом другом предмете):
typedef struct sItem { char Name[32]; // Краткое название предмета char Description[128]; // Описание предмета float Weight; // Вес (в кг.) float Size; // Размер (в куб. дециметрах) char MeshFilename[16]; // Имя X-файла с сеткой char ImageFilename[16]; // Имя файла .BMP } sItem;
Описание объекта Sword в игре будет выглядеть так:
sItem Sword = { "Меч", "Большой тяжелый меч.", 2.25f, 12.68f, "Sword.x", "Sword.bmp" };
Теперь меч готов к использованию! Ладно, не по-настоящему, поскольку сейчас мы задали только физические свойства меча (вместе с именами файлов для сетки и для изображения). Движку игры недостаточно этой информации чтобы использовать предмет. Здесь на сцену выходит функция предмета.
Если какой-нибудь предмет попадет в руки ребенка (да и любого из нас), ребенок найдет ему применение, хотя, возможно, оно и не будет правильным. Может показаться заманчивым сделать объектами в игре все типы предметов, но это не соответствует вашей цели. Предметы (объекты) в вашей игре будут использоваться по своему назначению, которое может быть отнесено к одной из следующих категорий (помните, что это не полный список, а лишь отправная точка для ваших идей):
Аксессуары. Кольца, ожерелья, пояса и любые другие виды носимого снаряжения, которые могут помочь персонажу, классифицируются как аксессуары. Иногда аксессуар — это магическая часть снаряжения, увеличивающая способности того, кто его носит.
Броня. Вы не будете терять здоровье от ударов, если носите соответствующую защиту. Броня может быть самой разной — от полных доспехов до пары ботинок.
Коллекции. Любые предметы, у которых нет специального назначения, считаются коллекционными. Они могут играть важную роль в сценарии игры, но могут быть и абсолютно бесполезными. Картины, декоративные статуэтки, кресла — все это коллекционные предметы.
Еда. Запеченные моллюски, пироги с моллюсками, моллюски по-французски, моллюски в сливочном соусе и даже моллюски барбекю — все, что пожелаете. Персонажи вашей игры должны периодически испытывать голод, и все, что они могут найти и съесть, классифицируется как еда, в том числе микстуры и травы.
Деньги. Все, что относится к сфере финансов, независимо от того, какую форму оно принимает — монеты, банкноты, раковины и т.д. Играм не требуется деноминация — одна золотая монета так же хороша, как и другая, и чем больше у вас монет, тем лучше.
Транспорт. Автобусы, лодки, самолеты, дельтапланы: люди знают как перемещаться по миру и предметы для этого принадлежат персонажам игры. Лодку нельзя носить с собой, но ваша игра распознает персонажа, являющегося владельцем предмета, делая соответствующую запись в его инвентаре.
Оружие. Меч, камень или удавка — любая вещь, которую можно использовать для нанесения повреждений.
Прочее. Все, что не попадает в перечисленные выше категории, классифицируется как «прочее».
Ваш игровой движок определяет, что делает каждый из предметов, на основании категории предмета. Например, оружие можно добавить к экипировке (вооружиться им), еду можно употребить в пищу. Чтобы сделать работу с предметами легче, можно при необходимости добавить подкатегории. Например, лечебный эликсир, хотя и съедобный, может быть отнесен к категории лекарственных предметов. Когда ваш игровой движок видит лекарственный предмет, он сразу знает, что надо увеличить уровень здоровья персонажа.
У каждого предмета есть дополнительная информация для использования, так что мы не можем остановиться здесь. У оружия есть атрибут силы, увеличивающий способность персонажа наносить повреждения, лечебный эликсир восстанавливает здоровье, а броня уменьшает повреждения, поучаемые персонажем в сражении. Теперь давайте взглянем, что из себя представляет каждая категория предметов.
Персонажи могут наносить повреждения голыми руками. Чем сильнее персонаж, тем больше повреждений он может нанести. Дайте персонажу в руки оружие и количество наносимых им повреждений резко возрастет. Одни виды оружия более смертоносны, чем другие, так что поиски лучшего оружия это одна из весомых причин для того, чтобы участвовать в приключениях.
У оружия может быть несколько применений. Например, зачарованный меч может ударить скрывающегося демона и, в то же самое время, создать мощную шаровую молнию. Такие дополнительные функции предметов я называю специализациями (specials).
Персонаж не может использовать любое оружие; здесь есть ряд ограничений. Размеры, вес, мощность и цена — вот некоторые из ограничений, которые надо учитывать. Какое удовольствие, если в руках новичка окажется самое мощное оружие в игре? Поэтому вы добавляете ограничения использования (больше об этом вы узнаете в разделе «Ограничения использования» далее в этой главе).
Вернемся назад к тому факту, что одни виды оружия могут наносить больше повреждений, чем другие. Количество наносимых оружием повреждений определяется числом, называемым модификатор атаки (attack modifier). Чем больше число, тем больший ущерб наносит оружие. Кроме того, некоторые виды оружия проще в обращении, что позволяет вам чаще поражать цели. Чтобы определить, насколько легко поразить цель данным оружием, вы используете модификатор поражений (to-hit modifier). Чем больше модификатор поражений оружия, чем больше шансов, что персонаж, используя данное оружие, поразит цель. Об использовании модификаторов и о том, как эти модификаторы относятся к персонажам, вы подробнее узнаете в главе 12, «Управление игроками и персонажами».
Некоторые типы оружия могут быть объединены в специальные группы, называемые группы вооружений (weapons groups). Отдельные виды оружия могут наносить конкретным созданиям больший вред, чем другие — для примера подумайте об использовании огненного меча против ледяного монстра.
И, наконец, вооружение может быть разделено на такие группы, как оружие ближнего боя (hand-to-hand) и оружие дальнего боя (ranged). Независимо от типа, у каждого оружия есть радиус действия (range of use). Меч может поразить цель, находящуюся прямо перед вами, в то время, как стрела лука может попасть в цель, расположенную в десятках метров от вас. Кроме того, некоторые виды оружия могут поражать сразу несколько целей. Все эти вещи следует учесть при разработке оружия.
Чем больше защиты, тем лучше, и в игре помогает каждый кусочек брони. Броня помогает повысить сопротивляемость к повреждениям. Количество сопротивляемости называется модификатором защиты (defense modifier). Также как и у вооружения, у брони есть специальные возможности, ограничения использования и возможность объединения в группы защиты (armor groups).
Броня может быть разделена на множество подкатегорий, таких как шлемы, нагрудники, кольчуги, поножи, ботинки, перчатки и т.д.
Как упоминалось ранее, у аксессуаров обычно есть специфические области применения. Магическое кольцо можно надеть для того, чтобы стать невидимым. Вы можете стать невидимым сразу, как только наденете кольцо, или, возможно, невидимость будет требовать активации. Аксессуары могут также действовать как броня; они могут увеличивать сопротивляемость некоторым воздействиям в игре — например, повышать сопротивляемость к ядам.
Съедобные предметы обычно бывают нескольких видов. Пища поддерживает жизнь, лекарства увеличивают здоровье, яды уменьшают здоровье. Снова могут оказывать эффект специальные возможности, но, поскольку у съедобных предметов всего несколько применений, вы можете жестко запрограммировать их в движке игры.
Коллекционные предметы обычно просты; они нужны только для какого-нибудь маленького фрагмента игры. Например, если персонаж дает вам свой портрет, чтобы вы отнесли его девушке из соседнего города (и только для этой цели), портрет считается коллекционным предметом. Коллекционные предметы просто способствуют развитию сюжета игры. Возможно, отправивший портрет персонаж получит в свою очередь какой-нибудь специальный предмет от девушки из соседнего города.
Перемещаться пешком медленно и скучно, поэтому могут потребоваться другие виды транспорта. Назначение транспорта — изменять способ перемещения персонажа (обычно по карте). Транспортные средства могут также открывать новые области в игре, которые были прежде недоступны. Например, приобретенная вашим персонажем лодка может использоваться чтобы переплыть через озеро к уединенному острову, или, возможно, лошадь, которую вы видели в соседнем городе, поможет вам благополучно пересечь бесплодную пустыню.
«Прочие» предметы большей частью бесполезны, поскольку не имеют какого-либо применения. Однако не стоит выбрасывать их. По меньшей мере, они могут выполнять определенные действия, заданные в движке. Например, если ваш персонаж хорошо проявит себя в битве, его могут наградить медалью. Хотя эта медаль и замечательно выглядит, она не служит никакой цели в игре.
В предыдущем разделе, «Определение функций объекта», вы увидели как много надо описать в функции объекта. К счастью, поскольку объект должен быть отнесен к одной из категорий, требуется не вся информация — меч наносит повреждения, а броня защищает — так что не требуется смешивать данные атаки и защиты.
Фактически, вам потребуется однозначно категоризировать каждый предмет в соответствии с вашим игровым движком, как это сделал я в разделе «Определение функций объекта». Каждая категория объектов нумеруется для ссылок (1 — оружие, 2 — броня и т.д.). У каждой категории есть связанные с ней значения, они могут определять модификатор (атака или защита), специальное использование, количество наносимых повреждений или добавляемого здоровья и присоединенный скрипт. Верно. Предметы могут использовать скрипты для увеличения своих возможностей.
За исключением присоединенного скрипта, все остальные значения могут быть представлены одной переменной — единой для значения модификатора, добавляемого здоровья и всего остального. Итак, добавим к ранее созданной структуре sItem следующие две переменные:
// ... Предыдущая информация sItem long Category; // 1-5 представляет показанные выше // категории предметов long Value; // Модификатор, увеличение здоровья и т.д. // ... Дальнейшая информация sItem
enum ItemCategories { WEAPON = 0, ARMOR, SHIELD, HEALING, OTHER };
У всего в игре есть своя цена. Назначение цены каждому предмету помогает определить, что игрок может купить или продать и по какой цене. Не стоит загромождать каждый предмет множеством цен; просто укажите единственное значение, которое персонаж должен заплатить, чтобы купить данный предмет. При продаже того же самого предмета персонажем ему будет предложена цена несколько меньшая той, которую он должен был бы заплатить, если бы покупал этот предмет. Например, предметы могут продаваться за половину той цены, которую персонаж заплатил при их покупке.
Цена предмета может быть добавлена в структуру sItem следующим образом:
// ... Предыдущая информация sItem long Price; // Цена покупки предмета
Иногда, вы можете захотеть, чтобы у игрока не было возможности продавать некоторые предметы — например, важные магические артефакты. Об этом позаботится битовый флаг и сейчас мы сразу добавим еще несколько флагов. В таблице 11.1 перечислены некоторые флаги, которые вы можете использовать.
Таблица 11.1. Битовые флаги предметов
Флаг | Описание |
SELLABLE | Предмет может быть продан |
CANDROP | Предмет может быть выброшен. Не используйте этот флаг для важных предметов, если не хотите, чтобы персонаж игрока мог их выбросить |
USEONCE | Предмет может быть использован только один раз. После использования предмет пропадает |
UNKNOWN | Неизвестный предмет. Чтобы правильно использовать его вы должны выполнить опознание |
Все флаги хранятся в переменной, расположенной в структуре sItem:
long Flags; // Битовые флаги предмета // ... Дальнейшая информация sItem
Пример вы можете посмотреть в замечательной игре Phantasy Star Online от Sega. Каждый флаг может быть представлен элементом перечисления (тогда получится максимум 32 флага). Чтобы установить, очистить или проверить флаг, используйте следующие макросы (в макросах v представляет переменную, хранящую флаги предмета, а f — интересующий вас флаг):
enum { SELLABLE = 0, // Бит 0 CANDROP, // Бит 1 USEONCE, // Бит 2 UNKNOWN // Бит 3 }; #define SetItemFlag(v,f) (v |= (1 << f)) #define ClearItemFlag(v,f) (v &= ~(1 << f)) #define CheckItemFlag(v,f) (v & (1 << f)) // Пример использования макросов и флагов long ItemFlags = 0; // Устанавливаем флаги, указывающие, что предмет // может быть продан и брошен SetItemFlag(ItemFlags, SELLABLE); SetItemFlag(ItemFlags, CANDROP); // Проверяем, может ли предмет быть брошен // и отображаем сообщение if(CheckItemFlag(ItemFlags, CANDROP)) MessageBox(NULL, "Can Drop Item", "Item", MB_OK); // Очищаем флаг продажи ClearItemFlag(ItemFlags, SELLABLE);
Вы можете захотеть, чтобы некоторые персонажи вашей игры не могли использовать отдельные предметы. Например, маг не может пользоваться тяжелым двуручным боевым топором, а для варвара бесполезен магический посох. В таких случаях, когда только конкретный персонаж может пользоваться конкретным предметом, вам необходимы ограничения использования для заданных классов персонажей.
Чтобы представить ограничения использования элемента, в структуру sItem включается еще одна переменная, отслеживающая 32 бита информации. Каждый бит представляет собой отдельный класс, значит всего вы можете отслеживать 32 класса. Если предмет может использоваться каким-либо классом, соответствующий бит установлен, если для персонажей данного класса предмет бесполезен — представляющий класс бит сброшен.
Вот дополнение к структуре sItem для поддержки ограничений использования:
long Usage; // Ограничения использования // ... Другие данные sItem
Чтобы было проще устанавливать, очищать и проверять биты классов в ограничениях использования, можно применять следующие макросы (v представляет переменную с флагами, а c — номер класса в диапазоне от 0 до 31):
#define SetUsageBit(v,c) (v |= (1 << c)) #define ClearUsageBit(v,c) (v &= ((~(1 << c)) #define CheckUsageBit(v,c) (v & (1 << c)) // Пример использования макросов long Flags = 0; SetUsageBit(Flags, 5); // Устанавливаем бит класса 5 if(CheckUsageBit(Flags, 5)) // Проверяем бит класса 5 MessageBox(NULL, "Usage Set", "Bit", MB_OK); ClearUsageBit(Flags, 5); // Очищаем бит класса 5
Используя показанные макросы (SetUsageBit, ClearUsageBit и CheckUsageBit) вы можете быстро проверить, может ли персонаж использовать предмет экипировки, основываясь на классе этого персонажа. Предположим, в игре маги относятся к классу 1, а воины — к классу 2. Когда маг попробует вооружиться палашом (у которого бит для класса 1 сброшен), игровой движок сообщит игроку, что маг не может использовать этот предмет.
Чтобы сделать предметы более универсальными, к ним можно прикреплять скрипты. Скрипт срабатывает при каждом использовании предмета — когда персонаж выпивает эликсир, когда в битве применяется меч или когда пользователь активирует специальную функцию предмета (например, использует волшебную палочку).
Сейчас вам надо только сохранить в структуре sItem имя файла скрипта.
// .. Предыдущие данные sItem char ScriptFilename[16]; // Имя файла скрипта .mls
Скорее всего, вы захотите, чтобы игрок мог видеть, как выглядит предмет, а значит вам потребуется загрузить представляющее объект двухмерное изображение или трехмерную сетку. Для этого внесите следующие дополнения в структуру sItem:
// .. Предыдущие данные sItem char MeshFilename[16]; // Имя файла сетки .X char ImageFilename[16]; // Имя файла изображения .bmp } sItem; // Завершение структуры
Теперь структура sItem готова к использованию! Взгляните на нее целиком (включая вспомогательные макросы):
enum ItemCategories { WEAPON = 0, ARMOR, SHIELD, HEALING, OTHER }; #define SetUsageBit(v,c) (v |= (1 << c)) #define ClearUsageBit(v,c) (v &= ((~(1 << c)) #define CheckUsageBit(v,c) (v & (1 << c)) enum { SELLABLE = 0, // Бит 0 CANDROP, // Бит 1 USEONCE, // Бит 2 UNKNOWN // Бит 3 }; #define SetItemFlag(v,f) (v |= (1 << f)) #define ClearItemFlag(v,f) (v &= ~(1 << f)) #define CheckItemFlag(v,f) (v & (1 << f)) typedef struct sItem { char Name[32]; // Краткое название предмета char Description[128]; // Описание предмета float Weight; // Вес (в кг.) float Size; // Размер (в куб. дм.) long Category; // Категория предмета long Value; // Модификатор, увеличение здоровья и т.д. long Price; // Цена покупки предмета long Flags; // Битовые флаги предмета long Usage; // Ограничения использования char ScriptFilename[16]; // Имя файла скрипта .mls char MeshFilename[16]; // Имя файла сетки .X char ImageFilename[16]; // Имя файла изображения .bmp } sItem;
Получив законченную структуру sItem можно вернуться к построению структуры данных меча. Предположим, что у меча модификатор повреждений равен +10 (это означает, что в битве к коэффициенту повреждений будет добавляться 10). Меч в игре обычно стоит 200 монет и может использоваться только воинами (класс 2).
// Определение классов персонажей #define WIZARD 1 #define WARRIOR 2 sItem Sword = { "Меч", "Большой тяжелый меч", // Название и описание 2.25f, 12.68f, // Вес и размер WEAPON, 200, SELLABLE | CANDROP, // Категория, цена и флаги (1 << WARRIOR), // Используется классом 2 (воинами) "", "Sword.x", "Sword.bmp" // Файлы скрипта, сетки и изображения };
Теперь меч описан и вы можете использовать его в игре. Но что за польза от единственного предмета? Мир вашей игры должен быть заполнен объектами! Как же обращаться со всеми ними?
netlib.narod.ru | < Назад | Оглавление | Далее > |