Player SOMETIMES gets stuck in walls if he gets hit by an enemy

pixeltroid

Member
I thought my movement code was perfect but I just discovered that sometimes the player gets stuck a bit in walls. But not completely, as moving away from the wall "unsticks" the player. Very, very rarely getting hit by a projectile knocks me into a wall and I am unable to move. I am not able to replicate these errors whenever I will, so I am cannot pinpoint the root of the issue. These errors happens very rarely, but I am afraid that later on, other players might stumble upon these errors and think the game is broken (which it is, to an extent)

Here are the codes I have for movement and horizontal collision in the player's movement script.

Code:
{
// motion
if (move != 0) && !keyboard_check(ord("F")) && !keyboard_check(ord("S")) // if pressing in either direction
{
      
// accelerate
if (move ==  1)  hspd = min(hspd + fric,  spd); // accelerate until max speed in right direction
else
if (move == -1)  hspd = max(hspd - fric, -spd); // accelerate until max speed in left direction
}
else
{
// either D or A are not pressed or both are pressed at the same time -> don't move
// decelerate
if (hspd > 0) hspd = max(hspd - fric, 0); // decelerate toward 0
else
if (hspd < 0) hspd = min(hspd + fric, 0); // decelerate toward 0
}
   

 
// animation
if (move != 0)
{
image_xscale = move;
}
if (abs(hspd) > 0.50)
{
   sprite_index = spr_playerwalk;
   image_speed = abs(hspd) * 0.10;   //.20
}
else
{
   sprite_index = spr_playerstand;
   image_speed = 0.10;
}
   
}

//horizontal collision

if (place_meeting(x+hspd, y, obj_solid))
{
while(!place_meeting(x+sign(hspd),y, obj_solid))
{
x+= sign (hspd);
}
hspd = 0;
}
x += hspd;
Here is the code that pertains to player knockback. These are in obj_player


1. In create event
Code:
//RECOIL
recoilSpeed=10 //3;//set recoilSpeed as the speed you wish them to be flung backwards, reduce this if they recoil too fast
playerRecoil=2; //2
playerStop=-1;
directionOfBullet = ""
2. In step event (Forgot to post this earlier)
Code:
//RECOIL 
if(playerRecoil!=-1){
playerRecoil-=1;
if (directionOfBullet == "Right"){ //facing right
hspeed-=4 
vspeed-=4; 

} else if (directionOfBullet == "Left"){ //facing left
hspeed+=4 
vspeed -=4;
}

// speed= recoilSpeed;
playerStop=1;
}

if(playerRecoil=-1 && playerStop=1){
hspeed=0; 
vspeed=0; 
playerStop=-1;
}


3. Upon colliding with enemy or enemy projectile

Code:
with (obj_enemyfireball3) {
    if obj_player.x <= x{
          obj_player.directionOfBullet="Right";
          obj_player.playerRecoil = 5
          }
          else {
          obj_player.directionOfBullet="Left";
          obj_player.playerRecoil = 5
          }
}

Is there something I can add or fix to make sure that I never get stuck in walls??
 
Last edited:

pixeltroid

Member
Maybe. Where's the bit where the recoil actually does anything?

in the create event:

//RECOIL
recoilSpeed=10 //3;//set recoilSpeed as the speed you wish them to be flung backwards, reduce this if they recoil too fast
playerRecoil=2; //2
playerStop=-1;
directionOfBullet = ""



In step event:
Code:
with (obj_enemyfireball3) {
if obj_player.x <= x{
obj_player.directionOfBullet="Right";
obj_player.playerRecoil = 5
}
else {
obj_player.directionOfBullet="Left";
obj_player.playerRecoil = 5
}
}
 
Last edited:

Nidoking

Member
You don't need to bold it. You just have to post the code that shows where playerRecoil ever has any impact on the actual position. The font has no effect on anything.
 

pixeltroid

Member
You don't need to bold it. You just have to post the code that shows where playerRecoil ever has any impact on the actual position. The font has no effect on anything.

oops. I forgot to post one block of code in the player's step event. I've updated the OP.

This is the code that I left out earlier:

Code:
//RECOIL 
if(playerRecoil!=-1){
playerRecoil-=1;
if (directionOfBullet == "Right"){ //facing right
hspeed-=4 
vspeed-=4; 

} else if (directionOfBullet == "Left"){ //facing left
hspeed+=4 
vspeed -=4;
}

// speed= recoilSpeed;
playerStop=1;
}

if(playerRecoil=-1 && playerStop=1){
hspeed=0; 
vspeed=0; 
playerStop=-1;
}
 

MishMash

Member
GML:
//horizontal collision

if (place_meeting(x+hspd, y, obj_solid))
{
while(!place_meeting(x+sign(hspd),y, obj_solid))
{
x+= sign (hspd);
}
hspd = 0;
}
[B]x += hspd;[/B]
I think this is because there is a slight logic flaw in your code here. The setup is correct in that movement is controlled exclusively through speed, and before moving the player, you are checking whether the player can move first.
The line that confuses me however is the

x += hspd;

at the bottom, which is outside of your collision code. hspd is set to zero but only if you are actively colliding with something, where that codes intent is to move as far as you can.

This scheme however has two problems which would be worth addressing:
1) If you are travelling sufficiently fast such that place_meeting(x+hspd, y, obj_solid) returns false, however there is a collidable object between your old position x, and your new position, x+hspd.
2) Possibility of rounding errors for fractional speed values. for consistency sake, place_meeting checks should be done with rounded position. I would suggest using ceil.
-- an example of where this will fail is if your speed is < 1, e.g. 0.4.
-- This can be a challenge to resolve if you want to support sub-frame speeds, however we can assume that you cannot partially clip something, and thus the minimum unit of transfer is 1.0.

So to address problem 1), you can firstly just remove the external position check, and always query place_meeting before moving. This is going to be less efficient, as you will always test in one pixel increments, however, if this Is perfect, then you have a solid and correct base you can work off of. The optimisation here is to then introduce a minimum step based on the minimum mask size you would not want to clip through.

To resolve problem 2), just wrap all expressions with x+hspd or x + sign(hspd) with ceil(). As described above, this is because currently, moving at a speed of 0.4 would pass the initial place_meeting test, but actually, you could begin moving inside the object. For additional safety, on stopping, we can lock the players x to be an exact pixel increment.



Code #1 - Add Ceils

GML:
//horizontal collision

if (place_meeting(ceil(x+hspd), y, obj_solid))
{
while(!place_meeting(ceil(x+sign(hspd)),y, obj_solid))
{
x+= sign (hspd);
}
hspd = 0;
}
[B]x += hspd;[/B]

Code #2 -- always query for collision before moving, allow movements in subpixel increments

GML:
//horizontal collision
if(hspd > 0 ){
for( var h = 0; h < ceil(abs(hspd)); h++){
if(!place_meeting(ceil(x+sign(hspd)),y, obj_solid){
x+= sign (hspd)*min(1, h-abs(hspd));
} else {
x = floor(x);
hspd = 0;
}
}
This code is super quick and dirty and m not 100% happy with it. Ideally, you would want to use ceil() when going to the right, and floor() when going to the left to allow for the most-overlap possible. I am quite confident however, that this is a rounding issue.
 

Nidoking

Member
Why are you using hspeed for the recoil and hspd for the actual movement? They should probably both be the same.
 

pixeltroid

Member
GML:
//horizontal collision

if (place_meeting(x+hspd, y, obj_solid))
{
while(!place_meeting(x+sign(hspd),y, obj_solid))
{
x+= sign (hspd);
}
hspd = 0;
}
[B]x += hspd;[/B]
I think this is because there is a slight logic flaw in your code here. The setup is correct in that movement is controlled exclusively through speed, and before moving the player, you are checking whether the player can move first.
The line that confuses me however is the

x += hspd;

at the bottom, which is outside of your collision code. hspd is set to zero but only if you are actively colliding with something, where that codes intent is to move as far as you can.

This scheme however has two problems which would be worth addressing:
1) If you are travelling sufficiently fast such that place_meeting(x+hspd, y, obj_solid) returns false, however there is a collidable object between your old position x, and your new position, x+hspd.
2) Possibility of rounding errors for fractional speed values. for consistency sake, place_meeting checks should be done with rounded position. I would suggest using ceil.
-- an example of where this will fail is if your speed is < 1, e.g. 0.4.
-- This can be a challenge to resolve if you want to support sub-frame speeds, however we can assume that you cannot partially clip something, and thus the minimum unit of transfer is 1.0.

So to address problem 1), you can firstly just remove the external position check, and always query place_meeting before moving. This is going to be less efficient, as you will always test in one pixel increments, however, if this Is perfect, then you have a solid and correct base you can work off of. The optimisation here is to then introduce a minimum step based on the minimum mask size you would not want to clip through.

To resolve problem 2), just wrap all expressions with x+hspd or x + sign(hspd) with ceil(). As described above, this is because currently, moving at a speed of 0.4 would pass the initial place_meeting test, but actually, you could begin moving inside the object. For additional safety, on stopping, we can lock the players x to be an exact pixel increment.



Code #1 - Add Ceils

GML:
//horizontal collision

if (place_meeting(ceil(x+hspd), y, obj_solid))
{
while(!place_meeting(ceil(x+sign(hspd)),y, obj_solid))
{
x+= sign (hspd);
}
hspd = 0;
}
[B]x += hspd;[/B]

Code #2 -- always query for collision before moving, allow movements in subpixel increments

GML:
//horizontal collision
if(hspd > 0 ){
for( var h = 0; h < ceil(abs(hspd)); h++){
if(!place_meeting(ceil(x+sign(hspd)),y, obj_solid){
x+= sign (hspd)*min(1, h-abs(hspd));
} else {
x = floor(x);
hspd = 0;
}
}
This code is super quick and dirty and m not 100% happy with it. Ideally, you would want to use ceil() when going to the right, and floor() when going to the left to allow for the most-overlap possible. I am quite confident however, that this is a rounding issue.

Hi. Thanks for your help. I really appreciate it.

I'm trying out your code. I think Code#2 is missing a few brackets?
 

pixeltroid

Member
I have implemented a temporary fix in the collide function

Does anyone know I can solve the issue I am facing? The horizontal collision code below is from Shaun Spaldings tutorial.

Code:
//horizontal collision

if (place_meeting(x+hspd, y, obj_solid))
{
while(!place_meeting(x+sign(hspd),y, obj_solid))
{
x+= sign (hspd);
}
hspd = 0;
}
x += hspd;

I've also seen it on posts on the GMC forum. But it's not working for me for some reason. Any idea why?
 

pixeltroid

Member
GML:
//horizontal collision

if (place_meeting(x+hspd, y, obj_solid))
{
while(!place_meeting(x+sign(hspd),y, obj_solid))
{
x+= sign (hspd);
}
hspd = 0;
}
[B]x += hspd;[/B]
I think this is because there is a slight logic flaw in your code here. The setup is correct in that movement is controlled exclusively through speed, and before moving the player, you are checking whether the player can move first.
The line that confuses me however is the

x += hspd;

at the bottom, which is outside of your collision code. hspd is set to zero but only if you are actively colliding with something, where that codes intent is to move as far as you can.

This scheme however has two problems which would be worth addressing:
1) If you are travelling sufficiently fast such that place_meeting(x+hspd, y, obj_solid) returns false, however there is a collidable object between your old position x, and your new position, x+hspd.
2) Possibility of rounding errors for fractional speed values. for consistency sake, place_meeting checks should be done with rounded position. I would suggest using ceil.
-- an example of where this will fail is if your speed is < 1, e.g. 0.4.
-- This can be a challenge to resolve if you want to support sub-frame speeds, however we can assume that you cannot partially clip something, and thus the minimum unit of transfer is 1.0.

So to address problem 1), you can firstly just remove the external position check, and always query place_meeting before moving. This is going to be less efficient, as you will always test in one pixel increments, however, if this Is perfect, then you have a solid and correct base you can work off of. The optimisation here is to then introduce a minimum step based on the minimum mask size you would not want to clip through.

To resolve problem 2), just wrap all expressions with x+hspd or x + sign(hspd) with ceil(). As described above, this is because currently, moving at a speed of 0.4 would pass the initial place_meeting test, but actually, you could begin moving inside the object. For additional safety, on stopping, we can lock the players x to be an exact pixel increment.



Code #1 - Add Ceils

GML:
//horizontal collision

if (place_meeting(ceil(x+hspd), y, obj_solid))
{
while(!place_meeting(ceil(x+sign(hspd)),y, obj_solid))
{
x+= sign (hspd);
}
hspd = 0;
}
[B]x += hspd;[/B]

Code #2 -- always query for collision before moving, allow movements in subpixel increments

GML:
//horizontal collision
if(hspd > 0 ){
for( var h = 0; h < ceil(abs(hspd)); h++){
if(!place_meeting(ceil(x+sign(hspd)),y, obj_solid){
x+= sign (hspd)*min(1, h-abs(hspd));
} else {
x = floor(x);
hspd = 0;
}
}
This code is super quick and dirty and m not 100% happy with it. Ideally, you would want to use ceil() when going to the right, and floor() when going to the left to allow for the most-overlap possible. I am quite confident however, that this is a rounding issue.
Hey there were some issues with your codes.

It worked fine while moving left to right, but not the other way round. Going right to left, the player would sometimes stick to the walls.
 
Hey there!
I just solved my collision issue where my character would get stuck whenever I moved backwards into a wall. My sprite (Collision Mask) was 100 x 100 and origin was set to 50. I tried setting it to 51 and now I got stuck moving forward into the wall. It's the stupidest thing but I increased the sprite width (Collision Mask) to 101 and had origin at 51 and now it works flawlessly. I figured you're problem could be a similar one since you got stuck after getting pushed back.

I know it's a fugly fix and I never had sprites set up like this before but I've been tackling this issue for far too long. I'm just gonna leave it like this for now.

Best wishes

Here's my code if it's of any interest:


//Horisontal Collision
if (place_meeting(x+hsp,y,obj_wall))
{
while (!place_meeting(x+sign(hsp),y,obj_wall))
{
x+=sign(hsp);
}
hsp=0
}
x+=hsp;


//Vertical Collision
if (place_meeting(x,y+vsp,obj_wall))
{
while (!place_meeting(x,y+sign(vsp),obj_wall))
{
y+=sign(vsp);
}
vsp = 0;
}
else
{
y+=vsp
}
 
Last edited:
Top