Legacy GM [Solved] I need help Putting certain Objects in a list then finding the Nearest one

Status
Not open for further replies.

Evanski

Raccoon Lord
Forum Staff
Moderator
How would I put certain Objects (like only enemies) in a list, then finding the Nearest one in that list?
I've tried lots of ways and I just can't come up with a solution if anyone could help I would appreciate it, Thanks!
 
N

nicoltoons

Guest
@the_dude_abides and @Hyomoto helped me with ds lists so you can try asking them nicely

Try (cos I'm in no means an expert on ds_list)
enemies = ds_list_create()
ds_list_add(enemies,obj_enemy1)
ds_list_add(enemies,obj_enemy2)
and so on

game_end/room_end event
ds_list_destroy(enemies)


//nearest object
inst = instance_nearest(xx,yy, ds_list_find_value(enemies,ds_list_size(enemies)-1)
 
Last edited by a moderator:
L

Linkdeous

Guest
also, i repeat myself everyyy time someone ask for help in the post, but we can't help you if you don't post :

1-Description of the problem
2-Code
3-What you want


please add information the more you can the better it is for other to understand your problem
 
Somebody rang??

EDIT:
As Linkdeous points out there is a specific function for that: instance_nearest

If however you want more control of what is chosen that what that function provides, for example seeing if an enemy is visible and ignoring it if it's not, then using a priority queue is a nice alternative (look them up in the manual under 'data structures')

//step event of a checking object
Code:
with (whatever)
{
if id != other.id
{
dist = point_distance(x,y, other.x, other.y);
if can see other /// not real code here obviously
{
ds_priority_add (other.dist_list, id, dist);  //dist_list wants to have already been created by the checking object
}
}
}
Priority queue' can be ordered based on a value - that value is the distance being entered in by the objects, which are also putting in their id. This allows you to do two values: the distance, and the id of the object that the distance value relates to.

A ds_list doesn't have this functionality, as it only stores one value - unless you do a lot more work with it than this alternative takes. Priority queues have the functionality built in to be used like this. Find the shortest distance, and you can find the id it refers to.
 
Last edited:

Evanski

Raccoon Lord
Forum Staff
Moderator
okay so what im trying to do is have an object (firebat) find the nearest object, but I want it to only find the nearest object that I tell it that it can look for (objects in a list)
then it finds the closest object in that list it can find, goes over to it and does the rest of its code

my code:
Code:
//find nearest enemy
var nearest_enemy = instance_nearest(x, y, [Hopefully the list of objects i tell it, it can find]);


//activate enemys freeze code
nearest_enemy.batkill = 1;

//if not at enemy go to enemy
if !(x = nearest_enemy.x) && !(y = ((nearest_enemy.y)-(sprite_height+10)))
{
     mp_potential_step(nearest_enemy.x,((nearest_enemy.y)-(sprite_height+10)),7,0);
} else {
    //if at enemy but has no path, start path
   if (x = nearest_enemy.x) && (y = ((nearest_enemy.y)-(sprite_height+10))) && (path_index = -1) && (didpath=0)
   {
        //setpath and stop trying to please
        path_start(ph_firebat_spin,7,path_action_stop,0);
        didpath = 1;
   }
         }
 
Last edited:
N

nicoltoons

Guest
@EvanSki have you tried a ds_list yet? It can hold all the enemy objects you want
 
Last edited by a moderator:

hogwater

Member
@the_dude_abides and @Hyomoto helped me with ds lists so you can try asking them nicely

Try (cos I'm in no means an expert on ds_list)
enemies = ds_list_create()
ds_list_add(enemies,obj_enemy1)
ds_list_add(enemies,obj_enemy2)
and so on

game_end/room_end event
ds_list_destroy(enemies)


//nearest object
inst = instance_nearest(xx,yy, ds_list_find_value(enemies,ds_list_size(enemies)-1)
BTW- there is now a destroy/cleanup event where you should destroy/delete your data structures. It activates whenever you would want to destroy things so you don't have to have cleanup code in several different places.
 
N

nicoltoons

Guest
BTW- there is now a destroy/cleanup event where you should destroy/delete your data structures. It activates whenever you would want to destroy things so you don't have to have cleanup code in several different places.
@hogwater it's either room_end/ or game_end. Room end is usually best (for me) when I have a levelled game and Game_end when it's an endless game or a game where I only need the same ds lists throughout
 

Evanski

Raccoon Lord
Forum Staff
Moderator
I'm so sorry I forgot the say this earlier, I'm on game maker studio 1 (because reasons just roll with me)

so I did this:

Code:
enemies = ds_list_create()
ds_list_add(enemies,obj_skeletion)
ds_list_add(enemies,obj_slime)

//nearest object
inst = instance_nearest(x,y, (ds_list_find_value(enemies,ds_list_size(enemies)-1)));
this works for the Slime but
1.It doesn't go to the nearest enemy
2.it cant find the Skeleton ever
 
Last edited:
so I did this:

enemies = ds_list_create()
ds_list_add(enemies,obj_skeletion) // <<<< obj_skeleton!
ds_list_add(enemies,obj_slime)
Yeah, but no...it doesn't work like that :) All the instances of those enemies either need to be looped through and have their id's put into the ds_list, or you can do it like this:

//step event of checking object
Code:
with (obj_skeleton)
{ds_list_add(other.enemies, id);}

with (obj_slime)
{ds_list_add(other.enemies, id);}
Using 'with' accesses every instance of that object when the object name is used, as above. As long as the checking object is not the same as the enemy object being checked you can do it like this. If it was the same object you'd do this:
Code:
with (obj_skeleton)
{if id != other
{ds_list_add(other.enemies, id);}}
'other' is the identity of the instance calling all of those objects to respond. By putting "if id != other" it will ignore itself if its in the same object type that its asking to respond.

However, a ds_list with id's stored on it won't help you - what is the distance? A ds_list with distances stored in it won't help you - what is the id of the instance that distance refers to?

Create a priority queue (look up 'data structures' in the manual) and do this:

//step event of checking object - 'dist_list' is the name of the priority queue (or whatever you want to call it - enemies etc)
Code:
with (obj_skeleton)
{
var dist = point_distance(x,y, other.x, other.y);
ds_priority_add (other.dist_list, id, dist);
}

with (obj_slime)
{
var dist = point_distance(x,y, other.x, other.y);
ds_priority_add (other.dist_list, id, dist);
}

if ds_priority_size(dist_list) > 0
{inst = ds_priority_find_min}
This will put all of those enemies id's into a queue, and then put the distance to them as well. The distance is being set as the value the queue checks, so whichever is closest will be the id that 'inst' gets set to. As I said above - if the checking object is the same as what its checking then you need to do what's up above ^^^^^
 
Last edited by a moderator:

Evanski

Raccoon Lord
Forum Staff
Moderator
Code:
enemies = ds_list_create()
ds_list_add(enemies,obj_skeletion)
ds_list_add(enemies,obj_slime)

dist = ds_priority_create();

with (obj_skeletion)
{
      var dist = point_distance(x,y, other.x, other.y);
      ds_priority_add (other.dist_list, id, dist);
}

with (obj_slime)
{
    var dist = point_distance(x,y, other.x, other.y);
    ds_priority_add (dist_list, id, dist);
}

if ds_priority_size(dist_list) > 0
{inst = ds_priority_find_min(dist)}
and I get <unknown_object>.<unknown variable>(100055, -2147483648) not set before reading it.
at gml_Script_scr_findenemy (line 10) - ds_priority_add (other.dist_list, id, dist);

in the step event of the Firebat (the checking object)
I have it call the script

its obj_skeletion
because I spelled it wrong a long time ago and now its just Skeletion through out all my code *Face-palm*
 
Code:
enemies = ds_list_create()
ds_list_add(enemies,obj_skeletion)
ds_list_add(enemies,obj_slime)

dist = ds_priority_create();

with (obj_skeletion)
{
      var dist = point_distance(x,y, other.x, other.y);
      ds_priority_add (other.dist_list, id, dist);
}

with (obj_slime)
{
    var dist = point_distance(x,y, other.x, other.y);
    ds_priority_add (dist_list, id, dist);
}

if ds_priority_size(dist_list) > 0
{inst = ds_priority_find_min(dist)}
and I get <unknown_object>.<unknown variable>(100055, -2147483648) not set before reading it.
at gml_Script_scr_findenemy (line 10) - ds_priority_add (other.dist_list, id, dist);
mm hmm - calling the priority queue 'dist' when you created it, and having it referred to as 'dist_list' when the other objects are trying to put their information into it...see if you can spot a problem there :) When you realize what doesn't match you will understand why you get an error.

You're going to find coding pretty difficult if you don't pay attention to what you've called things....

You also need to destroy the priority queue at some point. Since you will be constantly using it, or at least quite regularly, I would make it in the create event, and delete it in the destroy event.

If you permanently keep the queue until the object is destroyed it will still need to be cleared after having data put into it. Otherwise it will end up being filled with duplicate information (the old data), and not be correct.
Code:
if ds_priority_size(dist_list) > 0
{inst = ds_priority_find_min(dist);
ds_queue_clear(dist);}
This will empty the queue of information, whilst still keeping it ready for refilling.

Also - why are you still creating a ds_list, even though it's not needed for this process?
 
Last edited by a moderator:

Evanski

Raccoon Lord
Forum Staff
Moderator
Code:
dist_list = ds_priority_create();

//with (obj_skeletion)
if instance_exists(obj_skeletion)
{
       //save a found skeleton with unique id
      var found_skeleton = obj_skeletion.id;

      //save that unique skele with its dist from bat
      var dist = point_distance(x,y, found_skeleton.x, found_skeleton.y);

      //throw it into the ds_priority
      ds_priority_add (dist_list, found_skeleton, dist);
}

//with (obj_slime)
if instance_exists(obj_slime)
{
      var found_slime = obj_slime.id;
      var dist = point_distance(x,y, found_slime.x, found_slime.y);
      ds_priority_add (dist_list, found_slime, dist);
}

//set inst as the closest found thing
if ds_priority_size(dist_list) > 0
{inst = ds_priority_find_min(dist_list)}
so that works..sorta, the bat picks which type of object to go to no matter its distance then picks the closest one of that type of object to fly too
I'm happy with this though but if i could make it so it chooses the closest out of all to fly too that would be perfect (I could probably do this my self with messy if statements )
 

Evanski

Raccoon Lord
Forum Staff
Moderator
Now that I think about it, If i tweaked with this line
var dist = point_distance(x,y, found_slime.x, found_slime.y);

i think the id and x_number are mixing together and this bad
 

TheouAegis

Member
@hogwater Room End and Game End events don't call the destroy events, they just "free" the memory. Unless this was resolved in a recent S2 patch. Seeing as how Evan isn't using S2, though, it's a moot discussion for now. lol

@EvanSki This ain't a coding language where you need to preprend object indexes or instance ids to every variable. I'll only address your skeleton code because you made the same mistakes for the slime too.


with (obj_skeletion)
you actually want with() here, dunno why you got rid of it

if instance_exists(obj_skeletion)
this conditional is inherent inside with()

var found_skeleton = obj_skeletion.id;
this gets the id of one and only one skeleton, not all of them
this line is also pointless if you use with()


var dist = point_distance(x,y, found_skeleton.x, found_skeleton.y);
contrary to your suspicions, this would have been okay if you didn't foul up the previous line

ds_priority_add (dist_list, found_skeleton, dist);
this line is correct


As I said, you can and should use with() in this case. Furthermore, you'd make things a lot simpler if you made the slime and skeleton children of the same parent object (but that's just to save you coding space, it's not necessary).

Code:
with obj_skeletion {
var dist = point_distance(x,y, other.x, other.y);
ds_priority_add(dist_list, id, dist);
}
And that's all you need to do for the skeleton. If you had a parent object, you'd replace obj_skeleton with the parent object and that would make those 3 lines of code apply to ALL enemies that are children of that parent object.
 
Last edited by a moderator:

Evanski

Raccoon Lord
Forum Staff
Moderator
[USER=28930 said:
@EvanSki[/USER] This ain't a coding language where you need to preprend object indexes or instance ids to every variable. I'll only address your skeleton code because you made the same mistakes for the slime too.


with (obj_skeletion)
you actually want with() here, dunno why you got rid of it

if instance_exists(obj_skeletion)
this conditional is inherent inside with()

var found_skeleton = obj_skeletion.id;
this gets the id of one and only one skeleton, not all of them
this line is also pointless if you use with()


var dist = point_distance(x,y, found_skeleton.x, found_skeleton.y);
contrary to your suspicions, this would have been okay if you didn't foul up the previous line

ds_priority_add (dist_list, found_skeleton, dist);
this line is correct


As I said, you can and should use with() in this case. Furthermore, you'd make things a lot simpler if you made the slime and skeleton children of the same parent object (but that's just to save you coding space, it's not necessary).

Code:
with obj_skeletion {
var dist = point_distance(x,y, other.x, other.y);
ds_priority_add(dist_list, id, dist);
}
And that's all you need to do for the skeleton. If you had a parent object, you'd replace obj_skeleton with the parent object and that would make those 3 lines of code apply to ALL enemies that are children of that parent object.
Code:
dist_list = ds_priority_create();

with obj_skeletion {
var dist = point_distance(x,y, other.x, other.y);
ds_priority_add(dist_list, id, dist);
}

with obj_slime {
var dist = point_distance(x,y, other.x, other.y);
ds_priority_add(dist_list, id, dist);
}


//set inst as the closest found thing
if ds_priority_size(dist_list) > 0
{inst = ds_priority_find_min(dist_list)}
and I get this:
Variable obj_skeletion.dist_list(100054, -2147483648) not set before reading it.
at gml_Script_scr_findenemy (line 26) - ds_priority_add(dist_list, id, dist);
 
Last edited by a moderator:

Evanski

Raccoon Lord
Forum Staff
Moderator
Oh, I forgot to put other. before dist_list.

ds_priority_add(other.dist_list, id, dist);
okay so now it works perfectly!

Code:
dist_list = ds_priority_create(); 

with obj_skeletion {
var dist = point_distance(x,y, other.x, other.y);
ds_priority_add(other.dist_list, id, dist);
}

with obj_slime {
var dist = point_distance(x,y, other.x, other.y);
ds_priority_add(other.dist_list, id, dist);
}

//set inst as the closest found thing
if ds_priority_size(dist_list) > 0
{inst = ds_priority_find_min(dist_list)}
Thank you all to who Helped!
 
L

Linkdeous

Guest
So i watched on internet, and it seem a good way to do it is to make a parent to all enemy that would be called something like obj_enemy
Then becasue it's a prent, it would be like a huge object with all enemy inside of it, so you run instance_nearest(x,y,obj_enemy) and there you go
 

Evanski

Raccoon Lord
Forum Staff
Moderator
I know this is really old and youre not meant to bump old topics without real reason but Just wanted to say Thanks to everyone who helped me here
and the product of this help
 
Status
Not open for further replies.
Top