本篇是游戏中物件的定义与使用(1)的续篇。
因为每个物件都被分类以便与另外的物件区分开来,所以并不是所有的信息都是必须的,剑具有杀伤力,而护甲则提供保护,因此没有必要去混合损伤与防御的数据。
需要将每个物件进行分类,以便游戏引擎可以使用它们,每种类别的物件都被加以编号(1是武器,2是护甲,以此类推)。每种类型都有一个相关的价值,如物件的等级指数(攻击或防御)、特定用途、治愈值或损伤值,以及一个附属的脚本。是的,物件可以使用脚本增强它们的能力。除了所附属的脚本外,还可以使用一个变量去表示所有的值,如等级指数、治愈值等。
TIP
You can use an enumerator value to represent the categories in the sItem
structure:
enum ItemCategories { WEAPON=0,ARMOR,SHIELD,HEALING,OTHER };
游戏中的每个物件都是有价值的,为每个物件指定一个货币价值可以帮助玩家在购买或出售物件时确定它的价格,相同的物件售出的价格通常比购买的价格稍低。
有时并不希望玩家能够出售某件物品,例如一个非常重要的魔术物件。一个标志位就能够起到这样的作用,而且还可以添加其他更多的标志位。
将每个标志表示为一个enum数值(最多32个标志),设置、清除、或检查一个标志,可以使用随后的宏(在宏的使用中,v代表物件的标志变量,而f代表了该标志):
enum {
SELLABLE = 0, // Bit 0
CANDROP, // Bit 1
USEONCE, // Bit 2
UNKNOWN // Bit 3
};
#define SetItemFlag(v,f) (v |= (1 << f))
#define ClearItemFlag(v,f) (v &= ~(1 << f))
#define CheckItemFlag(v,f) (v & (1 << f))
// Example using macros and flags
long ItemFlags = 0;
// Set item flags to sellable and item can be dropped
SetItemFlag(ItemFlags, SELLABLE);
SetItemFlag(ItemFlags, CANDROP);
// Check if the item is dropable and display a message
if(CheckItemFlag(ItemFlags, CANDROP))
MessageBox(NULL, “Can Drop Item”, “Item”, MB_OK);
ClearItemFlag(ItemFlags, SELLABLE); // Clear sellable flag
使用限制
游戏中的某些角色可能不能使用某个特定的物件。例如一个魔法师,他不可能挥舞一把巨大的战斧,而一个野蛮人则不可能施展法术。在这种情况下,特定的角色只能被允许去使用特定的物件,所以需要指定角色类别的使用限制。
NOTE
A character class is a classification or grouping of characters based on their
race or profession. For example,
all humans belong to the same class, but to be more specific, human fighters are
considered a
separate class from human wizards (or just fighters and wizards—who says they
all have to be human).
To represent the usage
restrictions of an item, another variable is introduced to the
sItem structure, one that tracks 32 bits of information. Each bit represents a
single
class, which means that you can track up to 32 classes. If an item is usable by
a certain
class, that respective bit is set; if an item is restricted in use by the
character’s
class, the appropriate bit is cleared.
Here’s the addition to the sItem structure, which handles usage restrictions:
long Usage; // Usage restrictions
// ... other sItem data
To make setting, clearing, and retrieving a usage restriction class bit easier,
you can
use the following macros (v represents the flag variable, and c is the class
number
ranging from 0 to 31):
#define
SetUsageBit(v,c) (v |= (1 << c))
#define ClearUsageBit(v,c) (v &= ((~(1 << c))
#define CheckUsageBit(v,c) (v & (1 << c))
// Examples using macros
long Flags = 0;
SetUsageBit(Flags, 5); // Set class 5 bit
if(CheckUsageBit(Flags, 5)) // Check class 5 bit
MessageBox(NULL, “Usage Set”, “Bit”, MB_OK);
ClearUsageBit(Flags, 5); // Clear class 5 bit
Using the preceding macros (SetUsageBit, ClearUsageBit, and CheckUsageBit), you
can
quickly check whether a character is allowed to use or equip the item based on
his
character class. For example, this game places wizards in class 1 and fighters
in
class 2. When the wizard tries to equip a broadsword (one that has the class 1
bit
clear), the game engine informs the player that the wizard cannot use the item.
为了使物件能够更加灵活通用,可以为物件附上脚本。无论是使用疗伤药,或是在战斗中使用剑,或者玩家启用了某种特定的物件(例如使用魔杖),每当一个物体被使用时,它的脚本也被触发。
最终的物件结构定义如下:
enum ItemCategories
{
MONEY = 0,
WEAPON,
ARMOR,
SHIELD,
ACCESSORY,
EDIBLE,
HEALING,
COLLECTION,
TRANSPORTATION,
CONTAINER,
OTHER
};
#define set_bit(v, c) ((v) |= (1 << (c)))
#define clear_bit(v, c) ((v) &= ~(1 << (c)))
#define check_bit(v, c) ((v) & (1 << (c)))
enum
{
SELLABLE = 0, // bit 0
CANDROP, // bit 1
USEONCE, // bit 2
UNKNOWN // bit 3
};
typedef struct sItem
{
char name[32]; // a short name for the item
char desc[128]; // a desciption of item
float weight; // weight (in lbs.)
float size; // size (in cubic feet)
long category; // category of item
long value; // modifier, health increase, etc.
long price; // buying price of item
long flags; // item bit flags
long usage; // usage restrictions
char script_filename[16]; // .mls script filename
char mesh_filename[16]; // .x mesh filename
char image_filename[16]; // .bmp image filename
} *sItemPtr;
With the complete sItem
structure in place, it’s time to get back to building the
sword item. Say that the sword item uses a +10 modifier on damage (which means
that you add 10 to the damage factor in combat). The sword normally sells for
200
monetary units in the game, and only fighter classes (class two) can use it:
// Character class definitions
#define WIZARD 1
#define WARRIOR 2
sItem Sword = {
“Sword”, “A big heavy sword”, // name and description
5.0f, 4.0f, // weight and size
WEAPON, 200, SELLABLE | CANDROP, // category, price, and flags
(1 << WARRIOR), // usage class 2 (warrior)
“”, “Sword.x”, “Sword.bmp” // Script, mesh, image files
};
Now that the sword item is defined, you can use it in the game. But what good is
a single
item? Your game world is going to be packed with items! How can you possibly
deal with all
those objects?
主物件列表
游戏中的每个物件都需要被定义,同时为了使事情保持简洁,需要在主物件列表(master item
list,MIL)中记录所有物件的描述。可以将MIL想象成一个物件的目录,如下图所示,每个物件都进行编号以便引用,同时每种物件仅显示一个。
每当需要一个新物件时,或者需要检索指定物件的属性特征时,可以搜索MIL。在一个基本的层面上,游戏的MIL可以被存储为sItem结构的数组,或一个顺序文件,它由物件结构的列表所组成,如下图所示:
构造MIL
The following code bit creates
a small item structure that contains the item’s name,
weight, and size. You will use this structure to construct a simple MIL:
typedef struct sItem
{
char Name[32]; // Name of item
float Weight; // Weight (in lbs.)
float Size; // Size (in cubic ft.)
};
From here, say that you want
to store five items in the MIL, all represented in an
array of sItem structures:
sItem Items[5]
= {
{ “Big Sword”, 5.0f, 4.0f },
{ “Small Sword”, 2.0f, 2.0f },
{ “Magic Wand”, 0.5f, 1.0f },
{ “Rock”, 1.0f, 0.5f },
{ “Potion”, 0.5f, 0.5f }
};
Now that you have defined your
MIL (using an array of sItem structures), you may want
to save the list out to a file for later retrieval. Such is the case if you are
using a separate
program that creates the MIL file for you, much like the program you’ll see in
the
upcoming section, “Using the MIL Editor.” As for here, take a look at the
following bit
of code that will create a file (called items.mil) and save the Items array to
the file:
FILE *fp=fopen(“items.mil”, “wb”);
for(short i=0;i<5;i++)
fwrite(&Items[i], 1, sizeof(sItem), fp);
fclose(fp);
Although short and to the
point, the preceding example for creating a MIL file
is wholly unusable in a real-world application such as a role-playing game. Item
descriptions need to contain much more information, and you could theoretically
work with thousands of items. Doing all that by hand is a waste of time. What
you
need is an item editor to help you create and maintain the MIL . . . and, so,
behold
the MIL Editor.