Latency between pressing button and action taking place?

I'm working on a platformer. I've got an issue where, when the player is running towards the edge of a surface, if I try to jump right off the edge, sometimes the player doesn't jump and instead just runs off the edge. I now realize that the issue isn't the jumping itself, but that there *seems* to a latency between the button press and the game registering the button press. I checked this by having the game create an object with the player sprite whenever I press the jump button. So, often when I'm running along a platform and jump near the end, the game only registers the button press after I've fallen off the platform.

Is this my imagination? Am I really just not pressing the jump button as soon as I think I am? Or is there some kind of latency issue that I'm not accounting for? I'm still a super beginner, and this is pretty much just Shaun Spaulding's platformer tutorial code. Just want to figure out if I'm crazy or if there's a way to work around this, or what.

EDIT: Sorry, of course I should have included code!
GML:
//oPlayer Step Event

key_left = keyboard_check(vk_left);
key_right = keyboard_check(vk_right);
key_jump = keyboard_check_pressed(vk_space);
key_jump_held = keyboard_check(vk_space);


var move = key_right - key_left;
hsp = move * walksp;
vsp += grv;


if place_meeting(x,y+1,oWall) && key_jump
{
    vsp = -jumpspeed;
}
//Creates an object to mark when I press the jump button
if key_jump instance_create_layer(x,y,"Player",oPlayerMarker);

if (vsp < 0) && (!key_jump_held) && !bounced
{
     vsp = max(vsp,-jumpspeed/4)
}



//horizontal collision
if (place_meeting(x+hsp,y,oWall))
{
    while !(place_meeting(x+sign(hsp),y,oWall))
    {
        x += sign(hsp);       
    }
    hsp = 0;
}
x += hsp;

//vertical collision
if (place_meeting(x,y+vsp,oWall))
{
    while !(place_meeting(x,y+sign(vsp),oWall))
    {
        y += sign(vsp);
    }
    vsp = 0;
    bounced = false;   
}

if move > 0
{
movingright = true;
movingleft = false;
}

if move < 0
{
movingleft = true;
movingright = false;
}


if keyboard_check_pressed(ord("V")) && movingright == true instance_create_layer(x+30,y,"Player",oAttackRight);
if keyboard_check_pressed(ord("V")) && movingleft == true instance_create_layer(x-30,y,"Player",oAttackLeft);


y += vsp;
 
Last edited:

ophelius

Member
GM's latency is very low for key presses, should be instant. The only problems I can think of is your monitor latency(how is it for other games?), or the sequence of events(Make sure the keypresses are checked before the actions are processed, otherwise it might be a frame too late).
Showing some code always helps
 
GM's latency is very low for key presses, should be instant. The only problems I can think of is your monitor latency(how is it for other games?), or the sequence of events(Make sure the keypresses are checked before the actions are processed, otherwise it might be a frame too late).
Showing some code always helps
Whoops! Showing code would help, wouldn't it? I edited the post to include my player object's step event.

I don't think monitor latency would be the issue; I haven't noticed anything like this for other games. It's an ASUS VH236H, for what it's worth.
 

ophelius

Member
This code you wrote shouldn't be right after the keyboard checks:
Code:
if place_meeting(x,y+1,oWall) && key_jump
{
    vsp = -jumpspeed;
}
Allow your player to fall first(add grv to vsp), then check for ground collision which will readjust the vps to 0 and reposition you above the block, then check for jump conditions only after this.
You don't have to check for blocks below again, if you've collided and are standing on the ground, your vpd will be at 0, so just check:
Code:
if(vps == 0 && key_jump) vsp = -jumpspeed;
Maybe after readjusting this it'll work better
 
Okay, I think I did what you suggested; but the result is still the same. Here's what I changed it to:

GML:
key_left = keyboard_check(vk_left);
key_right = keyboard_check(vk_right);
key_jump = keyboard_check_pressed(vk_space);
key_jump_held = keyboard_check(vk_space);
key_attack = keyboard_check_pressed(ord("V"))

var move = key_right - key_left;
hsp = move * walksp;
vsp += grv;

//horizontal collision
if (place_meeting(x+hsp,y,oWall))
{
    while !(place_meeting(x+sign(hsp),y,oWall))
    {
        x += sign(hsp);       
    }
    hsp = 0;
}
x += hsp;

//vertical collision
if (place_meeting(x,y+vsp,oWall))
{
    while !(place_meeting(x,y+sign(vsp),oWall))
    {
        y += sign(vsp);
    }
    vsp = 0;
    bounced = false;   
}

if vsp == 0 && key_jump
{
    vsp = -jumpspeed;
}
//Creates an object to mark when I press the jump button
if key_jump instance_create_layer(x,y,"Player",oPlayerMarker);

if (vsp < 0) && (!key_jump_held) && !bounced
{
     vsp = max(vsp,-jumpspeed/4)
}

if move > 0
{
movingright = true;
movingleft = false;
}

if move < 0
{
movingleft = true;
movingright = false;
}


if key_attack && movingright == true instance_create_layer(x+30,y,"Player",oAttackRight);
if key_attack && movingleft == true instance_create_layer(x-30,y,"Player",oAttackLeft);


y += vsp;
 

ophelius

Member
Yes, that's better. Not sure what else to suggest, it's pretty standard code you have there, it's hard to figure out without seeing the effect you're describing

This is an excellent article on the blog to combat this very issue:
https://www.yoyogames.com/blog/544/flynn-advanced-jump-mechanics
Wow, that is a great tutorial.

Though adding jump buffering is a feature that shouldn't be added to fix this issue, it's more for assisting players for a better jumping experience. I would experiment with adding this later when this latency issue is resolved.

Maybe post a picture of your player collision mask, could be an issue there with its size
 
The collision mask shouldn't be the issue. At the moment my player sprite is literally just a green rectangle with the collision mask covering the whole thing. This isn't a project I'm planning to ever release or anything, I just wanted to try making a basic platforming game without just following a tutorial step by step, so I'm just using extremely basic, blocky placeholder sprites.

Maybe the coyote time mentioned in the article is actually what's missing? Perhaps the only latency is from my brain to my hands, making me believe I've pressed space sooner than I have?
 
Maybe the coyote time mentioned in the article is actually what's missing? Perhaps the only latency is from my brain to my hands, making me believe I've pressed space sooner than I have?
generally jump buffer is for when you run off a ledge and jump a frame or two too late. or when you press jump right before you hit the ground and the game lets you jump anyway, though you havent actually pressed the button on the ground.

Are you experience the latency when approaching a ledge or just standing in the smack middle of a platform?

EDIT: rereading the OP it sounds like only on the edge - if this is the case it sounds like you're used to games including the jump buffer feature and now that its absent it's tripping you up.

add it in and see how it feels!
 

ophelius

Member
Maybe since it's just a simple project, attach it here so we can mess around with it

rereading the OP it sounds like only on the edge - if this is the case it sounds like you're used to games including the jump buffer feature and now that its absent it's tripping you up.
add it in and see how it feels!
My platformer jumping is instant, right up to the edge, no buffering needed. I don't think that's what you need to worry about right now
 
Last edited:
My platformer jumping is instant, right up to the edge, no buffering needed. I don't think that's what you need to worry about right now
well my point is it may feel instant to you, with your reaction speed and experience
but to someone else, who also may or may not be accustomed to having a buffer, it may feel sticky or laggish.
 

ophelius

Member
Ah ok, now I know what you mean, there's definitely jump delay issues on the edges, even with the block being halfway over the edge only. I'll mess around with it later after I eat supper
 
Glad to know I'm not nuts! It's odd, because in my project where I've followed Shaun Spaulding's "Complete Platformer" tutorial to the letter, using all his assets and everything, I have the same issue; so it seems strange that he would have this kind of thing in his code, or that it's not something that I can really find anything about with a quick Google search.
 

ophelius

Member
Ok, I've played around with it, studied it, and came up with some conclusions:

-There's actually nothing wrong going on here, the problem is it's all based on the width and speed of your sprite. He's moving so fast (4px/frame) that it just looks like he's still on the edge when you're attempting to jump.

-Checking the vsp first gave a slight improvement because if you check horizontally first and you're right at the edge, you're moving him off the edge first, then checking vertically where the block might not be under anymore.
So check vsp first:
Code:
//vertical collision
if (place_meeting(x,y+vsp,oWall))
{
    while !(place_meeting(x,y+sign(vsp),oWall))
    {
        y += sign(vsp);
    }
    vsp = 0;
    bounced = false;
}


if vsp == 0 && key_jump
{
    vsp = -jumpspeed;
}

//Creates an object to mark when I press the jump button
//if key_jump instance_create_layer(x,y,"Player",oPlayerMarker);

if (vsp < 0) && (!key_jump_held) && !bounced
{
     vsp = max(vsp,-jumpspeed/4)
}

y += vsp;


//horizontal collision
if (place_meeting(x+hsp,y,oWall))
{
    while !(place_meeting(x+sign(hsp),y,oWall))
    {
        x += sign(hsp);
    }
    hsp = 0;
}
x += hsp;
-Making the block walk a bit slower, like 3 improved it a bit

-I even slowed down the game to 2 frames per second(game_set_speed(2, gamespeed_fps)) and zoomed right in by changing the camera to 320x240 just so I can really observe closely, and you can make it right to the edge and still jump (after switching the vsp and hsp like above first, without the switch it wasn't as exact)

-So because you're using blocks, this illusion is more apparent. Everything is blocky so you feel like you can wait until the very edge to jump. If you were using sprites where the sprite was running, and maybe his head is sticking out a bit above his body, you could narrow down the collision mask to be as thin as his feet are apart, and while playing, someone would jump sooner because the player's head is a bit further so your reaction time would be sooner too. My tip, get real sprites, zoom in a little more so you don't need such fast movement, and experiment with different speed values for movements and jumping.

-Tip: Add a Draw event in your player object and round the x and y there, because I noticed when zoomed in it's not pixel perfect, so rounding in the Draw routine doesn't change the x/y variables, but only alligns the drawing better.
So in Draw event: draw_sprite(sprite_index, image_index, floor(x), floor(y));

-Other tip: Objects use up cpu time. Don't use a block object for each ground/wall tile. That'll be very taxing for large levels. Instead use tile layers to draw out your tiles from a tilesheet, which get rendered really fast, then create an invisible wall object that you can stretch across any lengths/height in the map editor only on the floors/walls you will be colliding with. This invisible wall object is what you check for collision. Make this object some semi-transparent colors just so you can see them on the room editor but still see the tiles behind it. Make sure to turn off the visible checkmark in the object properties. In the end, the cpu is only checking a fraction of walls with this method.
(Edit: of course, if your wall tile is a solid colored block, no need for tiles or invisible collision objects, just stretch that wall object and it'll look the same. All this is if you actually have graphics)

Other than that, if this is still unsatisfactory, look into that tutorial mentioned above, adding jump buffering would solve this issue for sure.

Anyways, don't feel discouraged, getting proper jumping/collision to work well is not as easy as those beginner video tutorials make it out to be, there's sometimes way more that needs to be done to perfect it. Keep studying and experimenting.
Good luck!

Edit: Just thought of an idea relating to the invisible collision blocks method I mentioned above. If you have an invisible really thin 2-3 pixel wide helper collision block that is used only for checking vertical collision, you just place it right next to the floor and the edge, extending the floor by a bit. It works like a normal collision block, but if your horizontal speed is 0, then don't check so you can fall through again if you're past the edge but on the helper block still. So as you're running it would allow you to jump 2-3 pixels past the edge. Just tweak the pixel width of the helper blocks to your liking.
 
Last edited:
Thanks so much for that analysis! I'll mess with implementing those tips tomorrow.

Could you explain a little more what you mean regarding the Draw event? I'm unclear on what adding a Draw event will change. In the line you wrote, what are floor(x) and floor(y) referring to?

I'm not feeling terribly discouraged at the moment; this is pretty much the reason I started this little project, to just mess around with Game Maker, get to grips with things by experience rather than just copying what a video says to do. Learn the ins and outs of the program without getting frustrated that a passion project isn't coming together as fast as I want, that sort of thing.
 

ophelius

Member
Each object draws itself, unless you add a Draw event from the object's properties box so you can manage the drawing yourself. The reason is that your x and y values might have decimals (x = 3.25, etc). So if you add a draw event using the draw_sprite routine where you can pass the x and y values through the floor functions (which rounds a number down to the next integer), that way you're always drawing at exact integer positions. It's only important if you use low resolutions, you can't notice it otherwise.
 
Last edited:

NightFrost

Member
through the floor functions
Personally I feel rounding makes more sense. However when doing this (floor, round, ceil, whatever you choose) it may have several unintended consequences if you are zooming your view, that is if your application surface is larger than your view. First, if your camera follows player and you round only player position but not camera position, it offsets player relative to camera which makes them appear to shake violently when they move. When camera location is updated it should round coordinates in same manner as player. Second, I've observed this draw rounding appears to happen before view is zoomed to app surface, which causes the whole thing become pixel-aligned, including the view. There will be no "subpixel" movement. This might be an effect you are looking for, or something you wanted to avoid. I avoid it because I feel it makes the view's scrolling appear more jumpy at larger zooms when simulating lower resolutions. So, I don't round coordinates at all.
 
Probably should have finished Spalding's whole tutorial before asking this! If anyone else ends up having this same problem and runs across this thread in the future, part 18 of his Complete Platformer tutorial does actually cover jump buffering:
 
Top