天行健 君子当自强而不息

Getting Online with Multiplayer Gaming(18)

 

Updating the Local Player

Between updates from the server, the clients need to update all players to keep the
game running smoothly. The client application limits updates to every 33ms (30 times
a second), which matches the server update rate. Between these player updates, the
client is allowed to collect input from the player who is used to change their actions.

The cApp::frame function is generally used to update the local player. The players
use the keyboard and mouse to control their characters, so I included a few Input
Core objects (m_keyboard and m_mouse):

bool cApp::frame()
{
    
// get local input every frame
    m_keyboard.acquire();
    m_mouse.acquire();
    m_keyboard.read();
    m_mouse.read();

    
// handle connection screen
    if(!g_connected || m_players[0].player_id == 0)
    {
        
// display connection message

        clear_display(0, 1.0f);

        
if(begin_display_scene())
        {
            draw_font(m_font, "Connecting to server ", 0, 0, 0, 0, COLOR_WHITE, DT_LEFT);
            end_display_scene();
        }

        present_display();
        
return true;
    }

    
// store movements every frame

    
static long move_action = 0, last_move = 0;

    
if(m_keyboard.get_key_state(KEY_UP) || m_keyboard.get_key_state(KEY_W))
        move_action |= ACTION_MOVE_UP;

    
if(m_keyboard.get_key_state(KEY_RIGHT)  || m_keyboard.get_key_state(KEY_D))
        move_action |= ACTION_MOVE_RIGHT;

    
if(m_keyboard.get_key_state(KEY_DOWN)  || m_keyboard.get_key_state(KEY_S))
        move_action |= ACTION_MOVE_DOWN;

    
if(m_keyboard.get_key_state(KEY_LEFT)  || m_keyboard.get_key_state(KEY_A))
        move_action |= ACTION_MOVE_LEFT;

    
// store attack action
    if(m_keyboard.get_key_state(KEY_SPACE) || m_mouse.get_button_state(MOUSE_LBUTTON))
        move_action |= ACTION_ATTACK;

    
// rotate camera

    
static bool cam_moved = false;

    
if(m_mouse.get_x_delta() > 0)
    {
        m_cam_angle -= 0.1f;
        cam_moved = 
true;
    }

    
if(m_mouse.get_x_delta() < 0)
    {
        m_cam_angle += 0.1f;
        cam_moved = 
true;
    }

    
static DWORD update_counter = timeGetTime();

    
// only update players every 33ms (30 times a second)
    if(timeGetTime() < update_counter + 33)
        
return true;

    
// set flag to allow player movement
    bool allow_move = true;

    
// do not allow movement if still swinging weapon or being hurt
    if(m_players[0].last_state == STATE_SWING || m_players[0].last_state == STATE_HURT)
        allow_move = 
false;

    
// handle movements if allowed
    if(allow_move)
    {
        
// process attack
        if(move_action & ACTION_ATTACK)
        {
            move_action = 0;    
// clear movement
            last_move   = 0;    // clear last movement

            // send attack message - let server signal swing

            sStateChangeMsg change_msg;

            change_msg.header.type      = MSG_STATE_CHANGE;
            change_msg.header.size      = 
sizeof(sStateChangeMsg);
            change_msg.header.player_id = m_players[0].player_id;
            change_msg.state            = STATE_SWING;
            change_msg.direction        = m_players[0].direction;

            send_network_msg(&change_msg, DPNSEND_NOLOOPBACK);
        }

        
// process local player movements
        if(move_action > 0 && move_action < 13)
        {
            
// set new player state
            
            EnterCriticalSection(&m_update_cs);

            m_players[0].last_state = STATE_MOVE;
            m_players[0].direction  = g_angles[move_action] - m_cam_angle + 4.71f;

            LeaveCriticalSection(&m_update_cs);

            
// reset last move if camera moved since last update
            if(cam_moved)
            {
                cam_moved = 
false;
                last_move = 0;
            }

            
// send actions to server if changed from last move
            if(move_action != last_move)
            {
                last_move = move_action;    
// store last action

                m_players[0].last_update_time = timeGetTime();

                sStateChangeMsg change_msg;

                
// construct message
                change_msg.header.type      = MSG_STATE_CHANGE;
                change_msg.header.size      = 
sizeof(sStateChangeMsg);
                change_msg.header.player_id = m_players[0].player_id;
                change_msg.state            = STATE_MOVE;
                change_msg.direction        = m_players[0].direction;

                send_network_msg(&change_msg, DPNSEND_NOLOOPBACK);
            }
        }
        
else
        {
            
// change to idle state
            EnterCriticalSection(&m_update_cs);
            m_players[0].last_state = STATE_IDLE;
            LeaveCriticalSection(&m_update_cs);

            
// send update only if player moved last update
            if(last_move)
            {
                last_move = 0;

                sStateChangeMsg change_msg;

                change_msg.header.type      = MSG_STATE_CHANGE;
                change_msg.header.size      = 
sizeof(sStateChangeMsg);
                change_msg.header.player_id = m_players[0].player_id;
                change_msg.state            = STATE_IDLE;
                change_msg.direction        = m_players[0].direction;

                send_network_msg(&change_msg, DPNSEND_NOLOOPBACK);
            }
        }
    }

    update_all_players();
    render_scene();

    move_action = 0;                    
// clear action data for next frame
    update_counter = timeGetTime();     // reset update counter

    
return true;
}

At every frame, the input devices are restored (in case a device’s focus has been
lost), and input is read in. If the user presses Esc, the game-play quits by returning
a value of false from the frame function.

From here, game-play may only continue if the client is connected to the server.
If no such connection exists, a message displays to that effect. Also, if a player is
still waiting for a DirectPlay identification number from the server, a message displays,
and a request is periodically sent to the server for the correct identification
number.

From here on, player input is parsed. A single variable tracks player actions (move_action),
and each bit in the variable represents a specific action (as shown in Figure 19.17). The
user’s actions are move up, move down, move left, move right, and attack. Also, camera
angle changes are recorded (and flagged for later updating).

Normally, players are allowed to move around the world, but if a player is currently
swinging his weapon or being hurt, that player is not allowed to move. You use the
allow_move flag to signify when a player’s actions can be processed, as shown here:

If a player chooses to attack, you need to construct a state-change message and
send that message to the server. After you send the state-change message, clear the
player’s movement actions. Notice that the client does not change its own state at
this point; the server determines when to change the player’s state.

If the player did not attack, his actions are checked to see whether the player is
moving.

After the player’s state and movement direction is set, the Frame function continues
by resetting the camera’s movements (by setting the cam_move flag to false). The
player’s controls are relative to the camera-viewing angle (if the player is pressing
the up arrow key, he is walking away from the camera). If you change the camera’s
angle while the player is walking, you force the player’s direction to change as well.
The client takes this change of the player’s direction into consideration when the
camera is rotated.

Once a player has moved, the client sends a state-change message to the server.
Notice that the state-change message is sent only if the player’s movement is different
from the last move he performed (as recorded in the last_move variable).

If the player hasn’t moved, his state is changed to standing still (STATE_IDLE), and a
state-change message is sent to the server.

At this point, the local player’s actions have been recorded and sent to the server.
Next, all players are updated, the scene is rendered, and the movement actions are
reset for the next frame.

posted on 2007-12-19 17:22 lovedday 阅读(211) 评论(0)  编辑 收藏 引用


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


公告

导航

统计

常用链接

随笔分类(178)

3D游戏编程相关链接

搜索

最新评论