How to: Grid-Based Enemy-on-Enemy Collision?

B

Brackleforth

Guest
Hello,

I've been following along with HeartBeast's random level generation tutorials, and completed all 7 of them. Afterwards, I adjusted the code to have grid-based movement, so my character moves in a 32 x 32 grid. I want to make a roguelike.

The issue I'm running into is how enemies should collide with each other when they are bunched up trying to reach the player object. The enemies move one unit on the grid, towards the player, every time the player moves. I put that feature in, but the collisions are difficult, since two enemies generally collide with each other in the same moment in the same spot, but they need to have different reactions.


Here is the code I have at my disposal - pretty much directly from HeartBeast's tutorials:

Here is scr_move:

Code:
/// move(hspd, vspd)
var hspd = argument[0];
var vspd = argument[1];

// Horizontal collisions
if (scr_grid_place_meeting(x+hspd, y)) {
    while (!scr_grid_place_meeting(x+sign(hspd), y)) {
        x+=sign(hspd);
        }
        hspd = 0;
}
// Move horizontally
x+=hspd;

// Vertival collisions
if (scr_grid_place_meeting(x, y+vspd)) {
    while(!scr_grid_place_meeting(x, y+sign(vspd))) {
        y+=sign(vspd);
        }
        vspd = 0;
}

// Move vertically
y+=vspd;

Here is scr_grid_place_meeting:

Code:
///grid_place_meeting(x, y)

var xx = argument[0];
var yy = argument[1];

// Remember out position
var xp = x;
var yp = y;

// Update the position for the bbox calculations
x = xx;
y = yy;

// Check for x meeting
var x_meeting = (obj_level.grid[# bbox_right div CELL_WIDTH, bbox_top div CELL_HEIGHT] != FLOOR) ||
                (obj_level.grid[# bbox_left div CELL_WIDTH, bbox_top div CELL_HEIGHT] != FLOOR);
// Check for y meeting          
var y_meeting = (obj_level.grid[# bbox_right div CELL_WIDTH, bbox_bottom div CELL_HEIGHT] != FLOOR) ||
                (obj_level.grid[# bbox_left div CELL_WIDTH, bbox_bottom div CELL_HEIGHT] != FLOOR);
               
var center_meeting = obj_level.grid[# xx div CELL_WIDTH, yy div CELL_HEIGHT] != FLOOR;
               
// Move back
x = xp;
y = yp;

// Return true or false

return x_meeting || y_meeting || center_meeting;

Here is scr_get_path_to_player:

Code:
/// get_path_to_player()
if keyboard_check_pressed(vk_left) or keyboard_check_pressed(vk_right) or keyboard_check_pressed(vk_up) or keyboard_check_pressed(vk_down)
{
if (instance_exists(obj_player)) {
    var xx = (obj_player.x div CELL_WIDTH)*CELL_WIDTH+CELL_WIDTH/2;
    var yy = (obj_player.y div CELL_HEIGHT)*CELL_HEIGHT+CELL_HEIGHT/2;

    if (mp_grid_path(obj_level.grid_path, path, x, y, xx, yy, false)) {
        path_start(path, 32, path_action_stop, false);
        alarm[0] = 1;
    }
}
}

... in which alarm 0 simply has this...

Code:
path_end();


I appreciate any help. This appears to be a troublesome thing that lots of people have run into with limited success.
 

Paskaler

Member
If your enemies are only ever moving in 32 "jumps", there's no need for most of the collision checks you do in the scr_grid_place_meeting. You could just get the sign of hspd and vspd, and use that as an offset, something like this:

Code:
var check_x = (x div CELL_WIDTH) + sign(hspd);
var check_y = (y div CELL_HEIGHT) + sign(vspd);
If there is a FLOOR at that location, move the enemy and mark it as SOLID(I don't know the macro name you used here). You should also set the tile at the current enemy's location to FLOOR again so others can occupy the tile.
 
B

Brackleforth

Guest
Thanks for the quick reply.
I used the macro WALL to represent solid walls, but that's just for the terrain generation's ds_grid, so the places with a WALL value should never change.
The enemy pathfinding is an mp_grid unique from the terrain's grid.

Since I'm using an mp_grid_path to handle the enemy pathfinding, I'm not sure how exactly to tell my enemy objects to check the tiles around them before moving, then update the tile they're on. But if I understand it right, you use mp_grid_add_cell and mp_grid_clear cell? Or maybe you use mp_grid_add_instances? The code that moves the enemy is scr_get_path_to_player.

I've tried a few things, but the issue I run into is that all of the enemies move at the same time, resulting in the enemies that are following immediately behind another enemy being stuck instead of moving when the one in front of it moves, since they check for collision at the same time.

To represent what I mean... p is the player object, x is a space, and o is an enemy:
Step 0: pxoo
Step 1: poxo

What I wanted for Step 1 is this: poox

Is there, like, a for-loop or something I can use to update the enemy's locations one at a time according to their proximity to obj_player...? And if the proximity is the same, maybe it could use id? This might not be necessary. Please let me know what you think would work best.
 
Top