• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GameMaker [SOLVED] How do I check which side of a bbox has been collided with?

I've been trying to make a projectile that will bounce off of enemies in the opposite direction- bouncing off things is working, but I've been massively struggling to figure out how to check what side of the instance it's likely impacting. Here's the current code from my GetSideCollide() function:

Code:
// Obtain the side of collision. 0 = left; 1 = right; 2 = top; 3 = bottom;
var leftA = argument0; // edges of area to check for a collision from
var rightA = argument1;
var topA = argument2;
var bottomA = argument3;

var leftB = argument4; // edges of area to check for a collision with
var rightB = argument5;
var topB = argument6;
var bottomB = argument7;

var leftcheck = abs(leftB - leftA); // distance between sides of collision areas
var rightcheck = abs(rightB - rightA);
var topcheck = abs(topB - topA);
var bottomcheck = abs(bottomB - bottomA);

if(rightA >= leftB && leftA <= leftB) // left side collision check
{
   if((leftcheck >= topcheck && topA < topB) || (leftcheck >= bottomcheck && bottomA > bottomB)) // if this side of impact has more area left over than other relevant sides...
   {
       return 0; // return that there was a collision on the left side
   }
}
if(leftA <= rightB && rightA >= rightB) // right side collision check
{
   if((rightcheck >= topcheck && topA < topB) || (rightcheck >= bottomcheck && bottomA > bottomB)) // if this side of impact has more area left over than other relevant sides...
   {
       return 1; // return that there was a collision on the right side
   }
}
if(bottomA >= topB && topA <= topB) // top side collision check
{
   if((topcheck >= leftcheck && leftA < leftB) || (topcheck >= rightcheck && rightA > rightB)) // if this side of impact has more area left over than other relevant sides...
   {
       return 2; // return that there was a collision on the top side
   }
}
if(topA <= bottomB && bottomA >= bottomB) // bottom side collision check
{
   if((bottomcheck >= leftcheck && leftA < leftB) || (bottomcheck >= rightcheck && rightA > rightB)) // if this side of impact has more area left over than other relevant sides...
   {
       return 3; // return that there was a collision on the bottom side
   }
}

return -1;
At some locations, the projectile will merely pass through due to returning -1 (which is my default return just so there's SOMETHING it returns), since there's some edge cases here- if the projectile is completely within the enemy's hitbox on left or right, for example, it can never bounce off from above or below because the hitbox for it isn't outside the enemy's hitbox. I can add a check for that easily, but then it doesn't solve the edge case of topcheck being less than leftcheck and rightcheck somehow, despite the fact that visually, it has clearly impacted the top of the hitbox, not the left and right sides. Anyone have any recommendations for what I could do instead to fix this? Am I going to have to do a ray loop to check from the prior frame's position to see which side of the bbox it was to have hit? That seems to be the 'easiest' way to do it, but it feels like there MUST be some easier, less intensive way...

I based it on information I got from a 2-year-old thread here, but there was VERY few details on how to do it other than "well just check if this value is bigger or smaller than this other value, and if so, it likely collided there!" It seemed to be for actually finding the point of impact, but what I need is not the point of impact, but the SIDE of impact. I can't simply use angles to find the corners because enemy hitboxes won't be just perfect squares, there'll be rectangles, too.

In case it helps: The projectile is a 4x4 hitbox moving at 12 speed in one of the diagonal angles only. The enemy I'm using to test is a perfect square that's 15x15.
 

3dgeminis

Member
An alternative:
Code:
///CREATE
speed_x

///STEP
if place_meeting(x+speed_x, y, o_wall)
   {
    while !place_meeting(x+sign(speed_x), y, o_wall) {x+=sign(speed_x)}
    speed_x*=-1
   }
x+=speed_x
You can also search for AABB collision
 
S

squirrelah

Guest
You can also use "point_collision" in the collision event.

Something like

Direction_of_collision = point_direction(x,y,other.x,other.y)

Put it in the collision event so it only runs when a collision happens.

And then depending on what the angle of the collision is you know whether it was the top, bottom, left or right.

For a square top would be between 225 and 315, right would be between 225 and 135. Bottom would be between 135 and 45, and left would be between 45 and 315
 
You can also use "point_collision" in the collision event.

Something like

Direction_of_collision = point_direction(x,y,other.x,other.y)

Put it in the collision event so it only runs when a collision happens.

And then depending on what the angle of the collision is you know whether it was the top, bottom, left or right.

For a square top would be between 225 and 315, right would be between 225 and 135. Bottom would be between 135 and 45, and left would be between 45 and 315
Not all the hitboxes would be perfect squares, though, so how would I account for variable widths and heights?
 

YellowAfterlife

ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ
Forum Staff
Moderator
If the projectile can only move in 4 diagonal directions, and similarly bounce only in those directions, you could very much do
Code:
var px = xprevious, py = yprevious;
var sx = speed_x, sy = speed_y; // or hspeed,vspeed if you use those
if (!place_meeting(px - sx, py + sy, obj_enemy)) { // you can add a wall check here too, ` && !p...`
    speed_x *= -1;
} else if (!place_meeting(px + sx, py - sy, obj_enemy)) {
    speed_y *= -1;
} else { // probably a corner
    speed_x *= -1;
    speed_y *= -1;
}
if that proves insufficient, you could instead check for via collision_line rather than just place_meeting.
 
If the projectile can only move in 4 diagonal directions, and similarly bounce only in those directions, you could very much do
Code:
var px = xprevious, py = yprevious;
var sx = speed_x, sy = speed_y; // or hspeed,vspeed if you use those
if (!place_meeting(px - sx, py + sy, obj_enemy)) { // you can add a wall check here too, ` && !p...`
    speed_x *= -1;
} else if (!place_meeting(px + sx, py - sy, obj_enemy)) {
    speed_y *= -1;
} else { // probably a corner
    speed_x *= -1;
    speed_y *= -1;
}
if that proves insufficient, you could instead check for via collision_line rather than just place_meeting.
I use just the generic "speed" variable- will that work with this? :/ What I'm doing right now with my collision is leaving speed itself alone, but changing the direction:

Code:
if(temp != noone)
{
   //var distX = GetCenter(temp.bbox_left, temp.bbox_right) - x;
   //var distY = GetCenter(temp.bbox_top, temp.bbox_bottom) - y;

   var impact = GetCollideSide(temp.bbox_left, temp.bbox_right, temp.bbox_top, temp.bbox_bottom, prevX, prevY); (this is changed from the prior version I posted here- I tried a ray, and it didn't work...)

   if(impact < 2 && impact != -1)
   {
       direction = 180-direction;
       if(impact == 1)
       {
           while(bbox_left < temp.bbox_right)
           {
               x++;
           }
       }
       else
       {
           while(bbox_right > temp.bbox_left)
           {
               x--;
           }
       }
   }
   else if(impact != -1)
   {
       direction = 360-direction;
       if(impact == 3)
       {
           while(bbox_top < temp.bbox_bottom)
           {
               y++;
           }
       }
       else
       {
           while(bbox_bottom > temp.bbox_top)
           {
               y--;
           }
       }
   }
}
(I pass the enemy's instance ID to "temp", so I can potentially reuse this code later for terrain collision as well instead of having to copy-paste it in every collision event.)


EDIT: Would like to report that I solved it! I downloaded a script here: https://www.gmlscripts.com/script/lines_intersect to test if a line from previous frame to just past the current frame would intersect with a line representing each edge of a rectangle, one pixel thick. Whichever one it impacted first out of left, top, right, and bottom, that's where it'd assume collision.

For anyone interested, my code using this script:

Code:
// Obtain the side of collision. Outputs: -1 = nothing in case the code doesn't work; 0 = left; 1 = right; 2 = top; 3 = bottom;
var left = argument0; // area to collide with
var right = argument1;
var top = argument2;
var bottom = argument3;
var prevX = argument4; // the location the previous frame, to ensure the origin point of our line is outside the area of collision
var prevY = argument5;

var endX = prevX+((speed+5)*dcos(direction)); // end point of collision, plus 5 radius to ensure the check goes into the collision area
var endY = prevY+((speed+5)*dsin(-direction));
if(lines_intersect(prevX, prevY, endX, endY, left, top, left, bottom, true) > 0) // check left side first, one-wide so it can't interfere with other checks...
{
   return 0; // return collision on this side
}
if(lines_intersect(prevX, prevY, endX, endY, left, top, right, top, true) > 0) // top side check
{
   return 2; // return collision on this side
}
if(lines_intersect(prevX, prevY, endX, endY, right, top, right, bottom, true) > 0) // right side check
{
   return 1; // return collision on this side
}
if(lines_intersect(prevX, prevY, endX, endY, left, bottom, right, bottom, true) > 0) // bottom side check
{
   return 3; // return collision on this side
}
return -1; // return -1 just in case none of these trigger somehow
 
Last edited:

YellowAfterlife

ᴏɴʟɪɴᴇ ᴍᴜʟᴛɪᴘʟᴀʏᴇʀ
Forum Staff
Moderator
I use just the generic "speed" variable- will that work with this?
It will (swap out speed_* for hspeed/vspeed) - speed/direction and hspeed/vspeed are synchronized, so changing hspeed updated speed and direction, for example.
 
Top