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

GML Having an instance follow a path starting from a specific point in said path.

Fluury

Member
Heya!

I've got an issue I need to deal with, but sadly can't put my finger on on how to tackle it.

I have a path made up of several points. For each of these points, I sometimes spawn an instance on the exact x/y of said point. This works fine.

When the player enters the range of the instance, I'd like the Instance to follow the *absolute* path starting from it's current position. For example, if we had a path made up of 5 points, and I happen to spawn an instance at point 2, I'd like the instance to follow the absolute path starting from point 2 rather than the beginning.

A basic look into the documentation made me familiar with the path_position variable, unfortunately it isn't exactly fit for what I need given it is a normalized value.

Each of the instances are aware at which point in the path they were spawned.

My current, crusty way of doing this is the following:

Code:
path_start(global.level_path,pspeed,path_action_stop,true);
        var path_pos = my_path_point/(path_get_number(global.level_path));
        path_position = path_pos;
This... I guess gives you a very rough idea of where you are, but obviously does not give you the exact position sadly.

If anyone has any ideas or pointers, I'd love to hear them as I've been struggling with fixing this for a while now.
 

Fluury

Member
Update on the issue:

So far I have found two ways of potentially tackling this.

The first one is using binary search to get the specific position of the path point which sadly doesnt work here as the path sometimes intersects itself. The exact thread with the source idea is this: https://forum.yoyogames.com/index.p...th-position-of-a-certain-point-of-path.44297/

Alternatively we have this ancient reddit thread, in which someone simply adds up all the distances of the points and then divides it by the total length of the path: https://www.reddit.com/r/gamemaker/...ext&utm_name=gamemaker&utm_content=t1_fhytihs

The latter seems applicable, however I am still up for hearing other ideas on how to implement this.
 

TheouAegis

Member
I just looped through the path with brute force. lol
Code:
var x1=path_get_point_x(path_index,my_path_point), y1=path_get_point_y(path_index,my_path_point);
for(var i=0,x2,y2; i<1; i+=0.01) {
x2 = path_get_x(path_index,i);
y2 = path_get_y(path_index,i);
if point_distance(x1,y1,x2,y2) < 2 //ideally less than 1, but I was just testing the speed of this
    break;
}
if i>=1 show_debug_message("Distance error")
else {
    x = x2;
    y = y2;
    path_position = i;
}
This actually runs fairly quickly. The only drawback I found with it is it does not work very well with curved paths. I kept getting some points on the path that would work and other points would freeze, even if I increased my distance buffer from 2 to 8. I went back and looked at the path in the IDE and realized the points weren't actually on the path when using a curved path.

Edit: It works with a buffer of 1 when using straight paths, although if you used this method you should test all points on the path to make sure they all work, otherwise you need to slow the rate at which i increases in the loop.

Edit 2: I looked over that guy's code. The concept seems like it should work even for criss-crossing paths. Just remember to run that code at the start of the game, not every time you're trying to work with the path.
 
Last edited:

Fluury

Member
I just looped through the path with brute force. lol
Code:
var x1=path_get_point_x(path_index,my_path_point), y1=path_get_point_y(path_index,my_path_point);
for(var i=0,x2,y2; i<1; i+=0.01) {
x2 = path_get_x(path_index,i);
y2 = path_get_y(path_index,i);
if point_distance(x1,y1,x2,y2) < 2 //ideally less than 1, but I was just testing the speed of this
    break;
}
if i>=1 show_debug_message("Distance error")
else {
    x = x2;
    y = y2;
    path_position = i;
}
This actually runs fairly quickly. The only drawback I found with it is it does not work very well with curved paths. I kept getting some points on the path that would work and other points would freeze, even if I increased my distance buffer from 2 to 8. I went back and looked at the path in the IDE and realized the points weren't actually on the path when using a curved path.

Edit: It works with a buffer of 1 when using straight paths, although if you used this method you should test all points on the path to make sure they all work, otherwise you need to slow the rate at which i increases in the loop.

Edit 2: I looked over that guy's code. The concept seems like it should work even for criss-crossing paths. Just remember to run that code at the start of the game, not every time you're trying to work with the path.
Nice idea! However I don't fully know what you meant with your Edit 2. The way I understand is that this gives me the normalized value for a specific point - so clearly I should run his script every single time I'd , in my case, start an instance following the path on a specific point of it.
 

TheouAegis

Member
No, you would have to edit his code. Use his code essentially to populate a global array with all of the path_positions of each point either at game start or room start and reference the array as needed.
 

Fluury

Member
No, you would have to edit his code. Use his code essentially to populate a global array with all of the path_positions of each point either at game start or room start and reference the array as needed.
Maybe I am misunderstanding or forgetting something essential but why would I do that instead of calling the script for each point individually?

As I said, the way I understand it is that this script gives me the path_position of a specific point I give to it - that's the only value it returns. So in my situation, if I had a path of 5 points with point 2 and 3 having an instance which I want to start moving starting from their current point position, I'd just call the script when they start the path once for each instance and that's it, no?

Of course I could make an array as you suggested, but that would give me path_positions for points I don't even need, thus executing the script more often than needed.

Mind you in the original script of this guy I changed the n to path_index, as that was something that put me off.

Perhaps I haven't explained my issue in great enough detail?
 

TheouAegis

Member
Because unless you are generating New paths every minute, it's redundant. What is the point of calculating the length of the path n times when the past isn't going to change? the only part in that script that you would need to call over and over and over is drawing circles at each of the points, which you don't need to do because that is not relevant to what you are trying to do (I don't even know why he put that in his code because there's no reason for him to call his code every single step like that). If these are going to be one-off paths, then fine. But if the path isn't going to change, if it's defined in the IDE or at the start of the room, then calling that script n times over t steps would be ridiculous. His code, if handled properly, will be much, much, much faster than my code.

Code:
///path_get_point_position(path,point index);
var path,dist_all,dist_point,array;
path = argument0;
dist_all = 0;
dist_point = 0;
point_index = argument1;
array = 0;
for(var i=0,d=0,n=path_get_number(path);i<n;i++){
 if i!=0{
   d = point_distance(path_get_point_x(path,i),path_get_point_y(path,i),path_get_point_x(path,i-1),path_get_point_y(path,i-1));
   dist_all+=d;
   dist_point+=d;
   }
 array[i]=dist_point;
}
for(i=0;i<n;i++)
 array[i]/=dist_all;
return array;
I fixed up a couple typos in his code, and made it work as a one-time script call (i think, i'm on my phone). call that script and save the results into a variable and that variable you save into will be an array of all the path_position for each point along the path.
 

Fluury

Member
Because unless you are generating New paths every minute, it's redundant. What is the point of calculating the length of the path n times when the past isn't going to change? the only part in that script that you would need to call over and over and over is drawing circles at each of the points, which you don't need to do because that is not relevant to what you are trying to do (I don't even know why he put that in his code because there's no reason for him to call his code every single step like that). If these are going to be one-off paths, then fine. But if the path isn't going to change, if it's defined in the IDE or at the start of the room, then calling that script n times over t steps would be ridiculous. His code, if handled properly, will be much, much, much faster than my code.

Code:
///path_get_point_position(path,point index);
var path,dist_all,dist_point,array;
path = argument0;
dist_all = 0;
dist_point = 0;
point_index = argument1;
array = 0;
for(var i=0,d=0,n=path_get_number(path);i<n;i++){
 if i!=0{
   d = point_distance(path_get_point_x(path,i),path_get_point_y(path,i),path_get_point_x(path,i-1),path_get_point_y(path,i-1));
   dist_all+=d;
   dist_point+=d;
   }
 array[i]=dist_point;
}
for(i=0;i<n;i++)
 array[i]/=dist_all;
return array;
I fixed up a couple typos in his code, and made it work as a one-time script call (i think, i'm on my phone). call that script and save the results into a variable and that variable you save into will be an array of all the path_position for each point along the path.
Yub, I was being a dummie. I get your point now given going through the entire path will very much give you all the positions you need after all.
Thanks for taking the time to explain that to me and brushing up the code!
 
Top