cAutomap::load and cAutomap::save
Recall that you need to enable
each map section in order for it to be visible when
rendered. The m_visible array tracks the visibility of each map section; if an
array
element is set to 0, the respective map section is not displayed. If the element
is set
to 1, the map section is drawn.
In your game, once the map
sections are marked as visible, you save those flags
so that a player can track his progress through the game and later load his map
progression to continue the game-play. The load and save functions do just that:
bool cAutoMap::load(pcstr filename)
{
FILE* fp = fopen(filename, "rb");
if(fp == NULL)
return false;
long num_sections;
fread(&num_sections, 1, sizeof(num_sections), fp);
fread(m_visible, 1, num_sections, fp);
fclose(fp);
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
bool cAutoMap::save(pcstr filename)
{
FILE* fp = fopen(filename, "wb");
if(fp == NULL)
return false;
fwrite(&m_num_sections, 1, sizeof(m_num_sections), fp);
fwrite(m_visible, 1, m_num_sections, fp);
fclose(fp);
return true;
}
The storage format for the
visibility array is simple: The file starts with a long variable
that states how many elements are in the array. Following that, the entire map
visibility
array is written out. To load the visibility array back up, read in the number
of
elements, ensure that they match the currently loaded map, and load in the
array.
cAutomap::get_num_section and cAutomap::visible_section,
cAutomap::invisible_section
These two functions return the
number of map sections loaded and allow you to
set the visibility of each map section. Each map section is numbered
sequentially
from the order stored in the .X file.
cAutomap::set_viewport and cAutomap::render
You use set_viewport to define
the area in which you want the auto map displayed
(specified in screen coordinates plus height and width in pixels). As you can
see,
the function is small—it only sets up the viewport structure declared in the
cAutomap
class.
As for the render function,
this is where your hard work shows. To display a map,
you have to provide a pointer to a camera that you are currently using (to
restore
it after changing the view matrix), the coordinates of the map camera to use
when
rendering, the number of characters to display on the map, and three arrays that
define each character’s coordinates and facing angle to draw on the auto map:
void cAutoMap::render(cCamera* old_camera,
float map_x_pos, float map_y_pos, float map_z_pos,
float num_arrows,
float* arrow_x_pos, float* arrow_z_pos, float* angle)
{
m_camera.move(map_x_pos * m_scale, map_y_pos, map_z_pos * m_scale);
set_display_camera(&m_camera);
D3DVIEWPORT9 old_viewport;
g_d3d_device->GetViewport(&old_viewport);
g_d3d_device->SetViewport(&m_viewport);
disable_zbuffer();
g_d3d_device->SetTexture(0, NULL);
// draw little map
cWorldPos world_pos;
set_display_world(&world_pos);
enable_alpha_blending(D3DBLEND_SRCCOLOR, D3DBLEND_DESTCOLOR);
for(long i = 0; i < m_num_sections; i++)
{
if(m_visible[i])
render_vertex_buffer(m_map_vb[i], 0, get_num_vertices(m_map_vb[i]) / 3, D3DPT_TRIANGLELIST);
}
disable_alpha_blending();
// draw the character positions
for(long i = 0; i < num_arrows; i++)
{
world_pos.move(arrow_x_pos[i] * m_scale, 0.0f, arrow_z_pos[i] * m_scale);
world_pos.rotate(0.0f, angle[i], 0.0f);
set_display_world(&world_pos);
render_vertex_buffer(m_arrow_vb, 0, 1, D3DPT_TRIANGLELIST);
}
// restore old camera if passed
if(old_camera)
set_display_camera(old_camera);
// restore old viewport
g_d3d_device->SetViewport(&old_viewport);
}
The render function starts off by
defining a few variables, performing some errorchecking,
and setting up a camera to render the map sections. That’s right. The
map sections are still 3-D meshes, just flat and viewed from above (which is the
reason
for the camera being rotated down earlier in the code).
Next you create the rendering
viewport (with the old viewport settings saved for
later restoring). You set the rendering states (no Z-buffering and no textures)
and
a transformation matrix to center the auto map in the world:
Next you render every map
section. Actually, only those map sections that are
flagged as visible are rendered. The code to render those map sections is small,
so
you can wrap it up with the code that renders the pointers (which represent the
characters’ positions on the map).
After rendering the map
sections, you disable alpha blending (in case it was used
to render the map) and position and render the pointer vertex buffer for each
character that was passed to the render function.
Last, you restore the camera
and viewport settings that were used prior to rendering
the auto map.
Using
cAutomap
The mapping demo for this
chapter contains a perfect example of using the auto
map class, but to give you a clear idea of its use, here is an example. Start by
instancing the cAutomap class and call create to load an .X file:
cAutomap
Automap;
Automap.create(“Map.x”, D3DCOLOR_RGBA(64,64,64,255));
At this point, the map is
loaded and ready to go. The map uses a color of dark gray
for rendering (which is the reason for the D3DCOLOR_RGBA macro). To start
rendering
the map, you must first set the position of the window to which you are
rendering:
Automap.set_viewport(0,0,200,200); // Use 0,0 to 200,200 for map
Next, you mark a map section as
visible:
Automap.visible_section(0); // Set 1st section to visible
All that’s left to do is to
render the map:
Automap.render(NULL, 0.0f, 200.0f, 0.0f, 0, NULL, NULL, NULL);
The preceding call positions
the camera and renders the single visible map section. Now, what about the
other map sections? How is your game going to know which map sections to flag as
visible? By using triggers; that’s how!
By instancing a cTrigger class,
you can embed triggers into your map that signal
which map sections have been entered, and thus marked as visible. You just mark
those map triggers using the same identification numbers as the map section mesh
contained with the map .X file (the first mesh in the file needs a trigger with
an
identification number of 1, the second mesh needs a trigger identification
number
of 2, and so on).
The Mapping example uses
triggers to mark sections of the map to display as characters
enter them—be sure to check out the example to see just what I’m talking about.