Blog

First Person Controls and Physics

2015-02-12

It is not a secret that today's first-person game applications are very popular. The reason for this is clear. Such an approach allows a player to better interact with the environment, feel like he or she is a part of the current events and it is often simply more comfortable from the viewpoint of the gameplay. Today we look at how to create a base frame for such an application with the help of the Blend4Web engine.

Location

First, we'll create a basis the whole location inside which the character will move. Let's add an object with some irregularities. It will be a terrain and a physical "foundation" for our whole scene. Also, let's ask an artist to create a small, pretty house for us, with which we'll look at how to manage static physics.

Physics

In Blend4Web, there are two types of physical object behavior: static and dynamic. In the first case, it is assumed that an object will not change its state as time passes. In the second group there are objects which can be translated by means of physics: all the characters, different pickable objects, crates, barrels etc.

Moreover, there are two possible ways to build the physical bounding volume of an object:

  • using its real geometry;
  • with the help of physical shapes (sphere, cube, capsule etc).

Static Objects

The terrain will be a static object with its geometry generating physical volume. In order to do this we choose Physics Type: Static in the objects physics properties and it is necessary to turn on the Special: Collision flag in material properties. Now dynamic objects will be able to collide with the terrain and the character will be able to walk on it.

We could do the same with the house but in our case we'll resort to a little optimization. To simplify physics geometry we'll create a new object with a significantly reduced vertices count. We must turn on the Special: Collision flag in the new object's material settings as well as forbid its rendering with the Do not Render flag. It is shown in green on the image below.

Such a technique is widely used in scenes where serious physical calculations are expected. In games, this allows to avoid unnecessary calculations and significantly increase FPS in the physics engine.

Dynamic Objects

Let's place some object which a character will be able to interact with in the scene. We'll use a small bucket:

As we can see it is a dynamic object of a Rigid Body type. For physics to be able to work with such objects it is necessary to turn on the Detect Collisions flag. The cylinder is used here as a bounding shape. This is a much more optimized option and it should be used whenever possible.

Character

Let's create an object for the future character and append dynamics physics with the Capsule shape to it. In the Blend4Web bar, we'll turn on the Detect Collisions and Character flags. Character properties can be left alone.

The last thing to pay attention to during the scene setup is the camera type. In our case the best option is to use an EYE type, and turn on the Use Vertical Rotation Clamping flag to block camera in extreme vertical positions.

Controls

It's time to bring some life to our scene. Let's use a base application from the developers documentation. The next code will be placed in the loading callback:

function load_cb(data_id) {
    // make camera follow the character
    var camobj = m_scs.get_active_camera();
    var character = m_scs.get_first_character();
    m_cons.append_stiff_trans(camobj, character, [0, 0.7, 0]);

    // enable rotation with mouse
    var canvas_elem = m_main.get_canvas_elem();
    canvas_elem.addEventListener("mouseup", function(e) {
        m_mouse.request_pointerlock(canvas_elem);
    }, false);

    setup_movement()
}

First of all, the camera is attached to the character with a translation offset of 0.7 units in the vertical direction. Second, there is an attempt to lock the mouse cursor when the canvas is clicked. The mouse.js addon will automatically ensure that the character turns as the mouse moves. Now let's write the control logic in the setup_movement function. First, let's define all the variables to be used later:

var key_a = m_ctl.create_keyboard_sensor(m_ctl.KEY_A);
var key_s = m_ctl.create_keyboard_sensor(m_ctl.KEY_S);
var key_d = m_ctl.create_keyboard_sensor(m_ctl.KEY_D);
var key_w = m_ctl.create_keyboard_sensor(m_ctl.KEY_W);
var key_space = m_ctl.create_keyboard_sensor(m_ctl.KEY_SPACE);
var key_shift = m_ctl.create_keyboard_sensor(m_ctl.KEY_SHIFT);

var move_state = {
    left_right: 0,
    forw_back: 0
}

var move_array = [key_w, key_s, key_a, key_d, key_shift];
var character = m_scs.get_first_character();

The first 6 lines just create sensors for control keys. Apart from the typical WASD controls, there is a speedup using SHIFT key and a jump using space bar. The move_state object is responsible for the character's speed in forward and side directions. The move_array is an array of control sensors. We use the first character found in the scene as a controlled object and save it into the character variable. Next, we define the main callback for the movement controls - move_cb.

var move_cb = function(obj, id, pulse) {
    if (pulse == 1) {
        switch (id) {
        case "FORWARD":
            move_state.forw_back = 1;
            break;
        case "BACKWARD":
            move_state.forw_back = -1;
            break;
        case "LEFT":
            move_state.left_right = 1;
            break;
        case "RIGHT":
            move_state.left_right = -1;
            break;
        case "RUNNING":
            m_phy.set_character_move_type(obj, m_phy.CM_RUN);
            break;
        }
    } else {
        switch (id) {
        case "FORWARD":
        case "BACKWARD":
            move_state.forw_back = 0;
            break;
        case "LEFT":
        case "RIGHT":
            move_state.left_right = 0;
            break;
        case "RUNNING":
            m_phy.set_character_move_type(obj, m_phy.CM_WALK);
            break;
        }
    }

    m_phy.set_character_move_dir(obj, move_state.forw_back,
                                      move_state.left_right);
};

This callback will be called when any of the following keys are pushed or released: W, A, S, D, Shift. In the first case (push) the pulse variable will equal 1, in the second case (release) it will be -1. The move_state stores information about currently pressed keys. So for example its forw_back field will be equal to:

  • 1, if W is pressed,
  • -1, if S is pressed,
  • 0, if none of these keys is pressed.

The next sensor manifolds are responsible for a logic of move_cb function calls:

m_ctl.create_sensor_manifold(character, "FORWARD", m_ctl.CT_TRIGGER,
    move_array, function(s) {return s[0]}, move_cb);
m_ctl.create_sensor_manifold(character, "BACKWARD", m_ctl.CT_TRIGGER,
     move_array, function(s) {return s[1]}, move_cb);
m_ctl.create_sensor_manifold(character, "LEFT", m_ctl.CT_TRIGGER,
    move_array, function(s) {return s[2]}, move_cb);
m_ctl.create_sensor_manifold(character, "RIGHT", m_ctl.CT_TRIGGER,
    move_array, function(s) {return s[3]}, move_cb);

var running_logic = function(s) {
   return (s[0] || s[1] || s[2] || s[3]) && s[4];
}
m_ctl.create_sensor_manifold(character, "RUNNING", m_ctl.CT_TRIGGER,
    move_array, running_logic, move_cb);

All sensor manifolds have a CT_TRIGGER type. This means they will fire whenever a logic function value changes. The character's accelerated movement logic includes all 5 sensors. Thus, a speed increase or decrease happens when the value of one of the movement sensors or Shift sensor changes.

The last action is the character's jump:

var jump_cb = function(obj, id, pulse) {
    m_phy.character_jump(obj);
}
m_ctl.create_sensor_manifold(character, "JUMP", m_ctl.CT_SHOT,
        [key_space], null, jump_cb);

Everything here is similar to the character's movement, but the sensor manifold has changed its type to CT_SHOT. Therefore, the callback is being fired only when the space bar is pressed but not when it is released.

As a result of these simple operations, we have a nice start for a full functional game in first person view. You can go further and add abilities to pick up some objects, to carry something or let the character interact with the game objects by pressing some keys.

Link to the standalone application.

All the sources will be available it the next free Blend4Web SDK.

Changelog

[2015-02-12] Initial release.

[2015-02-16] Camera type changed to EYE.

Comments
07 aug. 2015 21:24
ASDW is not working but my mouse is working . How to solve this ? please help ….
10 aug. 2015 10:25
Reply to post of user aldine_jade17
where should I put the code? I don't get it Am I going to open a notepad and place the code there?
Cool, I believe you can do that, also can try with chrome firefox app designer, or try intel xdk. You can debug there the awsd problem and have more fun
10 aug. 2015 14:22
Reply to post of user aldine_jade17
ASDW is not working but my mouse is working . How to solve this ? please help ….
Hello. This is a pretty strange behavior. This example doesn't work even if you run it from our website? Could you please make a screenshot of your web console? (Press F12 while in browser)
19 aug. 2015 01:45
Is there a way to use the mouse for look control but keep pointer functionality for Selectable objects?
19 aug. 2015 10:08

Is there a way to use the mouse for look control but keep pointer functionality for Selectable objects?
Something like in the Farm demo where you can select barrels and other stuff by looking at them and pressing F?
19 aug. 2015 22:41
Ya, something like that… So long as pressing F will open hotlinks, of course. And assuming there is still some kind of pointer to indicate what you are going to click on.
08 jan. 2016 15:37
Is posible blend4web can tell that does not hide the mouse when you are moving the camera?
I want to click on objects (in canvas caption) without having to give the esc key. Thank you.
08 jan. 2016 21:19
Reply to post of user pakirrote
Is posible blend4web can tell that does not hide the mouse when you are moving the camera?
I want to click on objects (in canvas caption) without having to give the esc key. Thank you.
Hello. If I understood you correctly, you don't want the pop-up menu like "www.blend4web.com has disabled your mouse cursor" to appear. When an application uses pointerlock we will always see this pop-up. This is not a part of b4w engine. This is a part of browsers. So it can't be hidden.
12 jan. 2016 13:53
How center mouse in the center of window when are captioned? thx.
13 jan. 2016 12:45
Hi,
How center mouse in the center of window when are captioned? thx.
Could you please provide some details on what you're trying to achieve? Maybe some links or videos? Thanks.
Please register or log in to leave a reply.