Legacy GM how to have an instance seek out the nearest instance of a certain object

D

Davidblackberry1

Guest
Alright, so if I have some npcs that will constantly search for food, is there a specific function to simply have the npc seek out the nearest instance of food? I thought that there was, but now I am not so sure. If there is not a function to directly do what I need, I am guessing that I must use instance_nearest in some way.
 

TheouAegis

Member
Code:
var d = 1000000, t = noone, n;
with obj_food
{
    n = distance_to_object(other.id);
    if n < d
    {
        d = n;
        t = id;
    }
}
if t != noone
    target = t;
 
W

Wraithious

Guest
Code:
var d = 1000000, t = noone, n;
with obj_food
{
    n = distance_to_object(other.id);
    if n < d
    {
        d = n;
        t = id;
    }
}
if t != noone
    target = t;
That's a really nice way to do it! but don't forget to check if obj_food exists and thus define your n variable with a value or GMS will be very angry if there's no food left haha
Code:
var d = 1000000, t = noone, n=0;
if(instance_exists(obj_food))
{
    with obj_food
    {
        n = distance_to_object(other.id);
        if n < d
       {
            d = n;
            t = id;
        }
    }
    if t != noone
        target = t;
}
 

TheouAegis

Member
you don't need to check if any obj_food exists. The safety is hard-coded into with().

Also you can declare a variable without setting it first.

var n;

That is a valid code as long as the next reference to n is a write operation.
 
W

Wraithious

Guest
Ahh I did not know that about with(), that's good to know!
 

TheouAegis

Member
That's why a lot of the benchmark tests between with() and indirect variable referencing were somewhat erroneous.

enemy.visible = 1

is much faster than

with enemy visible = 1

But there are a couple drawbacks with the first code. First, it can crash the program if no instance exists, whereas with will never crash. Second, with the proper check preceding it, the first code is actually slower than with() when there is only one instance present. However, the speed of with() drops off quickly as soon as more than one instance is present, since all you need to do is check if one instance exists to use the indirect variable referencing, but with() will perform the check for each instance. So with() says, "for each instance of the specified object, does it exist in the room?" whereas the indirect variable reference merely requires it to say, "are there any instances of the specified object in the room?" Furthermore, if you know with absolute certainty that an instance of an object will exist, you can safely avoid the instance_exists() check and keep things moving along quickly.

The only serious drawback with the indirect variable referencing is it is only worthwhile for writing to multiple instances or reading from one known [existent] instance. It is useless for reading when the indirect pointer is to an object rather than an instance, whereas with() can be used to read from all instances of a specified object and indirect referencing is nearly useless for reading 6 or more variables (roughly speaking). So indirect referencing is best when writing to multiple instances and with() is best for reading from multiple instances or modifying 5 or more variables from any number of instances. You also need with() to destroy external instances prior to Studio 2.
 
D

Davidblackberry1

Guest
I only use "if statements" and regular "statements", what is this fangled "with" function that everybody seems to be referring to?
 
Code:
var d = 1000000, t = noone, n;
with obj_food
{
    n = distance_to_object(other.id);
    if n < d
    {
        d = n;
        t = id;
    }
}
if t != noone
    target = t;
Basically, TheouAegis' code is saying:
Code:
var d = 1000000, t = noone, n
Create a variable (d) to store a max distance for the object you're searching for and set it to an arbitrarily large number. Create a variable (t) to store the target instance ID inside. Create a variable (n) to store the distance to the object in question.
Code:
with obj_food
   {
       n = distance_to_object(other.id);
Get the distance from the instance you're searching for (in this case, obj_food) to the instance that is doing the searching and store it in n.
Code:
if n < d
    {
        d = n;
        t = id;
    }
If the distance to the object (this is stored in n) is less than the max distance you're searching (this is stored in d) then set d equal to n (so now the max distance (d) is the distance from the searching object to the food (n)). Set t to the instance ID of the food object you've found.
Code:
if t != noone
    target = t;
If t has been set to an ID (in other words, if obj_food has been found within the max searching distance) then set a variable called target to t.

You can then use the target variable outside of this code in anyway you want (i.e. you can use it to make your searcher walk towards the food or whatever).
 
W

Wayfarer

Guest
So indirect referencing is best when writing to multiple instances and with() is best for reading from multiple instances or modifying 5 or more variables from any number of instances. You also need with() to destroy external instances prior to Studio 2.
Just to clarify, if you have something like:
Code:
var newInst = instance_create(x, y, oSomething);
newInst.variable1 = true;
newInst.variable2 = true;
newInst.variable3 = true;
Are you saying that's faster than with() if you're modifying less than 5 (or so) variables?

That's what I normally do when creating instances, but I just want to make sure I'm understanding correctly.
 

TheouAegis

Member
Just to clarify, if you have something like:
Code:
var newInst = instance_create(x, y, oSomething);
newInst.variable1 = true;
newInst.variable2 = true;
newInst.variable3 = true;
Are you saying that's faster than with() if you're modifying less than 5 (or so) variables?

That's what I normally do when creating instances, but I just want to make sure I'm understanding correctly.
Yes. In that case, you know the instance exists (well, it's safe to assume it will actually be created), so no validation needs to be made, in which case the cost of calling the instance's ID over and over and over is buffered enough by the speed savings from not having to validate its existence.

The 5 variable limit is just a rough estimate. The test I did was 8 variables and the numbers were pretty close.
 

Yal

🐧 *penguin noises*
GMC Elder
I only use "if statements" and regular "statements", what is this fangled "with" function that everybody seems to be referring to?
It's a loop that sequentially executes code from the perspective of every object matching a given instance ID, object index, or keyword such as other or all.
 
E

Edmanbosch

Guest
Alright, so if I have some npcs that will constantly search for food, is there a specific function to simply have the npc seek out the nearest instance of food? I thought that there was, but now I am not so sure. If there is not a function to directly do what I need, I am guessing that I must use instance_nearest in some way.
I think that the code would look something like this:
Code:
var nearest_food = instance_nearest(x, y, obj_food);
move_towards_point(nearest_food.x, nearest_food.y, move_speed);
 

TheouAegis

Member
Might want to add a check in there for the distance to the nearest food. It could be on the opposite side of the map, for all instance_nearest() could care. And what if the nearest instance of obj_food has been tapped out? Or what if the NPC can only eat/gather one type of food? Or what if only 6 NPCs can gather from a single food source at a time and there are already 6 NPCs at the nearest food source?

instance_nearest() works fine sometimes, but it has its limitations. The with-cycling method (I forgot what FC called it) is considerably more robust, even if it is a tad slower.
 
E

Edmanbosch

Guest
Might want to add a check in there for the distance to the nearest food. It could be on the opposite side of the map, for all instance_nearest() could care. And what if the nearest instance of obj_food has been tapped out? Or what if the NPC can only eat/gather one type of food? Or what if only 6 NPCs can gather from a single food source at a time and there are already 6 NPCs at the nearest food source?

instance_nearest() works fine sometimes, but it has its limitations. The with-cycling method (I forgot what FC called it) is considerably more robust, even if it is a tad slower.
Doing all that is very simple since instance_nearest gets the id of the closest instance.
If the NPC only has a certain range, then this could be done instead:
Code:
var nearest_food = instance_nearest(x, y, obj_food);
if (distance_to_object(nearest_food) < food_range) {
    move_towards_point(nearest_food.x, nearest_food.y, move_speed);
}
 
Last edited by a moderator:

TheouAegis

Member
Doing all that is very simple since instance_nearest gets the id of the closest instance.
If the NPC only has a certain range, then this could be done instead:
Code:
var nearest_food = instance_nearest(x, y, obj_food);
if (distance_to_object(nearest_food) < food_range) {
    move_towards_point(nearest_food.x, nearest_food.y, move_speed);
}
Okay, so you tackled one single problematic aspect of it. But I addressed more than one issue. What if the nearest food source is in range but already crowded out with maximum occupancy? How will the NPC know to go to the next nearest one? Case in point, mining minerals in StarCraft: Only one SCV can harvest from a mineral patch at a time. So what if an SCV goes to the nearest mineral patch and there is already one there? What's he going to do, queue up? Then production will come to a screeching halt as all the other SCVs arrive to the same mineral patch and queue up. It would be like having 20 port-a-potties at a fair and everyone is standing in line waiting to crap in just one unit. So how do you find the next closest resource? Deactivate each unavailable resource and redo the process again and again until you find one available? At that point the speed of the code drops off drastically.

The instance_nearest() method:
  1. Loop through all instances
  2. Find the nearest one
  3. Test if it meets all necessary conditions beyond object_index
  4. If yes, go to 6; if not, deactivate the instance
  5. Go back to 1
  6. Reactivate all instances
The With loop method:
  1. Loop through all instances
  2. Test if each meets all necessary conditions beyond object_index
  3. Find the nearest one
For a very, very simple game mechanic, instance_nearest() would work fine, but it falls off quickly as soon as you go beyond any requirements other than distance.
 
E

Edmanbosch

Guest
Okay, so you tackled one single problematic aspect of it. But I addressed more than one issue. What if the nearest food source is in range but already crowded out with maximum occupancy? How will the NPC know to go to the next nearest one? Case in point, mining minerals in StarCraft: Only one SCV can harvest from a mineral patch at a time. So what if an SCV goes to the nearest mineral patch and there is already one there? What's he going to do, queue up? Then production will come to a screeching halt as all the other SCVs arrive to the same mineral patch and queue up. It would be like having 20 port-a-potties at a fair and everyone is standing in line waiting to crap in just one unit. So how do you find the next closest resource? Deactivate each unavailable resource and redo the process again and again until you find one available? At that point the speed of the code drops off drastically.

The instance_nearest() method:
  1. Loop through all instances
  2. Find the nearest one
  3. Test if it meets all necessary conditions beyond object_index
  4. If yes, go to 6; if not, deactivate the instance
  5. Go back to 1
  6. Reactivate all instances
The With loop method:
  1. Loop through all instances
  2. Test if each meets all necessary conditions beyond object_index
  3. Find the nearest one
For a very, very simple game mechanic, instance_nearest() would work fine, but it falls off quickly as soon as you go beyond any requirements other than distance.
But both of them gets the nearest one. After that, you can do whatever you want with both of them.
To solve the problem you mention here:
Code:
var nearest_food = instance_nearest(x, y, obj_food);
if (distance_to_object(nearest_food) < food_range && nearest_food.harvesters < 1) {
    move_towards_point(nearest_food.x, nearest_food.y, move_speed);
}
I still don't see how your solution is better since it does the exact same thing as instance_nearest(). They both get the id of the nearest instance.
 

TheouAegis

Member
All you address is when the conditions are ideal. Your code does nothing to address when the conditions are not ideal. Your code says, "if the nearest food is close enough and it has room, go to it." Okay, so then what if the nearest food source doesn't have room? Your code works if it does, but it doesn't work if it doesn't.
 
E

Edmanbosch

Guest
All you address is when the conditions are ideal. Your code does nothing to address when the conditions are not ideal. Your code says, "if the nearest food is close enough and it has room, go to it." Okay, so then what if the nearest food source doesn't have room? Your code works if it does, but it doesn't work if it doesn't.
Then you just need to add an "else" after the if, and then do whatever you want.
 
Then you just need to add an "else" after the if, and then do whatever you want.
They could do as you say and have an ELSE statement, whereby if using instance_nearest gives an unwanted result they then loop through the other objects. TheouAegis is just pointing out that unless you want a very straightforward result, then learning to use WITH will be important. And even in your example - could still be necessary to do so.

I'm unsure of the processing cost, but it may go like this:

1) Check 'instance_nearest' - whatever that costs for GMS to do its check.
2) Test conditions - if not met then it must loop through all the objects anyway
3) Possibly loop through all, including ones already checked by the instance_nearest process - whatever that costs

If the object returned by 'instance_nearest' meets the criteria, then it would be the quickest (least costly) way to do it (but not very precise). If it doesn't then you simply end up duplicating the same check manually, and so you may as well just have checked them all to begin with, to save the extra processing time.

At least - that's what I assume would be happening.
 
E

Edmanbosch

Guest
They could do as you say and have an ELSE statement, whereby if using instance_nearest gives an unwanted result they then loop through the other objects. TheouAegis is just pointing out that unless you want a very straightforward result, then learning to use WITH will be important. And even in your example - could still be necessary to do so.

I'm unsure of the processing cost, but it may go like this:

1) Check 'instance_nearest' - whatever that costs for GMS to do its check.
2) Test conditions - if not met then it must loop through all the objects anyway
3) Possibly loop through all, including ones already checked by the instance_nearest process - whatever that costs

If the object returned by 'instance_nearest' meets the criteria, then it would be the quickest (least costly) way to do it (but not very precise). If it doesn't then you simply end up duplicating the same check manually, and so you may as well just have checked them all to begin with, to save the extra processing time.

At least - that's what I assume would be happening.
I'm not sure what you mean. "instance_nearest" only gets the ID of the nearest instance. It doesn't loop through all objects in the room for checking the conditions it has to meet so that the NPC can follow it.
 
I mean the points the other guys are bringing up are very valid points and pretty much necessary for clean, functional and fast code if you want to do anything at all beyond simply move to a position.
 
E

Edmanbosch

Guest
I mean the points the other guys are bringing up are very valid points and pretty much necessary for clean, functional and fast code if you want to do anything at all beyond simply move to a position.
But what they do is get the id of the nearest instance, which is exactly what instance_nearest does. Not sure how what they're doing is any better.
 
W

Wayfarer

Guest
At least to begin with, I think using instance_nearest() in this situation would be fine. Sure, with() is more flexible and allows for more control, but it could also over-complicate things for the OP.

Having said that, @Davidblackberry1, it's worth trying to understand how with() works on a basic level at least.

In it's simplest terms...
Code:
with (someObject) {
  x += 10;
}
... will move ALL "someObject" instances 10 pixels to the right. :D

Have you managed to get the NPCs to go to the nearest food yet using instance_nearest() (or using the with() method)?
 
M

Monsi

Guest
There was a speed comparison topic somewhere, that found using with() to be extremely slow in comparison to other alternatives. Using with() to check against all of an object kind sounds like a big performance hit. I would use instance_nearest, or keep track of all the instances yourself and iterate through that list.
 
Say the OP is using an mp_grid and pathfinding to go to the "nearest object", as returned by 'instance_nearest'. That command only looks at distance, so it could return an object that is very close, but due to walls of a room and doors etc is actually much farther to reach.

'Instance_nearest' does not have any further refinement based around actual access to the object. So if you wanted the closest object in terms of distance traveled to it, unfettered by other objects like walls etc, then 'instance_nearest' would be useless. You could be standing next to the room that the food is in, only separated by a wall, but the access to the room is way beyond the object itself - the command doesn't know things like that.

You can't cycle through 'instance_nearest', which is why people make scripts for sorting objects....since it only does one thing - show you, without any other considerations, what is closest.

PS:
According to FrostyCat - using WITH is less costly than using a FOR loop going through 'instance_find'.

So if you do have other criteria, beyond just how close the object is, then you will need to go through all the other instances somehow, and WITH is apparently the best way.

If you don't need any other criteria than distance, then 'instance_nearest' will do just fine.
 
Last edited:
E

Edmanbosch

Guest
Say the OP is using an mp_grid and pathfinding to go to the "nearest object", as returned by 'instance_nearest'. That command only looks at distance, so it could return an object that is very close, but due to walls of a room and doors etc is actually much farther to reach.

'Instance_nearest' does not have any further refinement based around actual access to the object. So if you wanted the closest object in terms of distance traveled to it, unfettered by other objects like walls etc, then 'instance_nearest' would be useless. You could be standing next to the room that the food is in, only separated by a wall, but the access to the room is way beyond the object itself - the command doesn't know things like that.

You can't cycle through 'instance_nearest', which is why people make scripts for sorting objects....since it only does one thing - show you, without any other considerations, what is closest.
But you could check to see if a wall is in the way using collision_line. The ways other people are showing do exactly what instance_nearest does.
 

Yal

🐧 *penguin noises*
GMC Elder
This discussion about efficiency is getting a bit heated... to quote that famous programmer guy whose name eludes me, "premature optimization is the root of all evil". Efficiency aside, repeatedly using functions to get IDs (or other values) generally makes your code harder to read and maintain, and IMO that's a bigger concern with the original code than the efficiency... way too many people are content with hacking together illegible messes that eventually become impossible to maintain properly, and especially newbies. I've lost several of my big projects (Gun Princess, Final Columbus) because the codebase became completely impossible to work with, so I'm talking from experience here...
(having to convert it to newer versions of GM that removed execute_string didn't help either, but details)

The most important thing is that code does what you want it to, as long as you don't have a too powerful PC you can count on Moore's Law making it work for a majority of players :p
 
Top