• 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!

Legacy GM Should I use collision_rectangle for platformer collisions?

Starturbo

Member
Hi, I'm making a platform game in GameMaker: Studio, and I'm wondering if I should even bother trying to use collision_rectangle. Someone I talk to, who is more experienced than I am, recommended me to use it for collisions. I know some other people are also starting to use collision_rectangle, however, almost all tutorials still use place_meeting. I'm kind of wondering which one I should use for the main ground collisions. I used to use place_meeting, actually, and got collisions to work pretty well (not perfect though). With collision_rectangle, however, I'm running into a lot of issues. The ground collision works well, besides a rare glitch, but I can't figure out how to get slopes working. Here's some of the ground collision code I have so far:

Code:
//Sets the Nearest Blocks to the Ones the Player is Colliding With

left_block = collision_rectangle(bbox_left + hsp, bbox_top + 1, bbox_left + hsp, bbox_bottom - 1, obj_par_block, false, true);

right_block = collision_rectangle(bbox_right + hsp, bbox_top + 1, bbox_right + hsp, bbox_bottom - 1, obj_par_block, false, true);

top_block = collision_rectangle(bbox_left + 1, bbox_top + vsp, bbox_right - 1, bbox_top + vsp, obj_par_block, false, true);

bottom_block = collision_rectangle(bbox_left + 1, bbox_bottom + vsp, bbox_right - 1, bbox_bottom + vsp, obj_par_block, false, true);

//Horizontal Collision

if left_block && image_xscale = -1
{
    hsp = 0;
   
    scr_position(left_block.bbox_right + 9, 0, self);
}

if left_block && (image_xscale = 1 || key_right) && !(key_left && key_right)
{
    hsp = 0;
   
    scr_position(left_block.bbox_right + 11, 0, self);
}

if right_block && image_xscale = 1
{     
    hsp = 0;
   
    scr_position(right_block.bbox_left - 8, 0, self);
}

if right_block && (image_xscale = -1 || key_left) && !(key_left && key_right)
{     
    hsp = 0;
   
    scr_position(right_block.bbox_left - 10, 0, self);
}

x += hsp;

//Vertical Collision

if top_block && !left_block && !right_block && vsp < 0
{
    vsp = 0;
   
    scr_position(0, top_block.bbox_bottom + (sprite_height / 2), self);
}

if bottom_block && vsp > 0
{
   vsp = 0;
   
   scr_position(0, bottom_block.bbox_top - (sprite_height / 2), self);
}

y += vsp;
And here's the code for slopes. Keep in mind, the following code is unfinished:

Code:
//Sets the Nearest Slopes to the Ones the Player is Colliding With

near_left_slope = collision_rectangle(bbox_left + hsp, bbox_top + 1, bbox_left + hsp, bbox_bottom - 1, obj_par_slope, true, true);

near_right_slope = collision_rectangle(bbox_right + hsp, bbox_top + 1, bbox_right + hsp, bbox_bottom - 1, obj_par_slope, true, true);

near_top_slope = collision_rectangle(bbox_left + 1, bbox_top + vsp, bbox_right - 1, bbox_top + vsp, obj_par_slope, true, true);

near_bottom_slope = collision_rectangle(bbox_left + 1, bbox_bottom + vsp, bbox_right - 1, bbox_bottom + vsp, obj_par_slope, true, true);

left_slope = collision_rectangle(bbox_left - 1, bbox_top + 1, bbox_left - 1, bbox_bottom - 1, obj_par_slope, true, true);

right_slope = collision_rectangle(bbox_right + 1, bbox_top + 1, bbox_right + 1, bbox_bottom - 1, obj_par_slope, true, true);

top_slope = collision_rectangle(bbox_left + 1, bbox_top - 1, bbox_right - 1, bbox_top - 1, obj_par_slope, true, true);

bottom_slope = collision_rectangle(bbox_left + 1, bbox_bottom + 1, bbox_right - 1, bbox_bottom + 1, obj_par_slope, true, true);

//Up Slopes

if near_bottom_slope && vsp > 0
{
    y += vsp;

    vsp = 0;
}

while collision_rectangle(bbox_left + 1, bbox_bottom + 1, bbox_right - 1, bbox_bottom + 1, obj_par_slope, true, true)
{
    y -= 1;

    vsp = 0;
}

x += hsp;

y += vsp;
I'm using a finite state machine, by the way. Any help is appreciated!
 

TheouAegis

Member
Update: They're nearly identical in terms of speed. Maybe I was confusing it with collision_line, which is slow AF in all versions of GM.

Bear in mind though that collision_rectangle is far more flexible since you can specify what coordinates to actually use, whereas with place_meeting you'd have to change the sprite's mask. But place_meeting is shorter to type out.
 
Last edited:
W

whale_cancer

Guest
Update: They're nearly identical in terms of speed. Maybe I was confusing it with collision_line, which is slow AF in all versions of GM.

Bear in mind though that collision_rectangle is far more flexible since you can specify what coordinates to actually use, whereas with place_meeting you'd have to change the sprite's mask. But place_meeting is shorter to type out.
I just wanted to +1 this. Using rectangles means you can more easily dynamically change how your object is going to handle collision detection. When I worked on a platformer, I found this flexibility was very useful for situations like climbing ladders, sliding, etc.,
 

Starturbo

Member
Okay, I guess I probably should use collision_rectangle then. My main problem now is getting collisions to work properly. For instance, there is a rare glitch (as in, hard to replicate) where jumping into the corner of a ground block causes the screen to shake, as long as you are moving into it. It's not game-breaking really, but it does look a bit weird. Going up slopes work well too, though you do move 1 pixel above the ground at the very top of the slope. I don't know how I should code going down slopes though.
 

TheouAegis

Member
For the corner thing, sounds like it could be a mismatch between your horizontal collision and vertical collision. You may consider using two different rectangle bounds for horizontal and vertical collisions, too. Don't think of it in terms of the whole box, but rather just the vertices. At which 4 points do you want to check for vertical collisions above/below? At which 4 points do you want to check for horizontal collisions fore/aft?
 
W

whale_cancer

Guest
Okay, I guess I probably should use collision_rectangle then. My main problem now is getting collisions to work properly. For instance, there is a rare glitch (as in, hard to replicate) where jumping into the corner of a ground block causes the screen to shake, as long as you are moving into it. It's not game-breaking really, but it does look a bit weird. Going up slopes work well too, though you do move 1 pixel above the ground at the very top of the slope. I don't know how I should code going down slopes though.
I had the same issue with corner blocks, but my sprites were probably smaller than yours so it was much less rare. You basically just need to program a special check for this exact situation (where your vspeed + hspeed lands you in a corner collision) that comes before your independent vertical and horizontal collision checks. That is, assuming it is the same problem I (and seemingly many others) had. I include this last caveat as your collision code seems much longer than what I remember doing.
 

Starturbo

Member
For the corner thing, sounds like it could be a mismatch between your horizontal collision and vertical collision. You may consider using two different rectangle bounds for horizontal and vertical collisions, too. Don't think of it in terms of the whole box, but rather just the vertices. At which 4 points do you want to check for vertical collisions above/below? At which 4 points do you want to check for horizontal collisions fore/aft?
Do you mean I should use two separate bounding boxes? I'm not sure how to do that though.

I had the same issue with corner blocks, but my sprites were probably smaller than yours so it was much less rare. You basically just need to program a special check for this exact situation (where your vspeed + hspeed lands you in a corner collision) that comes before your independent vertical and horizontal collision checks. That is, assuming it is the same problem I (and seemingly many others) had. I include this last caveat as your collision code seems much longer than what I remember doing.
Yeah, I just tried something like that, but now I have an issue where sometimes the player "floats" one pixel off the ground after jumping while touching a wall. Here's an example of the code I tried:

Code:
//Corner Collision

if (left_block && bottom_block) || (right_block && bottom_block)
{
    hsp = 0;
   
    vsp = 0;
}
 
W

whale_cancer

Guest
Yeah, I just tried something like that, but now I have an issue where sometimes the player "floats" one pixel off the ground after jumping while touching a wall. Here's an example of the code I tried:

Code:
//Corner Collision

if (left_block && bottom_block) || (right_block && bottom_block)
{
    hsp = 0;

    vsp = 0;
}
You will need to include a way of snapping the player to the object you are colliding with. The basic logic would be...

If moving diagonally would result in a collision then...
Advance the player incrementally until they are next to the colliding object (separately for each direction)...
and Set the players speeds to 0.

This is usually done with a for loop advancing the player's position by 0.1 until they snap up against the object.
 

Starturbo

Member
You will need to include a way of snapping the player to the object you are colliding with. The basic logic would be...

If moving diagonally would result in a collision then...
Advance the player incrementally until they are next to the colliding object (separately for each direction)...
and Set the players speeds to 0.

This is usually done with a for loop advancing the player's position by 0.1 until they snap up against the object.
Something like this?

Code:
//Corner Collision

if left_block && bottom_block
{
    var i;

    for (i = 0; i < 10; i += 1)
    {
        x += i;
    }
}

if right_block && bottom_block
{
    var i;

    for (i = 0; i < 10; i += 1)
    {
        x -= i;
    }
}
I tried this code, anyways, and appears to fix it. However, I still don't quite know how I should code going down slopes, and I there is still that issue with going up slopes, where you float one pixel above the ground at the very top.

Edit: It doesn't seem to fix it, actually. Though, I tried something more similar to what you said, but you still float one pixel off the ground.
 
Last edited:
P

Paolo Mazzon

Guest
Use a collision grid. They are easy to implement and much faster than normal collisions. Just create a ds_grid and to check collisions check all four corners of a sprite's hitbox. I don't know about all this non-pixel perfect stuff, just round the player's x/y before performing collisions.
 

Starturbo

Member
Use a collision grid. They are easy to implement and much faster than normal collisions. Just create a ds_grid and to check collisions check all four corners of a sprite's hitbox. I don't know about all this non-pixel perfect stuff, just round the player's x/y before performing collisions.
That sounds like an interesting idea, but I'm having some trouble figuring out how I would use them for platform collisions. I'm still not an expert at GML, by the way. I don't think I've heard of anyone else using a ds_grid for platform collisions either. Still, I'm interested in the idea, so if you are able to explain it more to make it easier for me, I'd appreciate it.
 
P

Paolo Mazzon

Guest
Well, plenty of people use collision grids even in GM because of the advantages. I'm going under the assumption you know how ds_grids work, so if not, check the documentation. Basically, you create a grid filled with wall positions. Say your room is 960 * 544, and the walls in it are 32 * 32. That would mean you need a 30 * 17 grid to store all the walls (960 / 32 = 30, 544 / 32 = 17). Loading walls could be done a few ways, you could make your own wall format and load it, you could replace all the wall objects in the room at startup, or random generate -- doesn't matter. Checking collisions is super easy as long as the things you're checking are the same size or less than the walls (Slightly bigger will likely be fine as well). All you need to do is check if all of the corners of the object you're checking are colliding with a wall. Here is a diagram of it

To check the corners of a hitbox (demonstrated by the red) you just use a coordinate value plus the the corresponding bbox_*, eg; top left = (obj.x + obj.bbox_left, obj.y + obj.bbox_top), and divide it by the width/height of the walls then round it down. Look at the big red on the diagram, we'll call the top left corner 48 * 16 and each wall's width/height 32. That would mean we check the grid position grid[# 48 div 32, 16 div 32], which comes back true because there is a wall there.

tl;dr, check all four corners of a hitbox for a hit on the grid.
 

Starturbo

Member
Well, plenty of people use collision grids even in GM because of the advantages. I'm going under the assumption you know how ds_grids work, so if not, check the documentation. Basically, you create a grid filled with wall positions. Say your room is 960 * 544, and the walls in it are 32 * 32. That would mean you need a 30 * 17 grid to store all the walls (960 / 32 = 30, 544 / 32 = 17). Loading walls could be done a few ways, you could make your own wall format and load it, you could replace all the wall objects in the room at startup, or random generate -- doesn't matter. Checking collisions is super easy as long as the things you're checking are the same size or less than the walls (Slightly bigger will likely be fine as well). All you need to do is check if all of the corners of the object you're checking are colliding with a wall. Here is a diagram of it

To check the corners of a hitbox (demonstrated by the red) you just use a coordinate value plus the the corresponding bbox_*, eg; top left = (obj.x + obj.bbox_left, obj.y + obj.bbox_top), and divide it by the width/height of the walls then round it down. Look at the big red on the diagram, we'll call the top left corner 48 * 16 and each wall's width/height 32. That would mean we check the grid position grid[# 48 div 32, 16 div 32], which comes back true because there is a wall there.

tl;dr, check all four corners of a hitbox for a hit on the grid.
Thanks! I think that helped a bit, but maybe I should read the documentation about ds_grids a bit more too.
 

Starturbo

Member
I'm still really confused by the collision grid method. I looked at the documentation some more too. Here's some of the code I have so far:

Code:
Player's Create Event:

wall_grid = ds_grid_create(room_width / 16, room_height / 16);

Player's Destroy Event:

ds_grid_destroy(wall_grid);

Collision Script:

top_left = floor((obj_par_block.bbox_left && obj_par_block.bbox_top) / 16);

bottom_left = floor((obj_par_block.bbox_left && obj_par_block.bbox_bottom) / 16);

top_right = floor((obj_par_block.bbox_right && obj_par_block.bbox_top) / 16);

bottom_right = floor((obj_par_block.bbox_right && obj_par_block.bbox_bottom) / 16);
I'm still really confused on how you check each corner for collision though, and I'm also not sure if the walls are "loaded" simply by making a grid.

The purpose of the destroy event is to prevent memory leaks, since the documentation said you should do that.
 

RangerX

Member
If ever you don't go grid method, I had a comment about your collisions functions. You were creating collision rectangles probably every step and that's useless.
If you want to save some processing, check if your character is moving and in what direction. Then create the collision rectangles that are absolutely necessary and that's all.
 

TheouAegis

Member
You don't just simply create a grid and suddenly right there is a collision map. You have to actually specify the values for that Collision map. The Collision map needs to be generated as a global variable , and usually should be generated at the start of the room. if cell 1, 5 is "solid", you need to set it. For checking collisions, you simply take a coordinate, divide it by the width and height of your tiles to find out what cell it falls under, and then check the value of that cell.
 

Starturbo

Member
You don't just simply create a grid and suddenly right there is a collision map. You have to actually specify the values for that Collision map. The Collision map needs to be generated as a global variable , and usually should be generated at the start of the room. if cell 1, 5 is "solid", you need to set it. For checking collisions, you simply take a coordinate, divide it by the width and height of your tiles to find out what cell it falls under, and then check the value of that cell.
So, basically, I'd have to go in and manually set the locations of all the solids through code? That would be a huge pain for me, since my rooms will be quite large. If that's the case, I may just stick with collision_rectangle.

If ever you don't go grid method, I had a comment about your collisions functions. You were creating collision rectangles probably every step and that's useless.
If you want to save some processing, check if your character is moving and in what direction. Then create the collision rectangles that are absolutely necessary and that's all.
Yeah, I think I may know what you're saying. I will see if I can fix that.
 

RangerX

Member
Yeah, I think I may know what you're saying. I will see if I can fix that.
To put it simply, you know if your character is moving left or right thanks to HSPEED variable. And you know if it moves up or down thanks to VSPEED.
So if there's positive hspeed, check the rectangle on your right. Do not calculate the one on your left because its entirely useless at that moment. See?
 

TheouAegis

Member
You don't have to set it manually, you can do what most people do and write a script that detects tiles and fills in the grid automatically.
 

Starturbo

Member
Code:
//Sets the Nearest Blocks to the Ones the Player is Colliding With

left_block = collision_rectangle(bbox_left + hsp, bbox_top + 1, bbox_left + hsp, bbox_bottom - 1, obj_par_block, false, true);

right_block = collision_rectangle(bbox_right + hsp, bbox_top + 1, bbox_right + hsp, bbox_bottom - 1, obj_par_block, false, true);

top_block = collision_rectangle(bbox_left + 1, bbox_top + vsp, bbox_right - 1, bbox_top + vsp, obj_par_block, false, true);

bottom_block = collision_rectangle(bbox_left + 1, bbox_bottom + vsp, bbox_right - 1, bbox_bottom + vsp, obj_par_block, false, true);

//Corner Collision

if left_block && bottom_block && hsp < 0
{
    hsp = 0;
   
    vsp = 0;
       
    scr_position(left_block.bbox_right + 9, 0, self);
}

if right_block && bottom_block && hsp > 0
{
    hsp = 0;
   
    vsp = 0;
   
    scr_position(right_block.bbox_left - 8, 0, self);
}

//Horizontal Collision

if left_block && hsp < 0
{
    hsp = 0;
   
    scr_position(left_block.bbox_right + 9, 0, self);
}

if right_block && hsp > 0
{     
    hsp = 0;
   
    scr_position(right_block.bbox_left - 8, 0, self);
}

x += hsp;

//Vertical Collision

if top_block && vsp < 0
{
    vsp = 0;
   
    scr_position(0, top_block.bbox_bottom + (sprite_height / 2), self);
}

if bottom_block && vsp > 0
{
   vsp = 0;
   
   scr_position(0, bottom_block.bbox_top - (sprite_height / 2), self);
}

y += vsp;
Here's what I have for ground collision now. I'm assuming you meant to only check the left side, if the hspeed is negative? Otherwise, I'm not sure how it would work. The ground collision seems to be working really well now and I'm not really getting any glitches. I also had to tell the player to stop, if colliding with walls in the movement code too, as before the hspeed was still slightly greater than 0, even when colliding with walls. The only issue I have now is with slopes. Going up slopes worked pretty well before when the tiles were objects. But now, I changed them to actual tiles, and put 16x16 invisible objects over them, which are what the player collides with. Maybe, they are just too small now?
 

RangerX

Member
//Sets the Nearest Blocks to the Ones the Player is Colliding With

left_block = collision_rectangle(bbox_left + hsp, bbox_top + 1, bbox_left + hsp, bbox_bottom - 1, obj_par_block, false, true);

right_block = collision_rectangle(bbox_right + hsp, bbox_top + 1, bbox_right + hsp, bbox_bottom - 1, obj_par_block, false, true);

top_block = collision_rectangle(bbox_left + 1, bbox_top + vsp, bbox_right - 1, bbox_top + vsp, obj_par_block, false, true);

bottom_block = collision_rectangle(bbox_left + 1, bbox_bottom + vsp, bbox_right - 1, bbox_bottom + vsp, obj_par_block, false, true);


//Corner Collision


Here's what I have for ground collision now. I'm assuming you meant to only check the left side, if the hspeed is negative? Otherwise, I'm not sure how it would work. The ground collision seems to be working really well now and I'm not really getting any glitches. I also had to tell the player to stop, if colliding with walls in the movement code too, as before the hspeed was still slightly greater than 0, even when colliding with walls. The only issue I have now is with slopes. Going up slopes worked pretty well before when the tiles were objects. But now, I changed them to actual tiles, and put 16x16 invisible objects over them, which are what the player collides with. Maybe, they are just too small now?


You are checking 4 collisions there, every step. Checking in all direction all the time is useless, that's what I was getting at.
 

Starturbo

Member
You are checking 4 collisions there, every step. Checking in all direction all the time is useless, that's what I was getting at.
Oh, I think I see what you mean now! Still, I'm not sure how I could avoid that, without writing out collision_rectangle every time. Unless, I made it so the directions checked depends on the hspeed and vspeed, which would still be checking one or two, as long as you are moving.

Update: That just results in an error, but I tried putting the checks in the Create Event, and it still works! I'm assuming if it's in the Create Event then it's not checking it all time, which would take care of that.

Update 2: For some reason, it doesn't work now. As in, you just go through walls. I believe it's because I still had the checks in the move script, but just removed those too.

Update 3: Okay, so I got the checks with hspeed and vspeed to work. Here's what I have for code now:

Code:
Step Event:

//Sets the Nearest Blocks to the Ones the Player is Colliding With

if hsp < 0 {left_block = collision_rectangle(bbox_left + hsp, bbox_top + 1, bbox_left + hsp, bbox_bottom - 1, obj_par_block, false, true);}

if hsp > 0 {right_block = collision_rectangle(bbox_right + hsp, bbox_top + 1, bbox_right + hsp, bbox_bottom - 1, obj_par_block, false, true);}

if vsp < 0 {top_block = collision_rectangle(bbox_left + 1, bbox_top + vsp, bbox_right - 1, bbox_top + vsp, obj_par_block, false, true);}

if vsp > 0 {bottom_block = collision_rectangle(bbox_left + 1, bbox_bottom + vsp, bbox_right - 1, bbox_bottom + vsp, obj_par_block, false, true);}

Collision Script:

//Corner Collision

if hsp < 0 && vsp > 0
{
    if left_block && bottom_block
    {
        hsp = 0;
   
        vsp = 0;
       
        scr_position(left_block.bbox_right + 9, 0, self);
    }
}

if hsp > 0 && vsp > 0
{
    if right_block && bottom_block
    {
        hsp = 0;
   
        vsp = 0;
   
        scr_position(right_block.bbox_left - 8, 0, self);
    }
}

//Horizontal Collision

if hsp < 0
{
    if left_block
    {
        hsp = 0;
   
        scr_position(left_block.bbox_right + 9, 0, self);
    }
}

if hsp > 0
{
    if right_block
    {     
        hsp = 0;
   
        scr_position(right_block.bbox_left - 8, 0, self);
    }
}

x += hsp;

//Vertical Collision

if vsp < 0
{
    if top_block
    {
        vsp = 0;
   
        scr_position(0, top_block.bbox_bottom + (sprite_height / 2), self);
    }
}

if vsp > 0
{
    if bottom_block
    {
        vsp = 0;
   
        scr_position(0, bottom_block.bbox_top - (sprite_height / 2), self);
    }
}

y += vsp;

Movement Script:

//Left

if key_left && !key_right && !collision_rectangle(bbox_left + hsp, bbox_top + 1, bbox_left + hsp, bbox_bottom - 1, obj_par_block, false, true)
{
    image_xscale = -1;

    hsp -= acc;
   
    hsp = max(hsp, -max_hsp);
   
        if hsp > 0 {hsp -= dec;}
}

//Right

if key_right && !key_left && !collision_rectangle(bbox_right + hsp, bbox_top + 1, bbox_right + hsp, bbox_bottom - 1, obj_par_block, false, true)
{
    image_xscale = 1;

    hsp += acc;
   
    hsp = min(hsp, max_hsp);
   
        if hsp < 0 {hsp += dec;}
}

//Decelerates Player if Two or No Keys Are Pressed

if (key_left && key_right) || (!key_left && !key_right)
{
    if hsp < 0 {hsp += dec;}
   
        if hsp > 0 {hsp -= dec;}
       
            if hsp < dec && hsp > -dec {hsp = 0;}
}
The only glitch I've noticed is that when walking off the edge of a block very slowly, you can get stuck in the wall by one or two pixels. You can easily walk out of it, but you can't turn the other way. It's not a game-breaking glitch or anything, but I want to look into fixing it, if possible. That and the slopes.
 
Last edited:

RangerX

Member
Create your variables for checking only if need be. Here's a pseudo code example:

if(hspeed <0)
then
{
LeftBlock=CollisionRectangle on the left of the character since its moving left

Do whatever you do now with "LeftBlock"
}



if(hspeed >0)
then
{
RightBlock=CollisionRectangle on the right of the character since its moving right

Do whatever you do now with "RightBlock"
}
 

Starturbo

Member
Create your variables for checking only if need be. Here's a pseudo code example:

if(hspeed <0)
then
{
LeftBlock=CollisionRectangle on the left of the character since its moving left

Do whatever you do now with "LeftBlock"
}



if(hspeed >0)
then
{
RightBlock=CollisionRectangle on the right of the character since its moving right

Do whatever you do now with "RightBlock"
}
That's sort of what I did, only I didn't put collision_rectangle in the same place where I set speed to 0 and change the position. I guess I could do that though, but I don't see much of a difference. I'm still wondering about the glitch I mentioned in my last post though. I don't know what causes it, but I do know that the !collision_rectangles in the move script are what prevents you from turning the other way in that situation. Though, if I didn't do that in the move script, you would still be going at a low speed when holding left/right and touching walls. Again, that glitch isn't a huge deal, as you can't actually go farther into the ground, and you can get out quite easily. It would be nice to fix though. Also, you go right through slopes now, unless you jump on top of them first. Going down slopes also haven't been coded yet and I don't quite know how I'll do that. Overall, slopes are really glitchy right now, and the code needs to be updated too.
 

Starturbo

Member
Okay, so I'm trying grid collisions now. I believe everything gets added to the grid, but I'm having some trouble with collisions. Here's what I have so far (I only included the bottom left in the collision script because the rest still use collision_rectangle, as of right now):

Code:
Grid Create Script:

//Creates DS Grid

global.grid = ds_grid_create(room_width / 16, room_height / 16);

//Sets Grid Variables

global.ground_i = 0;

Grid Set Script:

//Adds all Wall Objects to Grid

if instance_exists(obj_par_ground) && global.ground_i < instance_number(obj_par_ground)
{
    with (instance_find(obj_par_ground, global.ground_i)) {ds_grid_set(global.grid, instance_find(obj_par_ground, global.ground_i).x, instance_find(obj_par_ground, global.ground_i).y, 1);}
    
    global.ground_i += 1;
}

Collision Script:

//Horizontal Collision

if hsp <= 0
{
    if global.grid[# bbox_left div 16, bbox_bottom div 16]
    {
        hsp = 0;
        
        scr_position(global.grid[# bbox_left div 16, bbox_bottom div 16] + 9, 0, self);
    }
}
 

TheouAegis

Member
Your grid is set ALMOST correctly.

global.grid = ds_grid_create(room_width div 16, room_height div 16)

Just in case you mess up and don't make your room the right size.

The issue is your adding code.

global.ground_i = 0

Just change that to a local variable, not a global.

var ground_i = 0;


if instance_exists(obj_par_ground) && global.ground_i < instance_number(obj_par_ground)

That's wrong. You can't use if, you need to use while.

if instance_exists(obj_par_ground)
while ground_i < instance_number(obj_par_ground)

This could probably be streamlined, but i'll just work with what you have for now.

The next issue is how you're calling the objects.

with instance_find(obj_par_ground, global.ground_i)

That makes all non-local and non-global variable references focus on that particular instance of obj_par_ground, so there's no need to keep calling instance_find over and over.

{ds_grid_set(global.grid, instance_find(obj_par_ground, global.ground_i).x, instance_find(obj_par_ground, global.ground_i).y, 1);

In addition to the instance_find issue previously mentioned, your other issue here is you are not dividing x and y by the "tile size" like you did with room_width and room_height, so the values you are setting are outside the grid.

with instance_find(obj_par_ground, ground_i) ds_grid_set(global.grid, x div 16, y div 16, 1);

Now putting that all together, your creation code should be

global.grid = ds_grid_create(room_width div 16, room_height div 16);
var ground_i = 0;
if instance_exists(obj_par_ground)
while ground_i < instance_number obj_par_ground {
with instance_find(obj_par_ground, ground_i) ds_grid_set(global.grid, x div 16, y div 16, 1);
ground_i++;
}


What does scr_position do? What bothers me (without knowing what the script does), is that you add +9 to bbox_bottom div 16, but you probably want (bbox_bottom+) div 16 instead.
 

Starturbo

Member
Thanks! I'll see what I can do. Also, scr_position is basically a script that re-positions the player. Here's the script itself:

Code:
//Sets x to argument2 if argument0 Does Not Equal 0

if argument0 != 0
{
    (argument2).x = argument0;
}

//Sets y to argument2 if argument1 Does Not Equal 0

if argument1 != 0
{
    (argument2).y = argument1;
}
About the +9 thing, that was basically for when I was using collision_rectangle, since the sprite's origin isn't directly to the left or right, but isn't quite in the center either. I figured out that 9 is the "perfect" value, so you don't appear to be going through walls, nor will there be a space between you and a wall, when touching one. It's kind of hard to explain though. I could try without that too, as maybe it's not needed for grid collisions.

Edit: I tried your code, but it still doesn't seem to work properly. I know a lot of the ground objects are stretched, could that be the issue?
 
Last edited:

TheouAegis

Member
That is one of the issues. Also in scr_position, you are passing the wrong arguments. You are reading the value of global.grid, which is only ever 0 or 1. Event at 9 to that value. So in all cases argument zero will always be greater than zero because it will be either 9 or 10.

As for the ground objects being stretch, you grid generation code does not account for that anywhere. You need to take the whip of the object, divided by 16 comma likewise for the height of the object if it has been scaled vertically at all, and then you will need to set the grid region based on those two values. So the region will be x / 16, y / 16, width / 16, height / 16. (i think region is defined by width and height.)
 

Starturbo

Member
Okay, I changed "with instance_find(obj_par_ground, ground_i) {ds_grid_set(global.grid, x div 16, y div 16, 1);}" to "with instance_find(obj_par_ground, ground_i) {ds_grid_set_region(global.grid, x / 16, y / 16, sprite_width / 16, sprite_height / 16, 1);}" and also replaced the collision code for bbox_right with something similar to that of bbox_left/bbox_bottom. Also, I think I could just not use scr_position, but when I try to set x to bbox_bottom div 16, I just get stuck in one place and I can't move around.
 

TheouAegis

Member
Psedo-code time:

if collision at bbox_bottom {
y -= bbox_bottom mod 16 + 1;​
}

if collision at bbox_right {
x -= bbox_right mod 16 + 1;​
}

if collision at bbox_left {
x += 16 - bbox_left mod 16;​
}

if collision at bbox_top {
y += 16 - bbox_top mod 16;​
}


If bbox_bottom is at 255, then bbox_bottom mod 16 is 15; 255-15 = 240 which is snapped to a grid. But it's still in the next cell over, so it's 255-(15+1) = 239.

If bbox_left is at 114, then bbox_left mod 16 is 2. But what this really means is that bbox_left is actually 14 pixels into the next cell, so the value is subtracted from 16. This will put bbox_left at 128, which is the (hopefully) open cell to the right of the collision.
 

Starturbo

Member
Okay, so I tried something similar, only it sets hspeed to 0 too. Here's the code so far:

Code:
//Horizontal Collision

if hsp <= 0
{
    if bbox_left = global.grid
    {  
        hsp = 0;
   
        x += 16 - bbox_left mod 16;
    }
}

if hsp >= 0
{
    if bbox_right = global.grid
    {  
        hsp = 0;
   
        x -= bbox_right mod 16 + 1;
    }
}
I'm focusing on left and right collisions for now, but once I find something that works, I'll apply it to top and bottom. But anyways, this code didn't work. I know it does do something when colliding with certain walls, but it still doesn't work right at all. I wonder if it has to do with "if bbox_left = global.grid" and "if bbox_right = global.grid", or if it's something else.
 

TheouAegis

Member
global.grid is your data structure. You need to read the cells in the data structure. You had it before, your code was just wrong. You can't just check if one of the bounding box sides is in a Cell, either. You have to check if 1 or more points along that bounding box side are in a solid cell.

if global.grid[# bbox_left div 16, (bbox_bottom-8) div 16] or global.grid# bbox_left div 16, (bbox_bottom-16) div 16] {
 

Starturbo

Member
global.grid is your data structure. You need to read the cells in the data structure. You had it before, your code was just wrong. You can't just check if one of the bounding box sides is in a Cell, either. You have to check if 1 or more points along that bounding box side are in a solid cell.

if global.grid[# bbox_left div 16, (bbox_bottom-8) div 16] or global.grid# bbox_left div 16, (bbox_bottom-16) div 16] {
I tried both of those, but it didn't work. Then again, the bbox_bottom/bbox_top collisions still use collision_rectangle, so perhaps I should change that.

Edit: Just tried doing that, but now I just fall through the ground.

You should read http://www.yoyogames.com/blog/2 if you plan on doing tile-based collision for your levels. I learned a lot from reading this blog post. My current project uses tile-based collision, and I couldn't be happier with the results I got.
Thanks! I don't believe I've ever seen that article before! After reading the article, grid collisions are pretty confusing to me, but I feel it did help. I'm willing to study them more though.
 
Last edited:

Starturbo

Member
Okay, so I've decided I'm going to use collision_rectangle again now. Grid collisions just seem hard to implement and everyone else I talk to recommends I use collision_rectangle. Maybe, I could try grid collisions again in the future though. Anyways, the main problem I'm having with collision_rectangle is with slopes. Specifically, you sometimes "float" one or two pixels above or into a slope. Here's the current code I'm using:

Code:
//Adds hsp and vsp to x and y

x += hsp;

y += vsp;

//Down Slopes

if vsp >= 0
{
    bottom = collision_rectangle(bbox_left + 2, bbox_bottom + 4, bbox_right - 2, bbox_bottom + 4, obj_par_slope, true, true)

    if bottom != noone
    {
        y += 4;
        
        vsp = 0;
    }
}

has_looped = false;

how_many_loops = 1;

//Up Slopes

while collision_rectangle(bbox_left + 2, bbox_bottom, bbox_right - 2, bbox_bottom, obj_par_slope, true, true)
{   
    y -= 1;

    vsp = 0;

    how_many_loops += 1;
    
    if how_many_loops >= 16 {break;}
    
    has_looped = true;
}

if has_looped = true
{
    y += 1;
}

if bottom = noone
{
    return false;
}
    else if bottom != noone
    {
        return true;
    }
    
//Frees from Memory
    
has_looped = 0;

how_many_loops = 0;

bottom = 0;

return false;
Any ideas?
 
Top