• 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 Tilemap Collisions + Object Collision

xDGameStudios

GameMaker Staff
GameMaker Dev.
I'm implementing a top-down Zelda-like system and initially I though it would be a good idea to use tilemap
collisions (as they are faster) but then I can't use tilemap collision for determine instance collision (with interactable objects) or can I? [if I can, could you please give me some tips on how to do this]

So I think I'll have to use the "old" collision method. Should I use both "object collision" and "tilemap collision" or should I not bother with such an implementation (would the performance be significant?)
 
A

anomalous

Guest
Well, this really depends on game design.
You can use both, tiles for environment and objects for special blocks, etc. No issue with this at all.
However you can still use an object for the one-off interact able items, and let it use a tlie for graphics and collision. If the item moves around, or has any non-simple animation, it may be best to just use an object.

You would check button pressed over tiles, and if the tile id matches a special tile that is say, a chest, you would trigger the open chest code. It's not different than check for collision with chest, and do collision code...you just check vs the chest TILE rather than the mask.

The main advantage is not having to have all those wall objects, and the collision is much faster. But a zelda game isn't going to stress this much anyway IMO.
However, I think staying with tile for tiles, and see if you can do objects for the special tiles, but just have them set a tile for the graphics and collision, so that you have fewer exceptions to worry about.

You will have difficulty doing a collision line using tiles, but there are a few (slow) algorithms floating around that can at least solve that issue if it comes up, and is not used constantly.
 
N

Naota

Guest
Using tilemaps for collision is quite possible - you just have to check the place your object is moving into at both relevant corners of its collision box for the presence of a tile on the collision layer, and if one is found you can respond appropriately. The code for testing this on an object moving downwards looks something like this:
Code:
//Move the object vertically
y += dy;

if (dy > 0) //Moving Downwards
{
   var t1 = tilemap_get_at_pixel(WallMap, bbox_left, bbox_bottom) & tile_index_mask; //Check bottom corners for collision. Store only the tile index mask ("&" bit operator)
   var t2 = tilemap_get_at_pixel(WallMap, bbox_right, bbox_bottom) & tile_index_mask;
   
   if (t1 != 0 || t2 != 0) //If a tile is at either corner, there's a collision.
   {
       //Do whatever you're doing upon a collision. In this case, snap the object to the edge of the colliding square.
       y = ((bbox_bottom & ~(global.WallMapTileHeight-1)) -1) - sprite_bbox_bottom;
   }
}

//WallMap is the index of the tilemap you're using to store collisions.
//bbox_ variables store the location of your object's bounding box in that direction in absolute coordinates. (So bbox_left, bbox_bottom is an object's bottom left corner)
//tile_index_mask and the "&" operator use bitwise operations to get just the tile index from the tile you're checking. If that index isn't 0, then there's a tile there and you're colliding.
If you check on the marketplace, the "YoYo Dungeon" demo uses just such a method (though its 4-directional movement handling is atrocious) if you're looking for an example of how it works.
 

xDGameStudios

GameMaker Staff
GameMaker Dev.
Well, this really depends on game design.
You can use both, tiles for environment and objects for special blocks, etc. No issue with this at all.
However you can still use an object for the one-off interact able items, and let it use a tlie for graphics and collision. If the item moves around, or has any non-simple animation, it may be best to just use an object.

You would check button pressed over tiles, and if the tile id matches a special tile that is say, a chest, you would trigger the open chest code. It's not different than check for collision with chest, and do collision code...you just check vs the chest TILE rather than the mask.

The main advantage is not having to have all those wall objects, and the collision is much faster. But a zelda game isn't going to stress this much anyway IMO.
However, I think staying with tile for tiles, and see if you can do objects for the special tiles, but just have them set a tile for the graphics and collision, so that you have fewer exceptions to worry about.

You will have difficulty doing a collision line using tiles, but there are a few (slow) algorithms floating around that can at least solve that issue if it comes up, and is not used constantly.
Yeah I know creating a hybrid system will be difficult... because when I calculate the collision with instances I normally add movement to the position on the fly.... and I won't be able to do that because I don't know if I have a tile blocking the way.

the same with tiles I would have to check if there is collision and change the velocity but not applying it to the x/y values right away... only editing the velocity values.

my other problem is pushing other objects I can push an obj_pushable... but only if the obj_pushable can move, if its (the obj_pushable) movement is blocked I can't push it.

Then I have ice sliding... OMG.. just thinking of it make my brain stop!

there are a lot of online videos but they all talk about this features independent from each other... joining everything in a system is not that easy xD
 

xDGameStudios

GameMaker Staff
GameMaker Dev.
Using tilemaps for collision is quite possible - you just have to check the place your object is moving into at both relevant corners of its collision box for the presence of a tile on the collision layer, and if one is found you can respond appropriately. The code for testing this on an object moving downwards looks something like this:
Code:
//Move the object vertically
y += dy;

if (dy > 0) //Moving Downwards
{
   var t1 = tilemap_get_at_pixel(WallMap, bbox_left, bbox_bottom) & tile_index_mask; //Check bottom corners for collision. Store only the tile index mask ("&" bit operator)
   var t2 = tilemap_get_at_pixel(WallMap, bbox_right, bbox_bottom) & tile_index_mask;
  
   if (t1 != 0 || t2 != 0) //If a tile is at either corner, there's a collision.
   {
       //Do whatever you're doing upon a collision. In this case, snap the object to the edge of the colliding square.
       y = ((bbox_bottom & ~(global.WallMapTileHeight-1)) -1) - sprite_bbox_bottom;
   }
}

//WallMap is the index of the tilemap you're using to store collisions.
//bbox_ variables store the location of your object's bounding box in that direction in absolute coordinates. (So bbox_left, bbox_bottom is an object's bottom left corner)
//tile_index_mask and the "&" operator use bitwise operations to get just the tile index from the tile you're checking. If that index isn't 0, then there's a tile there and you're colliding.
If you check on the marketplace, the "YoYo Dungeon" demo uses just such a method (though its 4-directional movement handling is atrocious) if you're looking for an example of how it works.
Yes I know that ;) but combining tile collision + object collision it's when it becomes a little hard!!
 
A

anomalous

Guest
Yeah I know creating a hybrid system will be difficult... because when I calculate the collision with instances I normally add movement to the position on the fly.... and I won't be able to do that because I don't know if I have a tile blocking the way.
the same with tiles I would have to check if there is collision and change the velocity but not applying it to the x/y values right away... only editing the velocity values.
I don't see the issue.
If instance collision, { do something}
This instead becomes
if instance collision || tile collision { do something}

Am I wrong in that? Most of my code where I check multiple collision types was pretty straightforward. It was managing them all that was worse, but your case may not be that bad.

there are a lot of online videos but they all talk about this features independent from each other... joining everything in a system is not that easy xD
Isn't that the truth! It's not easy once the systems get big.
 
Y

Yin

Guest
Sorry to bump this thread, but I'm trying to implement exactly this and got as far as being able to push a block around and having it collide with tilemaps and wall objects, but if the player keeps pushing he goes inside the object.

I'm following this tutorial for the object collisions: youtube dot com/watch?v=I-DR-RKMwZw&t=706s

And this is the code I have right now:

Code:
// keyboard controls

move_up = keyboard_check(vk_up);
move_down = keyboard_check(vk_down);
move_left = keyboard_check(vk_left);
move_right = keyboard_check(vk_right);

Code:
// scr_player_movement

input_get(0);

    yy=spd * (move_down - move_up);
    xx=spd * (move_right - move_left);

scr_move_block();

scr_collisions(xx,yy);
Code:
// scr_collisions


// regular, non-tilemap collisions
var xx = argument[0];
var yy = argument[1];

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


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




y+=yy;
// vertical collisions
if(yy<0){
    var c1=tilemap_get_at_pixel(tilemap,bbox_left,bbox_top) & tile_index_mask;
    var c2=tilemap_get_at_pixel(tilemap,bbox_right,bbox_top) & tile_index_mask;
    var objCheck = place_meeting(x,y,fam_Collisions);
    if(c1+c2+objCheck>0){
        y=(((bbox_top+16) & ~15))-spr_bbox_top;
   
      }
   
}

if(yy>0) {
    var c1=tilemap_get_at_pixel(tilemap,bbox_left,bbox_bottom) & tile_index_mask;
    var c2=tilemap_get_at_pixel(tilemap,bbox_right,bbox_bottom) & tile_index_mask;
    var objCheck = place_meeting(x,y,fam_Collisions);
    if(c1+c2+objCheck>0){
        y=((bbox_bottom & ~15)-1)-spr_bbox_bottom;
       
       }
}


x+=xx;
// horizontal collisions


if(xx<0){
    var c1=tilemap_get_at_pixel(tilemap,bbox_left,bbox_top) & tile_index_mask;
    var c2=tilemap_get_at_pixel(tilemap,bbox_left,bbox_bottom) & tile_index_mask;
    var objCheck = place_meeting(x,y,fam_Collisions);
    if(c1+c2+objCheck>0){
        x=((bbox_left+16) & ~15)-spr_bbox_left;
     
    }
}



if(xx>0){
    var c1=tilemap_get_at_pixel(tilemap,bbox_right,bbox_top) & tile_index_mask;
    var c2=tilemap_get_at_pixel(tilemap,bbox_right,bbox_bottom) & tile_index_mask;
    var objCheck = place_meeting(x,y,fam_Collisions);
    if(c1+c2+objCheck>0){
        x=((bbox_right & ~15)-1)-spr_bbox_right;
       
      }
}

Code:
// scr_move_block

// horizontal

if (place_meeting(x+xx, y, obj_push)){
    var block = instance_place(x+xx, y, obj_push);
    with (block) {
        scr_collisions(obj_player.xx/2,0);
    }
    xx /= 2;
}

// vertical

if (place_meeting(x, y+yy, obj_push)){
    var block = instance_place(x, y+yy, obj_push);
    with (block) {
        scr_collisions(0,obj_player.yy/2);
    }
    yy /= 2;
}
I've tried and looked everywhere but I'm not sure why doesn't the player stop walking if he's already got an object colliding with a wall. Any help is appreciated!
 
Y

Yin

Guest
Well, I ended up giving up on the collision code and rewriting it completely. This is the code I have now, it's a bit messy (sorry, I'm very new to GML) but it works. Hope it helps someone!

Code:
//Push Left
if (place_meeting(x-2,y,obj_push)) && (move_left){
    obj_player.isPushingLEFT = true;
    var block = instance_place(x-2,y,obj_push);
    with (block)
    {
    var c1=tilemap_get_at_pixel(tilemap,x-8,y-7) & tile_index_mask;
    var c2=tilemap_get_at_pixel(tilemap,x-8,y+7) & tile_index_mask;
    var objCheck = place_meeting(x-1,y,fam_Collisions);
    if(c1+c2+objCheck=0)
        {
            x -= obj_player.spd/2;
            obj_player.xx /= 2;
        }else{
        obj_player.xx = 0;
        }
    }
}else{
obj_player.isPushingLEFT = false;
}

//Push Right
if (place_meeting(x+2,y,obj_push)) && (move_right){
    obj_player.isPushingRIGHT = true;
    var block = instance_place(x+2,y,obj_push);
    with (block)
    {
    var c1=tilemap_get_at_pixel(tilemap,x+8,y-7) & tile_index_mask;
    var c2=tilemap_get_at_pixel(tilemap,x+8,y+7) & tile_index_mask;
    var objCheck = place_meeting(x+1,y,fam_Collisions);
    if(c1+c2+objCheck=0){
            x += obj_player.spd/2;
            obj_player.xx /= 2;
        }else{
        obj_player.xx = 0;
        }
    }
}else{
obj_player.isPushingRIGHT = false;
}

//Push Up
if (place_meeting(x,y-2,obj_push)) && (move_up){
    obj_player.isPushingUP = true;
    var block = instance_place(x,y-2,obj_push);
    with (block)
    {
    var c1=tilemap_get_at_pixel(tilemap,x-7,y-8) & tile_index_mask;
    var c2=tilemap_get_at_pixel(tilemap,x+7,y-8) & tile_index_mask;
    var objCheck = place_meeting(x,y-1,fam_Collisions);
    if(c1+c2+objCheck=0){
            y -= obj_player.spd/2;
            obj_player.yy /= 2;
        }else{
        obj_player.yy = 0;
        }
    }
}else{
obj_player.isPushingUP = false;
}


//Push Down
if (place_meeting(x,y+2,obj_push)) && (move_down){
    obj_player.isPushingDOWN = true;
    var block = instance_place(x,y+2,obj_push);
    with (block)
    {
    var c1=tilemap_get_at_pixel(tilemap,x-7,y+8) & tile_index_mask;
    var c2=tilemap_get_at_pixel(tilemap,x+7,y+8) & tile_index_mask;
    var objCheck = place_meeting(x,y+1,fam_Collisions);
    if(c1+c2+objCheck=0){
            y += obj_player.spd/2;
            obj_player.yy /= 2;
        }else{
        obj_player.yy = 0;
        }
    }
}else{
obj_player.isPushingDOWN = false;
}
 
Top