本篇是游戏中物件的定义与使用(5)的续篇。
cMapIcs::get_next_long,cMapIcs::get_next_float
//----------------------------------------------------------------------------
// Return long value from next line in file.
//----------------------------------------------------------------------------
long get_next_long(FILE* fp)
{
char buf[1024];
long pos = 0;
int c;
// Read until EOF or EOL
while(1)
{
if((c = fgetc(fp)) == EOF || c == '\n' || pos == sizeof(buf)-1)
break;
if((c >= '0' && c <= '9') || c == '.' || c == '-')
buf[pos++] = c;
}
if(pos == 0) // if there is no long value in file
return -1;
buf[pos] = 0;
return atol(buf);
}
//----------------------------------------------------------------------------
// Return float value from next line in file.
//----------------------------------------------------------------------------
float get_next_float(FILE* fp)
{
char buf[1024];
long pos = 0;
int c;
// Read until EOF or EOL
while(1)
{
if((c = fgetc(fp)) == EOF || c == '\n' || pos == sizeof(buf)-1)
break;
if((c >= '0' && c <= '9') || c == '.' || c == '-')
buf[pos++] = c;
}
buf[pos] = 0;
return (float)atof(buf);
}
构造函数、析构函数
//----------------------------------------------------------------------------
// Constructor, initialize member data.
//----------------------------------------------------------------------------
cMapIcs::cMapIcs()
{
memset(this, 0, sizeof(*this));
}
//----------------------------------------------------------------------------
// Destructor, free allocated resource.
//----------------------------------------------------------------------------
cMapIcs::~cMapIcs()
{
free();
}
cMapIcs::load, cMapIcs::save, and cMapIcs::free
As their names suggest, this
trio of functions loads, saves, and frees a list of items
that belong to a map. The first of the three, Load, loads and creates a list of
items.
For simplicity, store all items in a text file, using the following format:
MIL_ItemNum
Quantity
XPos
YPos
ZPos
ParentID
Each item uses six lines of text, and each entry (group of six lines) is
numbered
sequentially (the first item in the file is item #0, the second item is #1, and
so on).
Here’s a sample file that contains two items:
// Item #0 as follows:
10 // MIL Item # (long value)
1 // Quantity (long value)
10.0 // XPos (float value)
0.0 // YPos (float value)
600.0 // ZPos (float value)
-1 // Owner (-1 = none, index # otherwise)
// Item #1 as follows:
1 // MIL Item #
1 // ...
10.0
0.0
600.0
0 // belongs to item #0 (first item in file)
The preceding comments are for
clarification; the actual storage file does not use
them. When reading in a list of items such as the preceding ones, the Load
function
converts the text into usable numbers. Using those numbers, it creates a
sMapItem
structure for each item in the map to be loaded, constructing a linked list as
the
items are loaded. After every item is read in, all objects that belong to
another are
matched up (using the Parent pointer in the sMapItem structure).
There’s really nothing too difficult here, so jump right into the cMapIcs::load
code:
//----------------------------------------------------------------------------
// Load all map items from file.
//----------------------------------------------------------------------------
bool cMapIcs::load(const char* filename)
{
free(); // free a prior set
FILE* fp;
if((fp = fopen(filename, "rb")) == NULL)
return false;
long item_index;
sMapItemPtr item;
sMapItemPtr item_ptr = NULL;
// loop forever reading in items
while(1)
{
// get next item number (break if no more items, which is represented by a return value of -1).
if((item_index = get_next_long(fp)) == -1)
break;
// create a new map item and link it in
item = new sMapItem;
if(item_ptr == NULL)
m_root_item = item;
else
{
item->prev = item_ptr;
item_ptr->next = item;
}
item_ptr = item;
item->item_index = item_index;
item->quantity = get_next_long(fp);
item->x_pos = get_next_float(fp);
item->y_pos = get_next_float(fp);
item->z_pos = get_next_float(fp);
item->owner_index = get_next_long(fp);
item->index = m_num_items++;
}
fclose(fp);
// match objects that belong to others
for(item_ptr = m_root_item; item_ptr != NULL; item_ptr = item_ptr->next)
{
// check if this item belongs to another
if(item_ptr->owner_index == -1)
continue;
// find matching parent item
for(item = m_root_item; item != NULL; item = item->next)
{
if(item_ptr->owner_index == item->index)
{
// a match, point to parent and stop scanning for parents.
item_ptr->parent = item;
break;
}
}
}
return true;
}
Save takes an internal list of
items and, using the filename you specify, saves that list
to a file on disk. The Save function is typically used to update the game data,
because
players might consistently pick up and drop items.
The Save function first assigns an index value to each sMapItem structure in the
linked list (based on their order). The first item in the linked list is 0
(zero), the
second item is 1, and so on. Each child item’s Owner variable is updated as well
at
this point, and finally all data is written to a file:
//----------------------------------------------------------------------------
// Save all map items to file.
//----------------------------------------------------------------------------
bool cMapIcs::save(const char* filename)
{
FILE* fp;
if((fp = fopen(filename, "wb")) == NULL)
return false;
sMapItemPtr item;
long index = 0;
for(item = m_root_item; item != NULL; item = item->next)
{
item->index = index++;
// match child items to parents
if(item->parent)
item->owner_index = item->parent->index;
else
item->owner_index = -1;
fprintf(fp, "%lu\r\n%lu\r\n%lf\r\n%lf\r\n%lf\r\n%ld\r\n",
item->item_index, item->quantity,
item->x_pos, item->y_pos, item->z_pos,
item->owner_index);
}
fclose(fp);
return true;
}
Finally, you use the Free
function when destroying the class (thus, deleting the
linked list of items). Here’s the code for free:
//----------------------------------------------------------------------------
// free allocate resource.
//----------------------------------------------------------------------------
void cMapIcs::free()
{
m_num_items = 0;
delete m_root_item;
}
You’re just deleting the item linked list and getting the
class ready for further use.
cMapIcs::Add and cMapIcs::Remove
As items are added to the map
(as the result of a player dropping them, for example),
you need to call add to make sure those dropped items make it into the list of
map objects. The add function does this by first allocating a sMapItem
structure, filling
it with the appropriate item information that you give it and then linking it
into the
map’s list of items:
//----------------------------------------------------------------------------
// Add map item into list.
//----------------------------------------------------------------------------
void cMapIcs::add(long item_index, long quantity,
float x_pos, float y_pos, float z_pos,
sMapItemPtr owner_item)
{
sMapItemPtr item = new sMapItem;
// fill the item structure
item->item_index = item_index;
item->quantity = quantity;
item->x_pos = x_pos;
item->y_pos = y_pos;
item->z_pos = z_pos;
item->parent = owner_item;
// insert into top of list
item->next = m_root_item;
if(m_root_item)
m_root_item->prev = item;
m_root_item = item;
}
Just as the Add function is used
to add objects to the map’s list of items, you’ll need
to use Remove to remove items from a map. You call Remove using the item’s
identifier
that you wish to remove from the map’s list. Remove also deletes the allocated
item
structure and takes care of items that belong to the removed item:
//----------------------------------------------------------------------------
// Remove map item from list.
//----------------------------------------------------------------------------
void cMapIcs::remove(sMapItemPtr item)
{
sMapItemPtr item_ptr;
sMapItemPtr next_item;
// remove child objects first
for(item_ptr = m_root_item; item_ptr != NULL; item_ptr = next_item)
{
next_item = item_ptr->next;
if(item_ptr->parent == item)
remove(item_ptr);
}
// remove from linked list and reset root if it's the current head of list.
if(item->prev)
item->prev->next = item->next;
else
m_root_item = item->next;
if(item->next)
item->next->prev = item->prev;
item->prev = item->next = NULL;
delete item;
}
cMapIcs::get_num_items,cMapIcs::get_parent_item,and cMapIcs::get_item
You use these three functions
to retrieve the number of items that belong to the map and to retrieve the
parent
sMapItem or specified item structure in the linked list. The first two of the
following three functions return a single variable while the third function does
the hard work by scanning through the linked list of objects, returning the
specified item in the list:
//----------------------------------------------------------------------------
// Return number of map items.
//----------------------------------------------------------------------------
long cMapIcs::get_num_items()
{
return m_num_items;
}
//----------------------------------------------------------------------------
// Return root map item.
//----------------------------------------------------------------------------
sMapItemPtr cMapIcs::get_root_item()
{
return m_root_item;
}
//----------------------------------------------------------------------------
// Return map item with specified index.
//----------------------------------------------------------------------------
sMapItemPtr cMapIcs::get_item(long index)
{
sMapItemPtr item;
// loop until reached item index
for(item = m_root_item; item != NULL && index != 0; item = item->next)
index--;
return item;
}
cMapIcs类的使用
Every map in your game will
have an associated list of items that belongs to it. The
map ICS will load those items and provide them to your engine whenever it needs
to render the map or add a specific item to a player’s inventory (when an item
contained in the map is picked up).
Take a look at the following code bit, which loads a sample list of items, adds
a
single item, removes another item, and saves the list of items:
cMapIcs
MapICS;
MapICS.load(“sample.mi”); // Load the file
// Add 1 of item # 10
MapICS.add(10, 1, 0.0f, 0.0f, 100.0f, NULL);
// Remove 2nd item from list
MapICS.remove(MapICS.GetItem(1));
// Save list back out
MapICS.save(“sample.mi”);
Although this is a simple
example of modifying a map’s item list, why not go ahead
and see just how complicated it can become.
The MapICS demo (see following snap) contains the full cMapICS class and the
sItem structure from the MIL edit program. You use the map ICS and MIL to
render a list of objects spread around a simple level.
The MapICS loads the map items
and uses their coordinate data to draw
a generic item mesh in the scene. Whenever you approach an item, a
targeting icon appears and displays the name of the item.
NOTE
The MapICS demo allows you to walk around a simple level by using the
arrow keys and mouse.You can pick up items by standing in front of one and
pressing the left mouse button. Pressing the right mouse button
causes you to drop a random item.
Once the level is rendered
out, each item in the map is scanned and drawn if in
view. The closest item is detected, and its name is printed to the screen. The
code
is well commented, so you should have no problem breezing through it.