1. Hey! Guest! The 36th GMC Jam will take place between February 27th, 12:00 UTC - March 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice

GMS 2 Range Movement With Collision

Discussion in 'Programming' started by Posho, Jan 24, 2020.

  1. Posho

    Posho Member

    Joined:
    Jul 23, 2016
    Posts:
    235
    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: Jan 24, 2020
  2. Catastrophe

    Catastrophe Member

    Joined:
    Sep 22, 2019
    Posts:
    265
    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: Jan 24, 2020
  3. Posho

    Posho Member

    Joined:
    Jul 23, 2016
    Posts:
    235
    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.
     
  4. Catastrophe

    Catastrophe Member

    Joined:
    Sep 22, 2019
    Posts:
    265
    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.
     
  5. Posho

    Posho Member

    Joined:
    Jul 23, 2016
    Posts:
    235
    Good headsup. Thanks!

    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.
     
  6. Catastrophe

    Catastrophe Member

    Joined:
    Sep 22, 2019
    Posts:
    265
    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.
     
  7. Posho

    Posho Member

    Joined:
    Jul 23, 2016
    Posts:
    235
    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. :(
     
  8. Catastrophe

    Catastrophe Member

    Joined:
    Sep 22, 2019
    Posts:
    265
    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.
     
  9. Posho

    Posho Member

    Joined:
    Jul 23, 2016
    Posts:
    235
    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);

    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.
     

Share This Page