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

reversing an enemy path

I'm currently working on a game in which both the player and the enemy ai move on tiles (9x9). I have an enemy currently that moves up and down the tiles freely using two paths as shown below:
path_up_down.png
path_down_up.png

In the game, the player will be able to create various objects and place them on a tile on either the player's side of the screen or the enemy's side. The problem I'm having is simply preventing the enemies from moving on the tile where an object is (such as a ticking bomb the player can place out).
path_problem.png

What I would like for the enemy objects to do is simply move in the opposite direction whenever there is a bomb or other object above them. They should never occupy the same space as the object. I've tried using path_reverse and flip_path, but those didn't work right and from my understanding those functions alter the actual paths themselves, which I don't think is what I need.

Anyway this is the relevant code:
Step Event
Code:
execute code:

COMMENT: Have the virus move if mode is set to 0 after waiting
if wait_countdown is equal to 0
      if mode is equal to 0
      if position_meeting(x, y - 45, player_bomb_obj) is not equal to true
            if position_meeting(x, y + 45, player_bomb_obj) is not equal to true
            if move_pattern is equal to "up_down"
            set the relative path to boomer_path_updown with speed 3 and at the end stop
      if position_meeting(x, y - 45, player_bomb_obj) is not equal to true
            if position_meeting(x, y + 45, player_bomb_obj) is not equal to true
            if move_pattern is equal to "down_up"
            set the relative path to boomer_path_downup with speed 3 and at the end reverse
execute code:

if position_meeting(x, y - 45, player_bomb_obj) ||  position_meeting(x, y + 45, player_bomb_obj)
{
    if path_index == boomer_path_updown || path_index == boomer_path_downup
    {
       var current_path, current_speed, current_position;
       current_path = path_index;
       current_speed = path_speed;
       current_position = path_position;


       path_speed = 0; // Should stop the enemy briefly
       path_reverse( current_path ); // Flip the direction
       path_speed = current_speed;
       
   
    }
}

Begin
By the way, this is what happens when the enemy object touches the bomb object:
path problem 2.png
 

TheouAegis

Member
Why are using paths for something as simple as up and down movement? All you are doing is increasing or decreasing the y-coordinate of the enemy 64 pixels or whatever. That does not require a path, and in fact using a path would be a waste of memory. And as you are finding out, it is also making your Collision detection tricky.
 
Why are using paths for something as simple as up and down movement? All you are doing is increasing or decreasing the y-coordinate of the enemy 64 pixels or whatever. That does not require a path, and in fact using a path would be a waste of memory. And as you are finding out, it is also making your Collision detection tricky.
I thought using a path would make things easier. As you can see I was wrong. Anyway, I removed the paths now and redid some of the code, but it still isn't working.

Code:
COMMENT: Have the monster move if mode is set to 0 after waiting
if wait_countdown is equal to 0
      if mode is equal to 0
      if position_meeting(x, y - 45, player_bomb_obj) is not equal to true
            if position_meeting(x, y + 45, player_bomb_obj) is not equal to true
            if move_pattern is equal to "up_down"
            set the vertical speed to -3
      if position_meeting(x, y - 45, player_bomb_obj) is not equal to true
            if position_meeting(x, y + 45, player_bomb_obj) is not equal to true
            if move_pattern is equal to "down_up"
            set the vertical speed to 3
      if position_meeting(x, y - 45, player_bomb_obj) is equal to true
            reverse vertical direction
      if position_meeting(x, y + 45, player_bomb_obj) is equal to true
            reverse vertical direction
 
B

bojack29

Guest
Two grids would work great here. Maybe even one. Why not check grid values instead of object id's? It would not only make more sense, but it would save you well on any overhead as well. This game is practically begging to be used via grid data structures.
 
Two grids would work great here. Maybe even one. Why not check grid values instead of object id's? It would not only make more sense, but it would save you well on any overhead as well. This game is practically begging to be used via grid data structures.
Well to tell you the truth I've just gotten back into using GM again a month ago and before last month I knew nothing of data structures. I just learned how to use the ds_list and ds_map functions last month. I'll look into that though, but I'm not sure exactly sure how to use it.
 
I still am going to look into the ds_grids like bojack29 suggested, but in the meantime I rewrote some of the code and changed a few things in the enemy and bomb object. Now the enemy object will change directions when moving toward a bomb or stop trying to move if the bomb is in the middle column. However, due to how everything is coded, there are times when the enemy will be moving close to the next tile and then collide with the bomb itself as it is created. This results in the enemy just stuck or stopped on the same panel as the bomb.
path problem 3.png
Here is a bit more of my code so you all can get a better picture of what I'm working with.

In the step event of the enemy object:
(Note: The parent of the bomb object is par_summon_obj).
Code:
execute code:

/// Movement - up & down

// Code to prevent the enemy from trying to move up or down while an object is on a middle panel
    if (instance_exists ( par_summon_obj ) && par_summon_obj.on_enemy_panel == 4) && (on_enemy_panel == 1 || on_enemy_panel == 7) { prevent_move = true; }
    if (instance_exists ( par_summon_obj ) && par_summon_obj.on_enemy_panel == 6) && (on_enemy_panel == 3 || on_enemy_panel == 9) {  prevent_move = true;}
    if (instance_exists ( par_summon_obj ) && par_summon_obj.on_enemy_panel == 5) && (on_enemy_panel == 2 || on_enemy_panel == 8) {  prevent_move = true;}
     // Note: If I ever decide to put a Boomerblade in the middle column at least the code is there (the third line)

// Prevent the monster from moving if prevent_move is set to true  
if prevent_move == true
if mode == 0
if path_index != boomer_attack_path1 || path_index != boomer_attack_path2 // might not need this line
{ vspeed = 0; }

// Have monster move if mode is set to 0 after waiting
if wait_countdown == 0 && mode == 0
{
     // If movement isn't prevented, move up & down as normal
     if prevent_move == false
     {
        if move_pattern == "up_down"  && (!position_meeting(x, y - 51, par_summon_obj )){ vspeed -= 3; } // This object originally moves up first
        if move_pattern == "down_up" && (!position_meeting(x, y + 51, par_summon_obj )) { vspeed = 3; } // This object originally moves down first
     }
}


execute code:

/// New On Panel Coding for the ENEMY SIDE
// This code sets the variable for which panel the enemy is currently on
if (x > 622 && x < 821) && (y > 508 && y < 591) { on_enemy_panel = 1; }

if (x > 822 && x < 1021) && (y > 511 && y < 587) { on_enemy_panel = 2; }

if (x > 1024 && x < 1222) && (y > 514 && y < 586) { on_enemy_panel = 3; }

if (x > 622 && x < 820) && (y >  594 && y < 666) { on_enemy_panel = 4; }

if (x > 824 && x < 1021) && (y > 594 && y < 666) { on_enemy_panel = 5; }

if (x > 1024 && x < 1222) && (y > 595 && y < 666) { on_enemy_panel = 6; }

if (x > 624 && x < 818) && (y > 679 && y < 751) { on_enemy_panel = 7; }

if (x > 824 && x < 1020) && (y > 680 && y < 750) { on_enemy_panel = 8; }

if (x > 1025 && x < 1221) && (y > 680 && y < 751) { on_enemy_panel = 9 }
New collision event with player_bomb_obj:
Code:
Collision Event with object player_bomb_obj:
COMMENT: Reverse movement to prevent monster from moving on panel
if vspeed  is not equal to 0
      if path_index is not equal to boomer_attack_path1
      if path_index is not equal to boomer_attack_path2
      reverse vertical direction
(^ I'm not setting the enemy vspeed to 0 or anything here, so I don't know how the object keeps getting stuck).

New object invisible_border_object collision event:
Code:
Collision Event with object invisible_border_object:
COMMENT: Reverse enemy movement when hitting the invisible border
if vspeed  is not equal to 0
      if path_index is not equal to boomer_attack_path1
      if path_index is not equal to boomer_attack_path2
 
Last edited:

TheouAegis

Member
Well I'm busy right now but taking a quick educated guess, I would surmise that your vspeed is low enough to the point that when the bomb is collided with, the enemy does not have enough time to move away from it, so it reverses its vertical speed, and then on the next step it's still colliding with the bomb so it reverses its speed again.
 
Well I'm busy right now but taking a quick educated guess, I would surmise that your vspeed is low enough to the point that when the bomb is collided with, the enemy does not have enough time to move away from it, so it reverses its vertical speed, and then on the next step it's still colliding with the bomb so it reverses its speed again.
That sounds like that could be the issue.

A somewhat temporary fix I've came up with until someone can help me figure this out is just to do kind of what the Megaman Battle Network games do where they freeze the game temporarily whenever certain objects are created. So what I've done is basically added this code to the enemy that basically "pushes" them up or down depending on where they are in relation to certain panels in the step event:
Code:
/// Temporary fix to prevent enemy from getting stuck
// The following code works, but there could be a more effective way of doing this.
if instance_exists (par_summon_obj)
{
// (Might have to tweak y movement values depending on the enemy object used (assuming this code is reused for similar enemys)
if path_index != boomer_attack_path1 || path_index != boomer_attack_path2 || path_index != boomer_attack_path1_alt_flip || path_index != boomer_attack_path2_alt_flip
if distance_to_object( par_summon_obj) == 0 && (par_summon_obj.on_enemy_panel > 3 && par_summon_obj.on_enemy_panel < 7)// If an object was put in the middle panel & the monster is on top of it
{ 

    if on_enemy_panel == 1 || y < 631 {  y -= 6; } // Move up if stuck between panels 1 or 4 (the top half of panel 4 which I will say is less than 631) with an object
    if on_enemy_panel == 7 || y > 631  {  y += 6; } // Move down if stuck between panels 7 or 4 (bottom half of panel 4 will be  greater than 631)
    if on_enemy_panel == 3 || y < 631 {  y -= 6; } // Move up if stuck between panels 1 or 6 (the top half of panel 6 which I will say is less than 631) with an object
    if on_enemy_panel == 9 || y > 631  {  y += 6; } // Move down if stuck between panels 9 or 6 (bottom half of panel 6 will be  greater than 631)
    if on_enemy_panel == 2 || y < 631 {  y -= 6; } // Move up if stuck between panels 2 or 5 (the top half of panel 5 which I will say is less than 631) with an object
    if on_enemy_panel == 8 || y > 631  {  y += 6; } // Move down if stuck between panels 8 or 5 (bottom half of panel 5 will be  greater than 631)
}

}
Then, deactivate the enemy objects whenever a variable, battle_controller.temporary_pause_game, is set to true. Then set the variable to false once the object is created and re-activate the enemy objects. Sadly though, this doesn't prevent the enemy from getting stuck all the time.
 

TheouAegis

Member
Are you moving the enemy gradually or jumping from tile to tile? Do any enemies actually walk/float to tiles? I thought they all just jumped from one tile to the next.

Is your bounding box of your sprites the same size as the floor tiles?

When the enemy is moving, when it is "snapped" to a tile, save the coordinates of that tile in the variables xstart and ystart (they are built-in variables that you can overwrite). When the enemy collides with something that got thrown into its target tile, just set x=xstart and y=ystart.

You could try putting the project and hold and practice using a collision map instead. You can make an array like

for(var yy=2; yy>-1; yy--)
for(var xx=0; xx<6; xx++)
{
COLMAP[xx,yy] = xx > 2;
}

That will make an array that looks like this:

0 0 0 1 1 1
0 0 0 1 1 1
0 0 0 1 1 1

A value of 0 means it belongs to the player, a value of 1 means it belongs to the enemies.

When something is on a tile, write its object_index to the map like so:

COLMAP[xx,yy] |= object_index+1 << 1;

You'd add 1 to object_index in case the object_index is 0 - if it's 0, nothing will be written, so add 1. The <<1 is so it doesn't conflict with the friendly/enemy floor values, as does the |= operation. You'd find the values for xx and yy by translating the x and y coordinates of the enemy/player/bomb so that they'd line up to the floor tiles. Suppose the grid starts at (128,256) in the room and each floor tiles was 64 pixels wide and 48 pixels high. In that case, you'd find xx and yy like so:

var xx = (x - 128) div 64;
var yy = (y - 256) div 48;

To see if a tile is usable by the enemy (and thus unusable by the player), you just check like so:

if COLMAP[xx,yy] != -1
is_enemytile = COLMAP[xx,yy] & $80000001;

To see if a tile is occupied, you just check like so:

if COLMAP[xx,yy] != -1
is_occupied = COLMAP[xx,yy] >> 1 > 0;

To free up a tile, such as when an enemy moves from one tile to another, is very simple:

if COLMAP[xx,yy] != -1
COLMAP[xx,yy] &= 1;

And going from there, if a bomb or something is in the way, just set the enemy back to its last position.
 
Are you moving the enemy gradually or jumping from tile to tile? Do any enemies actually walk/float to tiles? I thought they all just jumped from one tile to the next.

Is your bounding box of your sprites the same size as the floor tiles?
Sorry it has taken so long for me to reply, been busy all week. To answer your questions....

No, this particular enemy doesn't jump from tile to tile. Some enemies will, and I would think that once I get to coding them I wouldn't have this much of a problem because they would be simply jumping from one tile to the next so it would be as simple as checking if something is in the position that they are trying to move to. Also, my bounding box is set to automatic. You think I should change this moving forward?

When the enemy is moving, when it is "snapped" to a tile, save the coordinates of that tile in the variables xstart and ystart (they are built-in variables that you can overwrite). When the enemy collides with something that got thrown into its target tile, just set x=xstart and y=ystart.
I haven't been snapping the enemy to the tiles. Should I have been doing this from the start with both the player, enemy, and tile objects? (Also, didn't realize you could save variables to the xstart and ystart).

Also, I guess I could try what you suggested and halt the project, but I don't really understand it too well. Its probably just the way you wrote it, but I get the jist of what your trying to tell me for the most part though.

Also, is there a difference between << and just a single <? And I assume this ($80000001) would be an object id?
 
Last edited:

TheouAegis

Member
If you're going to use collision detection for moving across the tiles, then you should have the bounding box be the same as the tiles.

There is a big difference between a<<n and a<n; one multiplies a by 2[sup]n[/sup], the other checks if a is less than n.

You maybe don't need to snap the enemies to the tiles, but MegaMan.EXE did that with almost everything, as far as I can remember. The only times things weren't snapped to the tiles was for attacks. I mean, you don't have to do everything the same way they did, but I'm just saying. Like, I I think if the player threw a bomb as the enemy was moving toward the bomb and the bomb lands on the enemy's tile, the enemy finishes moving to that tile and keeps moving, then moves off the tile. But I haven't played the EXE series in a long time, so my memory is probably off.
 
Haven't got a chance to try out what you suggested with the array, (still don't quite understand it) but I did try the first thing you suggested with the xstart and ystart variables, and it didn't work (perhaps I wrote something wrong). The enemy object just teleports back to the position is started when the room was created.

I modified the step event coding for the movement like you suggested:
Code:
// Have monster move if mode is set to 0 after waiting
if wait_countdown == 0 && mode == 0
{
     // If movement isn't prevented, move up & down as normal
     if prevent_move == false
     {

        if move_pattern == "up_down"  && (!position_meeting(x, y - 51, par_summon_obj ))
        {
             vspeed -= 3;// Move up first
          
        }
        if move_pattern == "down_up" && (!position_meeting(x, y + 51, par_summon_obj ))
        {
            vspeed = 3; // Move down first
    
        }
         if vspeed != 0
         {         
            if on_enemy_panel == 1 { xstart = 722; ystart = 556; }
            if on_enemy_panel == 2 { xstart = 923; ystart = 556; }
            if on_enemy_panel == 3 { xstart = 1124; ystart = 556; }
            if on_enemy_panel == 7 { xstart = 722; ystart = 720; }
            if on_enemy_panel == 8 { xstart = 923; ystart = 720; }
            if on_enemy_panel == 9 { xstart = 1124; ystart = 720; }
        }
    
     }
}
Then I replaced the code that I had previously that pushed the enemy up or down when it came into contact with the bomb with x = xstart and y = ystart:
Code:
if instance_exists (par_summon_obj)
{
if path_index != boomer_attack_path1 || path_index != boomer_attack_path2 || path_index != boomer_attack_path1_alt_flip || path_index != boomer_attack_path2_alt_flip
if distance_to_object( par_summon_obj) == 0 && (par_summon_obj.on_enemy_panel > 3 && par_summon_obj.on_enemy_panel < 7)// If an object was put in the middle panel & the monster is on top of it
{

x = xstart
y = ystart

}

}
I mean, you don't have to do everything the same way they did, but I'm just saying. Like, I I think if the player threw a bomb as the enemy was moving toward the bomb and the bomb lands on the enemy's tile, the enemy finishes moving to that tile and keeps moving, then moves off the tile. But I haven't played the EXE series in a long time, so my memory is probably off.
Yeah, I know. I'm not trying to copy too much from those games since I am trying to make an original game, but I still want the battle system to feel very similar to those games. And I think the battlechip your thinking of is the mini-bomb, which is the one you throw. The one I'm referring to is like the Timebomb, which was placed out and ticks down to 0 then explodes.
 
Last edited:
Top