GameMaker Range Movement With Collision

Posho

Member
Hello,

I am trying to make an enemy move to a random position within a radius. First the position is set and then it travels in the Step Event. It has to move to a position that is longer than a minimum value and shorter than a maximum value. This is how I went with it:

Define target position:
Code:
var dir = irandom_range(0, 360);

do // First check: the inner circle
{
    dir += 45;
}
until (!raycast_check(x, y, dist_min, dist_min, obj_crate, 1))

var dist = irandom_range(dist_min, dist_max);
var try_x = x + lengthdir_x(dist, dir);
var try_y = y + lengthdir_y(dist, dir);
  
var coords = raycast_coord(x, y, try_x, try_y, obj_crate, 1);

target_x = coords[coord.x_];
target_y = coords[coord.y_];
^ First it sets a random angle and keeps rotating adding 45° until an "obj_crate" isn't in the way of the minimum range, checked using a raycast_check function.
Then it sets a random value between the minimum and maximum ranges and sets a random point towards that direction. And finally, using a raycast_coord function defines a point where the instance would collide with "obj_crate".

Step Event:
Code:
var add = spd * global.delta;
var dist = point_distance(x, y, target_x, target_y);

if (dist < add)
{
    add -= dist;
    action = actions.idle;
}

x += lengthdir_x(add, direction);
y += lengthdir_y(add, direction);
^ Here, the instance just moves towards the point.

Raycast Check
Code:
///raycast_check(x1, y1, x2, y2, collision_object, quality)

var x1 =  argument0;
var y1 =  argument1;
var x2 =  argument2;
var y2 =  argument3;
var obj = argument4;
var qua = argument5; // 1 is most precise, but slowest

var found = false;
var dir = point_direction(x1, y1, x2, y2);
var count=0
while (point_distance(x1, y1, x2, y2) > 1)
{
    if (collision_point(x1, y1, obj, false, true))
        return true;
  
    x1 += lengthdir_x(qua, dir);
    y1 += lengthdir_y(qua, dir);
}

return false;

Raycast Coord
Code:
///raycast_coord(x1, y1, x2, y2, collision_object, quality)

enum coord { x_, y_, z_ };

var x1 =  argument0;
var y1 =  argument1;
var x2 =  argument2;
var y2 =  argument3;
var obj = argument4;
var qua = argument5; // 1 is most precise, but slowest

var found = false;
var dir = point_direction(x1, y1, x2, y2);

while (point_distance(x1, y1, x2, y2) >= 1)
{
    if (collision_point(x1, y1, obj, false, true))
        break;
    
    var xp = x1;
    var yp = y1;
    
    x1 += lengthdir_x(qua, dir);
    y1 += lengthdir_y(qua, dir);
}

var pos;
pos[coord.x_] = xp;
pos[coord.y_] = yp;

return pos;

The code works fine and can walk towards walls without getting stuck, but in some random occasions it will get stuck to infinity during the do/until loop. I assumed it's because it got stuck within the "object_crate" so I tried pushing the instance a bit until it's outside but that still doesn't do the trick.

Any ideas? I've been at this for some hours and I just don't see the problem. Help? :confused:
 
Last edited:
C

Catastrophe

Guest
Well, what confuses me is that your raycast function takes

x,y,x2,y2

and your target function passes in to it

x,y,distance,distance.

I would've expected it to pass something like
x,y, x + lengthdirx(distance,dir), y + lengthdiry(distance,dir)

Which unless I'm misreading it means you'd be raycasting towards some random upperleft room coordinate rather than where you'd want.

You also change dir += 45 but then don't use dir again which will always cause a freeze if the raycast finds something in the first pass: the next dir you are using in raycast is separate I believe because it is "var"-casted making it local to that function. That is, your dir is changing, but you're just running the same code without any changes each turn of the loop, so your while loop would logically never break out since the only thing changed is something that isn't affecting the "until" portion

Either way, it sounds like you can more easily solve this if you use show_debug_message on several variables in your loop, since it sounds like these are incorrect. When the game freezes, you'd be able to see the messages in our output in the IDE continually running and be able to piece it together.

(example: "show_debug_message("testing collision with boxes towards: x1: " + string(x1) + "y1 + " string(y1) + " x2 + " string(x2)" etc)
 
Last edited by a moderator:

Posho

Member
You also change dir += 45 but then don't use dir again which will always cause a freeze if the raycast finds something in the first pass: the next dir you are using in raycast is separate I believe because it is "var"-casted making it local to that function. That is, your dir is changing, but you're just running the same code without any changes each turn of the loop
Whoops! That explains a lot. I just changed it to:
Code:
do // First check: the inner circle
{
    dir += 45;
    show_debug_message(dir)
}
until (!raycast_check(x, y, x+lengthdir_x(dist_min, dir), y+lengthdir_y(dist_min, dir), obj_crate, 1))
But it still gets stuck though. Odd.
 
C

Catastrophe

Guest
Well that's the only major bug I could find. There's a minor issue you should fix in the long run, which is this:


while (point_distance(x1, y1, x2, y2) > 1)

should be


while (point_distance(x1, y1, x2, y2) > qua)

But so far you're only using 1 anyways...

I'd do a show_message_debug on distance_to_object(obj_crate) to see if these are only cases where you're inside the crate that are causing freezes (i.e. when it's 0). If that's not the case, there's some other issue. But yeah this is a pretty debuggable issue most likely if you just keep outputting variables.
 

Posho

Member
Well that's the only major bug I could find. There's a minor issue you should fix in the long run, which is this:


while (point_distance(x1, y1, x2, y2) > 1)

should be


while (point_distance(x1, y1, x2, y2) > qua)
Good headsup. Thanks!

I'd do a show_message_debug on distance_to_object(obj_crate) to see if these are only cases where you're inside the crate that are causing freezes (i.e. when it's 0). If that's not the case, there's some other issue. But yeah this is a pretty debuggable issue most likely if you just keep outputting variables.
I made it so it outputs to the debug each time it does one loop for the do/while IF it is colliding with an obj_crate. Weird thing is that sometimes it will output like 4 messages until it finds its way out with the rotation thing from the beginning, but sometimes it will just get stuck indefinitely. Pretty odd considering the enemy is surrounded by a literal square of obj_crates, which is a lot of space to walk out.
 
C

Catastrophe

Guest
Btw, are you just trying to implement pathfinding with this basically? If so, mp_grid_path is fairly easy to use. I can only see this line of code giving more and more bugs if that's the goal of it.
 

Posho

Member
Btw, are you just trying to implement pathfinding with this basically? If so, mp_grid_path is fairly easy to use. I can only see this line of code giving more and more bugs if that's the goal of it.
Nothing too fancy like pathfinding just yet in terms of movement. I just want an enemy to move towards a random point in a straight line.
I honestly can not believe I'm stuck at something that seems so simple, but here I am lol. :(
 
C

Catastrophe

Guest
Well in any case, I would have a fail condition (or use an 8 directional for loop instead of a while loop), where your object just goes idle if it can't find a correct place to walk. So add i++ and if (i > 7) {break} in your while loop. At least then you'll find out exactly which enemies are bugging out and how it's happening. And you should just leave that in really, an occasional broken enemy is better than your game freezing.
 

Posho

Member
I pretty much ended up fixing it by adding a collision test at every step (which is dumb considering the raycast function is there). Not elegant at all, but works for now and this movement is not going to be that notable anyways.
Code:
var add = spd * global.delta;
var dist = point_distance(x, y, target_x, target_y);

if (instance_place(x+lengthdir_x(add, direction), y+lengthdir_y(add, direction), obj_crate))
{
    action = actions.idle;
    return;
}

if (dist < add)
{
    action = actions.idle;
    add -= dist;
}

x += lengthdir_x(add, direction);
y += lengthdir_y(add, direction);

Well in any case, I would have a fail condition (or use an 8 directional for loop instead of a while loop), where your object just goes idle if it can't find a correct place to walk. So add i++ and if (i > 7) {break} in your while loop. At least then you'll find out exactly which enemies are bugging out and how it's happening. And you should just leave that in really, an occasional broken enemy is better than your game freezing.
Yeah, not a bad idea. I'll just do an 8-iteration loop and then output "ERROR: Instance stuck" just in case this function gives me pain later on.
 
Top