Thursday, January 5, 2017

path finding - GameMaker : how can i get the instance ID of multiple instance in a radius?


GameMaker and coding noob here, need help on something maybe trivial for some of you.


I'm working on the map part of a game. I have a map with spots on it, each spot is an instance of the same object.


I need to draw lines connecting the current spot and the close ones given a maximum distance (100 pixel radius). Also, I need to remove those lines that cross each other, and to keep track of which lines I already traveled.


I made an object ctr_obj that moves (on mouse click) from one spot to another if they are distant less than 100 pixels.


Create Event code :



smp = instance_nearest(x,y,map_spot)
x = smp.x;
y = smp.y;

Draw Event code :


for (i=0; i{
mapSpot[i] = instance_find(map_spot,i);
near[i] = instance_nearest(x,y,map_spot)
if point_distance(mapSpot[i].x,mapSpot[i].y,near[i].x,near[i].y)<100

{
draw_line(mapSpot[i].x,mapSpot[i].y,near[i].x,near[i].y)
}
}

This part works as intended, I got the line drawn between the spot object I'm currently in and the other closest spot.


Ideally, I need to have all the lines of the map drawn all the time, not only when my ctr_obj is on a spot, and to keep track of the already traveled line (by changing its color, for example).


I tried to do something like that directly on the draw event of the map_spot object.


Draw Event code :


for (i=0; i
{
inst[i]= collision_circle(x,y,100,map_spot,false,false);
if inst[i] !=noone
{
inst[i].x +=1000000;
}
}
for (i=0; i{
if inst[i] !=noone

{
inst[i].x-=1000000;
}
}
for (i=0; i{
draw_set_color(c_gray);
draw_line(x, y, inst[i].x, inst[i].y)
if done=1 && inst[i].done=1
{

draw_set_color(c_lime)
draw_set_alpha(0.8)
draw_line(x, y, inst[i].x, inst[i].y)
}
}

It was the only way I found to be able to get the instance_ID of multiple instances of the same object around a point. But as the event run on all map_spot I can't find a way to keep track of everything.


done is a variable to check if I already got on a particular spot. It doesn't really work as intended as if there are three map spots close enough each other I get a green triangle and not just the path used. And some paths don't get draw at all.


as show on the picture here


Can someone give me an hint?



EDIT 1 : So I try the solution, but something is not working. based on the solution provided, this is what I've done :


the script i use :


///scr_find_near(dist,object)

var list = ds_list_create();
with (argument1)
{
if (point_distance(x,y,other.x,other.y) < argument0)
ds_list_add(list,other.id);
}

return list;

the draw_event code of the map_spot object :


closeList = scr_find_near(300,map_spot);
nb_near= ds_list_size(closeList)-1;

for (i = 0; i < nb_near; i += 1)
{
mp[i] = ds_list_find_value(closeList,i);
draw_set_alpha(1);

draw_set_color(c_gray);
draw_line(x,y,mp[i].x,mp[i].y);
info = string("nb of close point"+string(nb_near));
}

the "nb of close point" is just used as a debug for now (i have an "info" tool-tip for debug on all my objects). i try at first with mp as a "normal" variable, not an array, but actually nothing get draw. i can see that each point got the good number of "nb of close point" with the tool-tip, so the ds_list got the good number of info stored in.


but i don't have any line between the map_spot. I'm pretty sure i missed something obvious at this point, i spend too much time on that and can can't see clearly what i need to do. the script you give me work perfectly, but i think i made a mistake in my way of trying to use it to draw the line...


EDIT 2 I continue my trial and error, with the code below on the map_spot draw_event :


with (map_spot){
if point_distance(x,y,other.x,other.y)<300

{
draw_set_color(c_gray)
draw_line(x,y,other.x,other.y);
if (done=1 && other.done=1)
{
draw_set_color(c_lime);
draw_line(x,y,other.x,other.y);
} } }

All the line between close spot are now correctly draw. That's a win. But i got my initial issue with keeping track of witch line have been traveled thru. Obviously, with the code above, all the line between map_spot that have been visited are now green, not just the one i really "used". example



the red number are the order of the map_spot i click on the map. the orange X mark the line that i don't want to be green.


I need to try with a global variable to keep track of the number of movement (i will need to keep track of that for other reason) maybe it can help me draw only the green line i want.


already thx for the help, i'm closer than before to make it work !


EDIT 3 :


I manage to get it to work, not with a DS_grid, i use a simple way (for me at least). I already had an invisible ctr_object that move when i clic on a node where i can go (checking distance) this object "ctr_map_position" had multiple usefull variable for my game map ; the ctr_map_position step event :


if (x != xprevious && y != yprevious) 
{
moveCount+=1;
parSec += 0.1*point_distance(x,y,xprevious,yprevious)
}


with that i keep track of the number of move(moveCount) and the total distance traveled in space (parSec). at each map_spot, i transfert the current moveCount value to a variable named "place" on the map spot, witch tell me the position of the map spot in the journey. (1,2,3,4 etc..) Finally I draw green line only between map_spot with variable 'place' = n and n+1


everything is now working as i intended to, so thx a lot for the help. obviously a ds_grid or an array like you say may be more optimized but i'm still learning with this project, and optimization is not currently my priority ! (even if i try to do think the best way possible)



Answer



I'm not sure about what you're trying to achieve, but I can help with your starting question: how to get the ids of nearby instances, given an object_index to look for.


The idea behind is: given a radius r (in your case, 100 pixels), there may be more than one instance which is distant less than r from a reference object. So, we have two properties to find out the instances we are looking for: their object_index, and their distance from us.


// Look for those instance closer than 100 px
list = ds_list_create();
with (map_spot)
{

if (point_distance(x,y,other.x,other.y) < 100)
ds_list_add(other.list,id);
}

If you put this code into a script file, let's say scrFindNearby, you can call it as a function from GML like this:


scrFindNearby();

By doing this, the instance variable list will store in a ds_list the ids of those instances closer than 100 pixels to the instance that called the function. At this point, you can do whatever you want with them. Also, you can edit the function above to save both id and distance into two separate ds_lists or even better a ds_grid or, if you aren't familiar with data structures, you can use arrays too.


Hope that helps. :)


EDIT 1



I can help with your problem drawing the right colour for lines. I figured out each map_spot has this variable done, which tells us if the spot has been visited before or not, so my answer is based upon this assumption.


Studying a path or a grid to figure out its properties, this is called graph theory. From now on, I'll call your map_spots nodes, and the lines connecting two spots arcs. In the second picture you uploaded, you created the eight nodes in crescent order, so green arcs are drawn between arcs 1 and 2, 2 and 3, and so on to arcs 7 and 8. This is correct, because you traveled to arc n coming from arc n-1, and the connecting line was crossed.


You were expecting gray lines connecting nodes 2 and 4, and nodes 3 and 8. So, why are they green? Easy: you are drawing lines using the wrong criteria. Lines are drawn gray or green depending on the wrong information. Why? Because you are tracking if a node was visited or not, but you need to know whence it was visited too.


Node (map_spot) labeled '4' was visited from node '3', then a green line is drawn. Being node '4' close enough to node '2', then we can draw a line here too. Node '2' was visited from node '1', so node '2' is marked as visited, and so is node '4'. That means the line connecting '2' and '4' should be green, because they both were visited!


This is correct according to the algorithm logic, but we know it's wrong from our perspective: that line should be gray, because nodes '2' and '4' have been visited, sure, but node '4' wan not visited from node '2', and vice versa.


I suggest you to keep the variable done for your map_spot objects, that will help you to keep track of which spots have been actually visited. In addition, you need a proper data structure to keep track of the paths you traveled through or not; each path (or line) can be identified with a starting point and an ending point, as those points are spots in the map. A two-dimensional array, or even better a ds_grid, are fine to hold this kind of information. This variable should belong to a controller object, or you can make it global if you want to.


For example, if you implement correctly a 2D-array to track the lines statuses, you may get something similar to this (based on the second image in your post):


/* Lines status matrix */
lines[0,0] = 1; // Node 1 visited from node 1 (self-connected)
lines[0,1] = 1; // Node 2 visited from node 1

lines[0,2] = -1; // Node 3 can't be visited from node 1
lines[1,2] = 1; // Node 3 visited from node 2
lines[1,3] = 0; // Node 4 not visited from node 2 (as you expected)
...
lines[2,7] = 0; // Node 8 not visited from node 3 (as you expected)

If this data structure is used, your draw code would look like this:


// Draw Event
with (map_spot)
{

if (point_distance(x,y,other.x,other.y) < 300)
{
draw_set_color(c_gray);
draw_line(x,y,other.x,other.y);
if (ctrSpots.lines[index,other.index]==1)
{
draw_set_color(c_lime);
draw_line(x,y,other.x,other.y);
}
}

}

Be careful when dealing with 2D-array or ds_grid indexes, you must always check the max number of instances before proceeding with drawing, and the data structure must be always be updated every time you add or remove a spot.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...