Skip to content

Intermediary Movement

Vinicius Reif Biavatti edited this page Oct 10, 2019 · 4 revisions

Movement Improvement

In the basic tutorial, we created the basic movement logic. That simple logic just considers one key to move the player. In this step we will improve that logic to make the movement function to consider multiple keys to move the player simultaneously. This step is easy and simple to understand.

The logic for this is a little different then the other movement logic we use. Here, we will use two events: keyup and keydown. Both events will control the key activation. When the user press some key (keydown event), this key will be marked as actived and the movement function will move the player. When this same key stops to be pressed (keyup event), this key will be marked as not activated so, the movement function will not move the player. We will work with boolean controlling in this step for the keys.

The first thing to do is add the boolean property to controll the activation of the key in the key attribute. We will made this like the code below:

// Data
let data = {
    // ...
    key: {
        up: {
            code: "KeyW",
            active: false
        },
        down: {
            code: "KeyS",
            active: false
        },
        left: {
            code: "KeyA",
            active: false
        },
        right: {
            code: "KeyD",
            active: false
        }
    }
    // ...
}

After define the attributes, we will change the logic of our keydown event function. This event will just active the pressed key. Note that we will change the if instruction to not consider else.

/**
 * Key down check
 */
document.addEventListener('keydown', (event) => {
    let keyCode = event.code;

    if(keyCode === data.key.up.code) {
        data.key.up.active = true;
    } 
    if(keyCode === data.key.down.code) {
        data.key.down.active = true;
    } 
    if(keyCode === data.key.left.code) {
        data.key.left.active = true;
    } 
    if(keyCode === data.key.right.code) {
        data.key.right.active = true;
    } 
});

Now, we will do the same for the kayup event, but instead of active the key, this step will inactive the released key.

/**
 * Key up check
 */
document.addEventListener('keyup', (event) => {
    let keyCode = event.code;

    if(keyCode === data.key.up.code) {
        data.key.up.active = false;
    } 
    if(keyCode === data.key.down.code) {
        data.key.down.active = false;
    } 
    if(keyCode === data.key.left.code) {
        data.key.left.active = false;
    } 
    if(keyCode === data.key.right.code) {
        data.key.right.active = false;
    } 
});

So now, we have to create the movement logic again. For this, we will create a new function called movePlayer(). This function will check the activated keys and make the player coordinates changes. In this function we will not use the else keyword because the movement needs to be simultaneous.

/**
 * Movement
 */
function movePlayer() {
    if(data.key.up.active) {
        let playerCos = Math.cos(degreeToRadians(data.player.angle)) * data.player.speed.movement;
        let playerSin = Math.sin(degreeToRadians(data.player.angle)) * data.player.speed.movement;
        let newX = data.player.x + playerCos;
        let newY = data.player.y + playerSin;

        // Collision test
        if(data.map[Math.floor(newY)][Math.floor(newX)] == 0) {
            data.player.x = newX;
            data.player.y = newY;
        }
    }
    if(data.key.down.active) {
        let playerCos = Math.cos(degreeToRadians(data.player.angle)) * data.player.speed.movement;
        let playerSin = Math.sin(degreeToRadians(data.player.angle)) * data.player.speed.movement;
        let newX = data.player.x - playerCos;
        let newY = data.player.y - playerSin;

        // Collision test
        if(data.map[Math.floor(newY)][Math.floor(newX)] == 0) {
            data.player.x = newX;
            data.player.y = newY;
        }
    }
    if(data.key.left.active) {
        data.player.angle -= data.player.speed.rotation;
    }
    if(data.key.right.active) {
        data.player.angle += data.player.speed.rotation;
    } 
}

A new correction we will do in this function is validate the player angle interval. The angle in degrees has a fixed interval from 1 to 360. This needs to be checked in the left or right movements. When the angle overflows tha angle interval it needs to be corrected. To keep the value inside the interval we will use the module (%) operator.

// ...
if(data.key.left.active) {
    data.player.angle -= data.player.speed.rotation;
    data.player.angle %= 360;
}
if(data.key.right.active) {
    data.player.angle += data.player.speed.rotation;
    data.player.angle %= 360;
}
// ...

This function will be called in the main loop of our application before the RayCasting render function. This will update the player coordinates for every loop cicle.

/**
 * Main loop
 */
function main() {
    setInterval(function() {
        clearProjection();
        movePlayer();
        rayCasting();
    }, data.render.delay);
}

If we test the application now, we will discover that the player will be moved so fast! It is because for every loop interation we are processing the player movement. It is not just in the event time. To change it, we will just change the player speed.

// Data
let data = {
    // ...
    player: {
        // ...
        speed: {
            movement: 0.05,
            rotation: 3.0
        }
    }
    // ...
}

Now, we can test the application. We will check that we can move the player so better.

Code

This is the full code of this tutorial step:

/**
 * Key down check
 */
document.addEventListener('keydown', (event) => {
    let keyCode = event.code;

    if(keyCode === data.key.up.code) {
        data.key.up.active = true;
    } 
    if(keyCode === data.key.down.code) {
        data.key.down.active = true;
    } 
    if(keyCode === data.key.left.code) {
        data.key.left.active = true;
    } 
    if(keyCode === data.key.right.code) {
        data.key.right.active = true;
    } 
});

/**
 * Key up check
 */
document.addEventListener('keyup', (event) => {
    let keyCode = event.code;

    if(keyCode === data.key.up.code) {
        data.key.up.active = false;
    } 
    if(keyCode === data.key.down.code) {
        data.key.down.active = false;
    } 
    if(keyCode === data.key.left.code) {
        data.key.left.active = false;
    } 
    if(keyCode === data.key.right.code) {
        data.key.right.active = false;
    } 
});

/**
 * Movement
 */
function movePlayer() {
    if(data.key.up.active) {
        let playerCos = Math.cos(degreeToRadians(data.player.angle)) * data.player.speed.movement;
        let playerSin = Math.sin(degreeToRadians(data.player.angle)) * data.player.speed.movement;
        let newX = data.player.x + playerCos;
        let newY = data.player.y + playerSin;

        // Collision test
        if(data.map[Math.floor(newY)][Math.floor(newX)] == 0) {
            data.player.x = newX;
            data.player.y = newY;
        }
    }
    if(data.key.down.active) {
        let playerCos = Math.cos(degreeToRadians(data.player.angle)) * data.player.speed.movement;
        let playerSin = Math.sin(degreeToRadians(data.player.angle)) * data.player.speed.movement;
        let newX = data.player.x - playerCos;
        let newY = data.player.y - playerSin;

        // Collision test
        if(data.map[Math.floor(newY)][Math.floor(newX)] == 0) {
            data.player.x = newX;
            data.player.y = newY;
        }
    }
    if(data.key.left.active) {
        data.player.angle -= data.player.speed.rotation;
        data.player.angle %= 360;
    }
    if(data.key.right.active) {
        data.player.angle += data.player.speed.rotation;
        data.player.angle %= 360;
    } 
}

/**
 * Main loop
 */
function main() {
    setInterval(function() {
        clearProjection();
        movePlayer();
        rayCasting();
    }, data.render.delay);
}

Let's go to the next tutorial!