Legacy GM Strange Pixel Issue

I am having trouble with my 2D platformer.
There seems to be some problem with my collision.
About 40% of the time my player object lands, it appears that he is half a pixel in the air, instead of being at the same level as the ground.
When the player moves horizontally, he has a chance of, when touching the sides, stopping right before the wall, and stopping while inside the wall. I made sure to have my movement speeds set to be an even number, just to make sure its not a problem with it not colliding correctly.
I have all my sprites centered the same, so i am not exactly sure what is going on.

The way I have my player object, is so that I have a different sprite for the different movements. for example: walking, jumping, idle. I also have a collision mask for the player, and all the different movement sprites are based on that sprite size.
So, in my mind, there shouldn't be any problem. but I recently came back to Gamemaker after taking a break for a year or so. so I am not sure if I am just being really dumb.
Here is my code:

Create
Code:
move = 0;
movespeed = 4;
jumpspeed = -2;
grav = 0.125;
facing = 1;
Step:
Code:
image_xscale = facing;
left = keyboard_check(vk_left);
right = keyboard_check(vk_right);
up = keyboard_check_pressed(vk_up);

if left || right && !place_free(x, y + 1) { sprite_index = spr_player_walk; image_speed = 0.25};
if move == 0 && gravity == 0 { sprite_index = spr_player_idle; image_speed = 0.025};

move = -(left - right);
x += (move * movespeed) * place_free(x + move * movespeed, y);

if up { vspeed = jumpspeed }

if place_free(x, y + 1)
{
    sprite_index = spr_player_jump
    gravity = grav;
} else {
    gravity = 0;
}

if move != 0 then facing = move;
Collision with collision parent:
Code:
if hspeed != 0 then hspeed = 0;
if vspeed != 0 then vspeed = 0;
 
So your gravity is 0.125, which means that your vspeed will not necessarily be a whole number while you are falling. For examples sake, let's say you are falling at a vspeed of 1.5 and you are 2 pixels above the ground. The place_free check is done for 1 pixel below you, and it is free, so you move 1.5 pixels down. You are now 0.5 pixels above the ground. The place_free check is done for 1 pixel below you and it's not free, so your gravity is set to 0 and you won't fall anymore. However, you are still 0.5 pixels above the ground, so, with the internal rounding needed to draw pixels, you'll actually be drawn 1 pixel above the ground. This is the logic of -why-, I'll leave it up to you to try to come up with a -how- to fix it for now.
 

TheouAegis

Member
x += (move * movespeed) * place_free(x + move * movespeed, y);
Likewise, if you're 6 pixels away from a wall and walk right toward the wall, you step forward 4 pixels. Now you're 2 pixels away from the wall. There will be a collision if you try to move 4 spaces forward, so you don't move at all. So you effectively stopped 2 pixels away from the wall.

Glad you're trying to at least code on your own first. lol

(Also glad you're using powers of 2 for your fractions.)
 
So your gravity is 0.125, which means that your vspeed will not necessarily be a whole number while you are falling. For examples sake, let's say you are falling at a vspeed of 1.5 and you are 2 pixels above the ground. The place_free check is done for 1 pixel below you, and it is free, so you move 1.5 pixels down. You are now 0.5 pixels above the ground. The place_free check is done for 1 pixel below you and it's not free, so your gravity is set to 0 and you won't fall anymore. However, you are still 0.5 pixels above the ground, so, with the internal rounding needed to draw pixels, you'll actually be drawn 1 pixel above the ground. This is the logic of -why-, I'll leave it up to you to try to come up with a -how- to fix it for now.
The way I've done it in the past, is using a whole number, for the gravity. but since I'm dealing with such small sprites (the player's sprite is 8x8), using a whole number for gravity is too much. is there any way I could fix this without using whole numbers for my gravity?
by the way, thanks for the reply!
 
Likewise, if you're 6 pixels away from a wall and walk right toward the wall, you step forward 4 pixels. Now you're 2 pixels away from the wall. There will be a collision if you try to move 4 spaces forward, so you don't move at all. So you effectively stopped 2 pixels away from the wall.

Glad you're trying to at least code on your own first. lol

(Also glad you're using powers of 2 for your fractions.)
thank you so much! I can't believe I didn't catch that.
now my problem is with the gravity. as RefresherTowel said, I can't have 0.125 as my gravity speed, and it makes sense. I tried setting it to 0.25, and 0.5, but those numbers are still confusing the place_meeting function. Is there any other way I could detect the collision, that isn't using place_meeting?
 

TheouAegis

Member
You can use whatever gravity you want. You need to code your collisions so when they occur, the player snaps to the object he is colliding with.
 
I

IzzoriousAxel

Guest
Code:
if(place_meeting(x + xVelocity, y, obj_solid))
{
    while(!place_meeting(x + xVelocity, y, obj_solid))
    {
        x += sign(xVelocity);
    }
    xVelocity = 0;
}
and repeat the same thing for yVelocity. Sign gives you -1, 1, or 0, making you move 1 pixel at a time in the while loop, until you're flush with the wall you detected a collision with earlier.

Edit: I feel like I should mention, there's another method called sweeping where you find intersections between lines, rectangles, capsules, and circles, using similar math to raycasting. While sweeping is much faster, it's also fairly complicated and involves a lot of not particularly simple math. Its use case is if you need perfect collision detection for objects that can travel at extremely high speeds that could possibly collide with very thin objects, or when your tiles aren't aligned to a grid.
 
Last edited by a moderator:
Code:
if(place_meeting(x + xVelocity, y, obj_solid))
{
    while(!place_meeting(x + xVelocity, y, obj_solid))
    {
        x += sign(xVelocity);
    }
    xVelocity = 0;
}
and repeat the same thing for yVelocity. Sign gives you -1, 1, or 0, making you move 1 pixel at a time in the while loop, until you're flush with the wall you detected a collision with earlier.

Edit: I feel like I should mention, there's another method called sweeping where you find intersections between lines, rectangles, capsules, and circles, using similar math to raycasting. While sweeping is much faster, it's also fairly complicated and involves a lot of not particularly simple math. Its use case is if you need perfect collision detection for objects that can travel at extremely high speeds that could possibly collide with very thin objects, or when your tiles aren't aligned to a grid.
Thank you, Izzorious Axel.
I think I figured out my problem.
I completely removed my collision event. Now I just check for collision in my step event:
Code:
while place_meeting(x,y+vsp,obj_collision){
vsp-=sign(vsp)
}
while place_meeting(x+hsp,y+vsp,obj_collision){
hsp-=sign(hsp)
vsp-=sign(vsp)
}
Thank you!
 
Top