Sign in to follow this  
Finalspace

[solved] Proper jump handling in a platformer prototype in HTML5

Recommended Posts

Hi there,

 

i am creating a platformer prototype in HTML5/Javascript right now and i was making good progress so far. But now after i moved my input and logic handling out from the rendering into the actual update method, my player sometimes can randomly jump much higher.

 

See the following video: http://root.xenorate.com/final/dev/jumpwtf.flv

 

I am using requestion animation frame for letting the browser give me a near stable 1 / 60 of a second update time interval and

of course use a fixed frame rate system, which should only update my game logic once 1/60 of a second - nothing fancy:

    var dt = 1.0 / 60.0;
var lastTime = window.performance.now();
var accumDelta = dt;
    var updateAndRender = function (curTime) {
        var deltaTime = (curTime - lastTime) / 1000;
        lastTime = curTime;

            accumDelta += deltaTime;
            while (accumDelta >= dt) {
                handleInput();
                update();
                accumDelta -= dt;
            }
            render();

        requestAnimationFrame(updateAndRender);
    };

    loadResources();
    canvas.focus();

    requestAnimationFrame(updateAndRender);

And this happens for input and update logic, also nothing fancy...

    var handleInput = function () {
        // Move player
        if (isKeyDown(Keys.Left) || isKeyDown(Keys.A)) {
            player.accel.x -= 45 / dt;
        } else if (isKeyDown(Keys.Right) || isKeyDown(Keys.D)) {
            player.accel.x += 45 / dt;
        }

        // Apply gravity
        player.accel.y += 20 / dt;

        // Let the player jump
        var jumpKey = 0;
        if (isKeyDown(Keys.Up)) {
            jumpKey = Keys.Up;
        } else if (isKeyDown(Keys.W)) {
            jumpKey = Keys.W;
        }
        if (jumpKey > 0 && player.onGround && !player.jumpKeyDown) {
            player.jumpKeyDown = true;
            player.whichJumpKey = jumpKey;
            // Remove applied gravity
            player.accel.y -= (20 / dt);
            // Apply jump
            player.accel.y -= (440 / dt);
        } else {
            if (player.jumpKeyDown && !isKeyDown(player.whichJumpKey)) {
                player.jumpKeyDown = false;
                player.whichJumpKey = 0;
            }
        }

        // Apply drag force
        player.accel.x += -10 * player.vel.x;
    };

    var update = function() {
        // p' = 1/2 at² + vt + p
        // v' = at + v
        var newPlayerDelta = new Vec2();
        newPlayerDelta.x = 0.5 * player.accel.x * dt * dt + player.vel.x * dt;
        newPlayerDelta.y = 0.5 * player.accel.y * dt * dt + player.vel.y * dt;
        var newPlayerPos = new Vec2();
        newPlayerPos.setFrom(player.pos).addTo(newPlayerDelta);
        player.vel.x = player.accel.x * dt + player.vel.x;
        player.vel.y = player.accel.y * dt + player.vel.y;

        // Handle all level and entity collisions
        handleCollisions(newPlayerDelta, newPlayerPos);
    };

What is wrong with that? I dont see any major mistakes here...

 

And and which is much more worse is that my player jumps less higher for normal jumps as well...

 

Thanks,

Final

Edited by Finalspace

Share this post


Link to post
Share on other sites

Where does Player.onground get updated?  

 

Well in the collision code, something like this - executed after all input handling:

var groundCollision = false;

while (test collision with tilemap) {
	if (tNormal.x == 0 && tNormal.y == -1) {
		groundCollision = true;
	}
}
player.onGround = groundCollision;

This means, when there are no collisions on the ground - the player is assumed to be in air.

 

Oh and maybe this can help too - code for getting the input events:

    var keystate = [];
    var Keys = {
        Left: 37,
        Right: 39,
        Up: 38,
        Down: 40,
        Space: 32,
        D: 68,
        A: 65,
        W: 87
    };

    canvas.addEventListener("keydown", function (e) {
        keystate[e.keyCode ? e.keyCode : e.which] = true;
        return false;
    }, false);
    canvas.addEventListener("keyup", function (e) {
        keystate[e.keyCode ? e.keyCode : e.which] = false;
        return false;
    }, false);

    var isKeyDown = function (key) {
        return typeof keystate[key] != "undefined" && keystate[key] === true;
    };

    var setKeyDown = function (key, value) {
        keystate[key] = value;
    };

Edited by Finalspace

Share this post


Link to post
Share on other sites

Well in the collision code, something like this - executed after all input handling:

This is all just guesswork on my part, but is the input ever run more than once before the collision code?  I'm just wondering if maybe the 'jump' is getting triggered multiple times occasionally because OnGround is sometimes still considered false.  That would explain super jumps.  You might try sticking a bunch of debug prints whenever the ground is reached and whenever a jump is executed to make sure they all line up.

 

EDIT:  The other possibility, if it's OnGround, is that you're colliding with something erroneously, and resetting OnGround to true.

 

 

But it's all conjecture, I'd toss the debug prints in there, and see if when a super jump happens, there is a disparity in the log.

Edited by ferrous

Share this post


Link to post
Share on other sites

Aren't you looping the update loop deltaupdate meny times?

Meaning it will be different per request frame?

 

EDIT: Might be that im mistaken.

Edited by Jooseppi

Share this post


Link to post
Share on other sites

The input handling is done in same manner like the update logic, which gets looped (accumuated delta) to ensure 60 updates per second.

Of course when there are short frame drops which happens all the time in request animation frame then it may get handled multi times, but including game logic as well - which is fine.

Frame drops occurs when GC is cleaned up some objects - which gets created in my prototype all the time.

 

I also moved out the input handling to the top, outside the game logic loop and this also produces the same issue :-(

var updateAndRender = function (curTime) {
	handleInput();

	accumDelta += deltaTime;
	while (accumDelta >= dt) {
		accumDelta -= dt;
		update(dt);
	}

	render();
	requestAnimationFrame(updateAndRender);
};

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this