天行健 君子当自强而不息

Putting Together a Full Game(10)

 

Using State-Based Processing

I developed the sample game to use state-based processing in order to effectively
use the application class's processing structure. The game uses these four states:

■ Main menu state. When executed, the game displays a main menu giving the
player the option to start a new game, load a game, return to or save a game
in progress, or to quit the game.

■ In-game state. This state is used most often because it takes care of updating
and rendering each frame of the game.

■ Character status window state. Whenever the player right-clicks during gameplay,
he accesses the character status window. Here, the player can use, equip,
or unequip items just by clicking them, as well as check on the character’s
statistics and known spells.

■ Barter window state. When the player talks to the villager, the barter window
opens in order to buy items. Click items to buy or press Esc or the right
mouse button to exit.

You use a state manager object to control the processing of these four states.

 

menu_frame:

 You use the menu_frame function to display the main menu, which, in all its glory, has
 a spinning texture-mapped polygon overlaid with the main menu options. The purpose
 of the menu_frame function is to track which option is being selected and to handle
 the appropriate functions.

void menu_frame(void* data, long purpose)
{
    
static const sMenuVertex verts[] = 
    {
        { -100.0f,  100.0f, 1.0f, 0.0f, 0.0f },
        {  100.0f,  100.0f, 1.0f, 1.0f, 0.0f },
        { -100.0f, -100.0f, 1.0f, 0.0f, 1.0f },
        {  100.0f, -100.0f, 1.0f, 1.0f, 1.0f }
    };
    
    
static IDirect3DVertexBuffer9*  menu_vb;
    
static IDirect3DTexture9*       menu_texture;
    
static IDirect3DTexture9*       menu_select;
    
static ID3DXFont*               title_font;
    
static cCamera                  menu_cam;
    
static cWorldPos                menu_pos;
    
    cApp* app = (cApp*) data;

    
if(purpose == INIT_PURPOSE) // initialize menu related data
    {   
        
// create and set the menu vertices
        create_vertex_buffer(&menu_vb, array_num(verts), sizeof(sMenuVertex), MENU_FVF);
        fill_in_vertex_buffer(menu_vb, 0, array_num(verts), verts);

        load_texture_from_file(&menu_texture, "..\\Data\\MenuBD.bmp", 0, D3DFMT_UNKNOWN);
        load_texture_from_file(&menu_select,  "..\\Data\\Select.bmp", 0, D3DFMT_UNKNOWN);

        create_font(&title_font, "Consolas", 48, 
falsefalse);
        menu_cam.point(0.0f, 0.0f, -150.0f, 0.0f, 0.0f, 0.0f);
    }
    
else if(purpose == SHUTDOWN_PURPOSE)    // shutdown resources used in menu
    {
        release_com(menu_vb);
        release_com(menu_texture);
        release_com(menu_select);
        release_com(title_font);
    }
    
else    // process a frame of menu
    {
        
// exit game or return to game if ESC pressed
        if(app->m_keyboard.get_key_state(KEY_ESC))
        {
            app->m_keyboard.m_locks[KEY_ESC] = 
true;
            app->m_keyboard.set_key_state(KEY_ESC, 
false);

            app->m_state_manager.pop(app);
            
return;
        }        

        
// see which option was selected if mouse button pressed
        if(app->m_mouse.get_button_state(MOUSE_LBUTTON))
        {
            
// lock the mouse button and clear button state
            app->m_mouse.m_locks[MOUSE_LBUTTON] = true;
            app->m_mouse.set_button_state(MOUSE_LBUTTON, 
false);

            
// determine which, if any selection.

            
long mouse_start = app->m_mouse.get_y_pos() - MAIN_MENU_TOP;

            
if(mouse_start >= 0)
            {
                
long hit_index = mouse_start / MAIN_MENU_HEIGHT;
                
                app->m_state_manager.pop(app);  
// pop the menu state

                // determine what to do based on selection
                switch(hit_index)
                {
                
case NEW_GAME:
                    app->m_state_manager.pop_all(app);

                    app->m_game_chars.free();
                    app->m_game_spells.free();
                    app->m_game_script.reset_data();
                    
                    app->m_game_chars.add_char(ID_PLAYER, 0, CHAR_PC, CHAR_STAND, -100.0f, 0.0f, 50.0f, 3.14f);
                    g_player = app->m_game_chars.get_char(ID_PLAYER);

                    app->m_teleport_map = -1;

                    app->m_state_manager.push(game_frame, app);

                    
// start new game and let script process as startup
                    app->load_level(1);

                    
break;

                
case RETURN_TO_GAME:
                    app->m_state_manager.push(game_frame, app);
                    
break;

                
case LOAD_GAME:
                    app->m_state_manager.pop_all(app);

                    app->m_game_chars.free();
                    app->m_game_spells.free();

                    app->m_game_chars.add_char(ID_PLAYER, 0, CHAR_PC, CHAR_STAND, -100.0f, 0.0f, 50.0f, 3.14f);
                    g_player = app->m_game_chars.get_char(ID_PLAYER);

                    
// load character's stats and inventory
                    app->m_game_chars.load_char(ID_PLAYER, "..\\Data\\Char.cs");
                    g_player->char_ics->load("..\\Data\\Char.ci");

                    
if(g_player->char_def.weapon != -1)
                        app->m_game_chars.equip(g_player, g_player->char_def.weapon, WEAPON, 
true);

                    g_player->health_points = g_player->char_def.health_points;
                    g_player->mana_points   = g_player->char_def.mana_points;

                    app->m_game_script.load("..\\Data\\Script.sav");
                    app->m_teleport_map = -1;

                    app->m_state_manager.push(game_frame, app);

                    app->m_game_chars.move_char(ID_PLAYER, 100.0f, 0.0f, -100.0f);
                    app->load_level(1);     
// start in town

                    
break;

                
case SAVE_GAME:
                    app->m_game_script.save("..\\Data\\Script.sav");

                    
// save character's stats and inventory
                    app->m_game_chars.save_char(ID_PLAYER, "..\\Data\\Char.cs");
                    g_player->char_ics->save("..\\Data\\Char.ci");

                    
break;

                
case QUIT_GAME:
                    app->m_state_manager.pop_all(app);
                    
break;
                }

                
return;
            }
        }   
// [end] if(app->m_mouse.get_button_state(MOUSE_LBUTTON))

        set_display_camera(&menu_cam);
        
        menu_pos.rotate(0.0f, 0.0f, timeGetTime() / 4000.0f);   
// rotate backdrop

        // render menu backdrop and all menus

        begin_display_scene();

        disable_zbuffer();
        set_display_world(&menu_pos);
        g_d3d_device->SetTexture(0, menu_texture);
        render_vertex_buffer(menu_vb, 0, 2, D3DPT_TRIANGLESTRIP);

        
// draw the game's title
        draw_font(title_font, g_title_name, 0, 16, CLIENT_WIDTH, 0, COLOR_LIGHT_YELLOW, DT_CENTER);

        
// select option based on mouse position

        
long mouse_start = app->m_mouse.get_y_pos() - MAIN_MENU_TOP;

        
if(mouse_start >= 0)
        {
            
long hit_index = mouse_start / MAIN_MENU_HEIGHT;

            
if( hit_index == NEW_GAME ||
               (hit_index == RETURN_TO_GAME && (g_menu_options & MENU_BACK)) ||
               (hit_index == LOAD_GAME && (g_menu_options & MENU_LOAD)) ||
               (hit_index == SAVE_GAME && (g_menu_options & MENU_SAVE)) ||
               (hit_index == QUIT_GAME))
            {
                begin_display_sprite();

                RECT rect;
                calculate_texture_rect(menu_select, 0, 0, 0, 0, &rect);

                
long dest_y = hit_index * MAIN_MENU_HEIGHT + MAIN_MENU_TOP;
                draw_texture(g_d3d_sprite, menu_select, &rect, 192, dest_y, 1.0f, 1.0f, COLOR_WHITE);

                end_display_sprite();
            }
        }

        
// draw enabled options 
        
        draw_font(app->m_font, "New Game", 0, 150, CLIENT_WIDTH, 0, COLOR_LIGHT_YELLOW, DT_CENTER);

        
if(g_menu_options & MENU_BACK)
            draw_font(app->m_font, "Back to Game", 0, 214, CLIENT_WIDTH, 0, COLOR_LIGHT_YELLOW, DT_CENTER);

        
if(g_menu_options & MENU_LOAD)
            draw_font(app->m_font, "Load Game", 0, 278, CLIENT_WIDTH, 0, COLOR_LIGHT_YELLOW, DT_CENTER);

        
if(g_menu_options & MENU_SAVE)
            draw_font(app->m_font, "Save Game", 0, 342, CLIENT_WIDTH, 0, COLOR_LIGHT_YELLOW, DT_CENTER);
        
        draw_font(app->m_font, "Quit", 0, 410, CLIENT_WIDTH, 0, COLOR_LIGHT_YELLOW, DT_CENTER);

        end_display_scene();

        present_display();
    }
}

posted on 2007-12-29 21:22 lovedday 阅读(235) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论