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

Определение объектов для вашей игры

Я отчаянно роюсь в своем мешке. Я клал вчера в него целебный эликсир; куда он подевался? Нашел время терять — в середине битвы, получая удары со всех сторон, и вот, уличив момент, я не могу поправить здоровье!

Посмотрим, вот мой кинжал, дополнительная защита, горстка золотых монет, что-то непонятное и — ох, вот он — мой целебный эликсир! И как я смог собрать все это барахло? Ладно, разберусь позже; сейчас я должен глотнуть эликсира и вернуться к убийству монстров.

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

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

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

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

Форма объекта

Хотя для нас форма является основополагающей для идентификации объекта, для компьютера она ничего не значит, и достаточно, чтобы объект был представлен в виде изображения или трехмерной модели. В главе 2, «Рисование с DirectX Graphics», вы узнали как просто загрузить растровое изображение или X-файл, содержащий трехмерную сетку, так почему бы не использовать эти растры или сетки для описания формы объекта?

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


Рис. 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;

 

ПРИМЕЧАНИЕ
Обратите внимание на 16-байтное ограничение размера буферов MeshFilename и ImageFilename — это значит, что вы можете использовать имена файлов длиной только 16 символов (15 плюс завершающий символ NULL). Если вы считаете, что вам потребуются более длинные имена, просто увеличьте размер буферов до требуемого.

Описание объекта Sword в игре будет выглядеть так:

sItem Sword = {
    "Меч", "Большой тяжелый меч.",
    2.25f, 12.68f,
    "Sword.x", "Sword.bmp"
};

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

Определение функции объекта

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

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

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

Оружие

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

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

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

Вернемся назад к тому факту, что одни виды оружия могут наносить больше повреждений, чем другие. Количество наносимых оружием повреждений определяется числом, называемым модификатор атаки (attack modifier). Чем больше число, тем больший ущерб наносит оружие. Кроме того, некоторые виды оружия проще в обращении, что позволяет вам чаще поражать цели. Чтобы определить, насколько легко поразить цель данным оружием, вы используете модификатор поражений (to-hit modifier). Чем больше модификатор поражений оружия, чем больше шансов, что персонаж, используя данное оружие, поразит цель. Об использовании модификаторов и о том, как эти модификаторы относятся к персонажам, вы подробнее узнаете в главе 12, «Управление игроками и персонажами».

ПРИМЕЧАНИЕ
Модификатор (modifier) — это нечто, меняющее каким-либо образом атрибуты персонажа. Например, модификатор повреждений (damage modifier) уменьшает или увеличивает количество повреждений.

Некоторые типы оружия могут быть объединены в специальные группы, называемые группы вооружений (weapons groups). Отдельные виды оружия могут наносить конкретным созданиям больший вред, чем другие — для примера подумайте об использовании огненного меча против ледяного монстра.

И, наконец, вооружение может быть разделено на такие группы, как оружие ближнего боя (hand-to-hand) и оружие дальнего боя (ranged). Независимо от типа, у каждого оружия есть радиус действия (range of use). Меч может поразить цель, находящуюся прямо перед вами, в то время, как стрела лука может попасть в цель, расположенную в десятках метров от вас. Кроме того, некоторые виды оружия могут поражать сразу несколько целей. Все эти вещи следует учесть при разработке оружия.

Броня

Чем больше защиты, тем лучше, и в игре помогает каждый кусочек брони. Броня помогает повысить сопротивляемость к повреждениям. Количество сопротивляемости называется модификатором защиты (defense modifier). Также как и у вооружения, у брони есть специальные возможности, ограничения использования и возможность объединения в группы защиты (armor groups).

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

Аксессуары

Как упоминалось ранее, у аксессуаров обычно есть специфические области применения. Магическое кольцо можно надеть для того, чтобы стать невидимым. Вы можете стать невидимым сразу, как только наденете кольцо, или, возможно, невидимость будет требовать активации. Аксессуары могут также действовать как броня; они могут увеличивать сопротивляемость некоторым воздействиям в игре — например, повышать сопротивляемость к ядам.

Еда

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

Коллекции

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

Транспорт

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

Прочее

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

Добавление функций к объекту

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

Категории предметов и значения

Фактически, вам потребуется однозначно категоризировать каждый предмет в соответствии с вашим игровым движком, как это сделал я в разделе «Определение функций объекта». Каждая категория объектов нумеруется для ссылок (1 — оружие, 2 — броня и т.д.). У каждой категории есть связанные с ней значения, они могут определять модификатор (атака или защита), специальное использование, количество наносимых повреждений или добавляемого здоровья и присоединенный скрипт. Верно. Предметы могут использовать скрипты для увеличения своих возможностей.

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

    // ... Предыдущая информация sItem

    long Category; // 1-5 представляет показанные выше
                   // категории предметов
    long Value;    // Модификатор, увеличение здоровья и т.д.

    // ... Дальнейшая информация sItem

 

СОВЕТ
Для значения, представляющего категорию предмета в структуре 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);

Ограничения использования

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

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

Чтобы представить ограничения использования элемента, в структуру 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 сброшен), игровой движок сообщит игроку, что маг не может использовать этот предмет.

Присоединение скриптов к предметам

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

СОВЕТ
При использовании скриптов хорошо применять специальный шаблон действий, приспособленный для предметов. За дополнительной информацией о создании шаблона действий и использовании редактора скриптов обратитесь к главе 10, «Реализация скриптов».

Сейчас вам надо только сохранить в структуре 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;

 

ПРИМЕЧАНИЕ
Помните, что размер буферов ScriptFilename, MeshFilename и ImageFilename ограничен 16 байтами, а значит, в структуре sItem вы можете хранить только имя файла без пути к нему. Кроме того, хотя структура 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< Назад | Оглавление | Далее >

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