GMS 2 [SOLVED] Tile Collision system snaps x to a grid when jump button is pressed

Hi there!

recently I picked up an old project (a remake of the old platformer game called Crystal Caves) and started changing over all object based collision to a tile based collision system.
my starting point was Shaun Spaldings tutorial and the YoYo games platformer tutorial.

the problem:
all collision is working fine! except one little thing when I started adding one-way platforms
suddenly whenever I walk and press the jump button the character gets snapped to the nearest 'grid' of 32 on the X axis before jumping up.
this did not happen before implementing
and to my knowledge nothing has changed in the code except improving when the code is executed plus making it work for one way platforms.
so my guess is that when I jump it somehow triggers a horizontal collision due to some behind the hood bounding box magic

so in short: perfectly working jump > adding one-way platform (which works) > jump snaps to nearest X of 32 and then jumps.

the code I run now:

Code:
/// @description Processing player

var c1, c2; //collision variables
var map = objGame.map; //tilemap for tile collision
//sort out input
key_right = keyboard_check(vk_right);
key_left = keyboard_check(vk_left);
key_jump = keyboard_check_pressed(vk_space);

//set speed and gravity
hsp = (key_right - key_left) * spd; //horzontal speed
vsp = grav; // vertical speed
grav += gravdelta; //change in gravity (acceleration towards ground)
if( grav>=gravmax ) {grav=gravmax;} //cap falling speed
var rvsp = vsp; //temporary variable
if rvsp < 0 {rvsp = rvsp + (rvsp < 0 ? -1 : 0) >> 0;} else {rvsp = rvsp + (rvsp < 0 ? 0 : 1) >> 0;} //floor and ceil vsp values bitwise to prevent bouncing effects due to rounding as bbox are rounded?

//jumping
if key_jump && jump==false { //on jump gravity is set to -6 (which effectively moves the player up instead of down)
    if can_jump == true {
    grav=grav_jump;        // make the player jump
    jump=true;            // flag that we are jumping
    can_jump = false;
    }
}

// vertical collision
if (grav < 0) {// if we are moving up check above the player
    c1 = tilemap_get_at_pixel(map,bbox_right,bbox_top+rvsp); //bounding box check top right returns index of tile it finds at the position 0 for nothing 1 2 or 3 for one of the tiles
    c2 = tilemap_get_at_pixel(map,bbox_left,bbox_top+rvsp); //bounding box check top left
    if ((c1 == 1) || (c2 ==1)) { //only collide when a wall tile (red) is above otherwise do nothing (jump through platform)
        y = y - (bbox_top&31); //move out of the wall tile
        grav = 0; //set gravity to 0 (stop moving up)
        vsp = 0; //stop moving vertically for the rest of the frame
    }
}
else { //check below the player
    c1 = tilemap_get_at_pixel(map,bbox_right,bbox_bottom+rvsp); //bounding box check bottom right
    c2 = tilemap_get_at_pixel(map,bbox_left,bbox_bottom+rvsp); //bounding box check bottom left
    if ((c1 != 0) || (c2 != 0)) {
        y = y - (bbox_bottom&31) + 31; // move out of the collision
        jump = false; //player is on the ground again
        can_jump = true; //so the player is allowed to jump again
        vsp = 0;   
    }
}

y += vsp; // move player vertically if no collision was detected in this frame

c1 = -1; //reset collision positions for next collision check
c2 = -1;

//horizontal collision
if (hsp > 0) { //move right
    dir = 1; //directon facing
    sprite_index = sprCharWalk;
    c1 = tilemap_get_at_pixel(map, bbox_right + hsp, bbox_top); //boudning box check top right
    c2 = tilemap_get_at_pixel(map, bbox_right + hsp, bbox_bottom); //bounding box check bottom right
    if ((c1 == 1) || (c2 == 1)) { //only when colliding with a wall tile (red) we stop (other are walk-through from the sides and bottom)
        x = x- (bbox_right&31) + 31; // move out of the collision
        hsp = 0;   
    }
} else if (hsp < 0) { //move left
    dir = -1;
    sprite_index = sprCharWalk;
    c1 = tilemap_get_at_pixel(map, bbox_left + hsp, bbox_top);
    c2 = tilemap_get_at_pixel(map, bbox_left + hsp, bbox_bottom);
    if ((c1 == 1) || (c2 == 1)) {
        x = x- (bbox_left&31); // move out of the collision
        hsp = 0;   
    }
} else {
    sprite_index = sprCharIdle;
}

x += hsp; // move player horizontally



//set jump animation and direction of looking
image_xscale = dir;
if (vsp != 0 || jump == true) {sprite_index = sprCharJump; can_jump = false;}
image_speed = 1;
the code I ran BEFORE adding the one-way platform system

Code:
/// @description Processing player
//scrProcessPlayer()
var bbox_side;
var map = objGame.map
//sort out input
key_right = keyboard_check(vk_right);
key_left = keyboard_check(vk_left);
key_jump = keyboard_check(vk_space);

//set speed and gravity
hsp = (key_right - key_left) * spd;
vsp = grav;
grav += gravdelta;
if( grav>=gravmax ) grav=gravmax;

if key_jump && jump==false {
    if can_jump == true {
    grav=grav_jump;        // make the player jump
    jump=true;            // flag that we are jumping
    }
}

//horizontal collision
if (hsp > 0) {bbox_side = bbox_right;} else {bbox_side = bbox_left;}
if (tilemap_get_at_pixel(map, bbox_side + hsp, bbox_top) == 1) || (tilemap_get_at_pixel(map, bbox_side + hsp, bbox_bottom) == 1) //only collide horizontally with wall collision tile (red)
{
    if (hsp > 0) {x = x- (bbox_right&31) + 31; }
    else {x = x- (bbox_left&31); }
    hsp = 0;
}

x += hsp; // move player horizontally

//vertical collision
if (vsp > 0) {bbox_side = bbox_bottom;} else {bbox_side = bbox_top;}
var rvsp = vsp;
if rvsp < 0 {rvsp = rvsp + (rvsp < 0 ? -1 : 0) >> 0;} else {rvsp = rvsp + (rvsp < 0 ? 0 : 1) >> 0;} //floor and ceil vsp values bitwise to prevent bouncing effects due to rounding
if (tilemap_get_at_pixel(map, bbox_right, bbox_side + rvsp) != 0) || (tilemap_get_at_pixel(map, bbox_left, bbox_side + rvsp) != 0)
{
    if (vsp > 0) {y = y - (bbox_bottom&31) + 31 ; jump = false; can_jump = true;}
    else {y = y - (bbox_top&31); grav = 0;}
    vsp = 0;   
}




y += vsp; // move player vertically

//set correct animation
if (hsp > 0) {dir = 1;} else if (hsp < 0) {dir = -1;}
if (hsp == 0) {sprite_index = sprCharIdle;} else {sprite_index = sprCharWalk;}
image_xscale = dir;
if (vsp != 0 || jump == true) {sprite_index = sprCharJump; can_jump = false;}
image_speed = 1;
additional info: the tileset that is being checked consists of the 4 tiles. the mandatory transparant at 0,0
a red square at 1,0 (used for solid walls)
a yellow square at 0,1 (used for one-way platforms)
a blue square at 1,1 (not in use at the moment, so will collide when a specific tile is not specified. will be used for enemy AI tile to guide movement)

things I have tried already:
relocating x+= hsp and y+= vsp to different locations (before inbetween or after the collision check)
relocating the horizontal collision before the vertical collision
reset the c1 and c2 variables inbetween horizontal and vertical collision to a value of -1



am I just missing something tiny here or is there something weird going on?
thanks in advance,
Sam
 
other things I have tried so far:

use an AND NOT instead of AND on the bitwise calculation of x and y (this made for some pretty glitchy stuff)
added and &tile_index_mask behind all tilemap_get_at_pixel (GM Wolf does this in his tutorial)

also did a run using a gamespeed of 10 to see what is really happening.
upon jump and run it first sets the x to the nearest 'middle of a tile' (origin sprite is bottom centre so could be due to this) with a y slightly inside the tile (about 4-6 pixels)
then it jumps up as intended.
 

TheouAegis

Member
Are you sure you didn't mess up the sprite origins? Make sure the idle sprite has the origin directly below inline with the crotch, make sure the running sprite has the origin directly below inline with the crotch, and make sure the jumping sprite has the origin directly below inline with the crotch.

Cuz I spent 15 minutes looking over your code again and again and again. I see nothing in what you posted that would cause that behavior, which leads me to suspect it's something you didn't post (like sprite origins, lol).

The only significant difference I see is in the old code, collision is always "to the left" unless hsp is positive, so even if hsp was negative it was still treated as a collision in the old code. ....Not that I see that having any significant effect (to this extent).

Did you use &! or did you use &~?
Doesn't matter, though. That wasn't the issue (at least not with the code I can see here).
 
Are you sure you didn't mess up the sprite origins? Make sure the idle sprite has the origin directly below inline with the crotch, make sure the running sprite has the origin directly below inline with the crotch, and make sure the jumping sprite has the origin directly below inline with the crotch.

Cuz I spent 15 minutes looking over your code again and again and again. I see nothing in what you posted that would cause that behavior, which leads me to suspect it's something you didn't post (like sprite origins, lol).
all the sprite origins are set to the same number 15,31 (bottom centre and then -1 on the x to make it asymmetrical (somewhere before I read somewhere that that is important lol, probably a tutorial by Heartbeast or something))

the AND NOT I used was &~ and I also found out why that didn't work, fixed it by following GMWolfs tutorial almost to the letter precise and my character gets stuck on walls only on it's left side lol (so far coding is wonderful)
so reverted that back to my own system as that at least worked (except for the jumping which is not game breaking yet so could be moved to the polish phase)

the old code always running a left collision check might be made with some twiddling around although I also don't suspect that to have an effect, but the jumping effect does seem more prominent when moving to the right.. hmmm

been breaking my head on this code for the better of 2 hours now... haven't found flaw yet and most other tutorials seem to fail on me too
 

TheouAegis

Member
all the sprite origins are set to the same number 15,31 (bottom centre and then -1 on the x to make it asymmetrical (somewhere before I read somewhere that that is important lol, probably a tutorial by Heartbeast or something))
Sprite asymmetry is a myth permeating around the forums since the early days of GM. The rules for sprite symmetry are as follows:

The left bound value and the right bound value must be opposite parity. If your left bound is 0 (even), then your right bound should be odd (e.g., 63). The origin of a sprite is a point, not a pixel, and should be dead center between those two, which can be calculated by (right_bounds+1-left_bounds)/2.Same applies to top and bottom, although most people don't require vertical symmetry, just horizontal symmetry.

If your sprite deviates from these rules even the slightest, then negating image_xscale will cause the bounding box to shift. If you are near an obstruction when that happens, you will be locked into a collision, which -- depending on how you coded collisions -- will usually either send the instance rocketing off into space or crash the game. If you must have asymmetry in a particular sprite for whatever reason (e.g., a knight carrying a spear), then the "front" of the sprite should be the largest.
 
hmm that might explain some of the "getting stuck" problems I have when I try GMWolfs tutorial and stick in onto my project
although I don't think it would explain all of it
sadly for the current system it doesn't help much since the y position is also changed on the jump and when I changed to origin from 16,31 to 15,31 that obviously wasn't changed

I posted a GIF of the problem at 10 fps, don't think that is visible yet with all the messages that are around my screen when I make a post lol




okay while typing this message I actually tried it on the GMWolf code and it works like a charm, no more getting stuck! the GMWolf code doesn't have the jumping bug either, only the one way platforms get some unintended behaviour
(when the jump ends halfway a platform you get instantly teleported to the top)
but that is probably hell of a lot easier to fix than the weird jumping mechanic

Thanks for the replies btw!
 

TheouAegis

Member
For a one-way, you need to check if there is no collision above the feet also, which usually just means "if a collision where i'm moving and not a collision where i am".
 
Top