GameMaker [Solved] how to find instances with certain variable using "instance_number"(or similar functions)?

G

gnemnij

Guest
In my RTS game I want to show the "unit count" variable to player's screen so I need to find those units with the variable "belongsTo" equals to player's id. I am new to GML so I dont know if I can use a simple way to find those instances.

Please help!
 
I'm not sure what you mean regarding matching the players id? But what I would do is have a controller object that keeps track of all important information, and then this can be used to display info to screen, etc. E.g. if your obj_controller has a unit_a variable, then whenever unit_a is created or destroyed, it can update the controller with a obj_controller.unit_a += 1 or -= 1. Then you can keep track of that particular unit number, and repeat for other unit types too.
 
S

Shawn Shipton

Guest
In my RTS game I want to show the "unit count" variable to player's screen so I need to find those units with the variable "belongsTo" equals to player's id. I am new to GML so I dont know if I can use a simple way to find those instances.

Please help!
Is this what you are looking for?

Code:
var num = instance_number(obj_belongingToPlayers);
var plyrsObj = array_create(num, noone);
for (var i = 0; i < num; i++)
{
    plyrsObj[i] = instance_find(obj_belongingToPlayers, i);
    with (plyrsObj[i])
    {
        if (variableInsideObject == "searchCriteria")
        {
            //do something
        }
    }
}
keep in mind that if you need to access a variable outside the object's scope after with(obj_instance) other than locals created with var localvar, you will have to use other.variable.
 
Last edited by a moderator:

FrostyCat

Redemption Seeker
Second time this week I had to cite this article: with block recipe cards

Citing the entry titled "Count the number of instances satisfying a condition" (the condition is belongsTo == other.id assuming that it is run from the player instance)
Code:
var my_units = 0;
with (obj_unit) {
  if (belongsTo == other.id) {
    my_units++;
  }
}
/* Use my_units here */
It's a travesty that mainstream GML education finds it OK to teach people how to work in an instance-based environment, while at the same time neglecting to teach how to batch-handle instances.

Is this what you are looking for?

Code:
var num = instance_number(obj_belongingToPlayers);
var plyrsObj = array_create(num, noone);
for (var i = 0; i < num; i++)
{
    plyrsObj[i] = instance_find(obj_belongingToPlayers, i);
    with (plyrsObj[i])
    {
        if (variableInsideObject == "searchCriteria")
        {
            //do something
        }
    }
}
keep in mind that if you need to access a variable outside the object's scope after with(obj_instance) other than locals created with var localvar, you will have to use other.variable.
I used to believe in for loops in this form until I saw what was going on underneath instance_find(), now I actively argue against it.

Whenever instance_find() is called, it iterates through the list of instances anew to find an instance. In contrast, a with block picks up all instances specified en-passant. Your for loop is covertly a quadratic time lookup, while the equivalent with block is genuinely linear time.
 
S

Shawn Shipton

Guest
Second time this week I had to cite this article: with block recipe cards

Citing the entry titled "Count the number of instances satisfying a condition" (the condition is belongsTo == other.id assuming that it is run from the player instance)
Code:
var my_units = 0;
with (obj_unit) {
  if (belongsTo == other.id) {
    my_units++;
  }
}
/* Use my_units here */
It's a travesty that mainstream GML education finds it OK to teach people how to work in an instance-based environment, while at the same time neglecting to teach how to batch-handle instances.


I used to believe in for loops in this form until I saw what was going on underneath instance_find(), now I actively argue against it.

Whenever instance_find() is called, it iterates through the list of instances anew to find an instance. In contrast, a with block picks up all instances specified en-passant. Your for loop is covertly a quadratic time lookup, while the equivalent with block is genuinely linear time.
Oh wow, I have only started with GMS since last month, I did not know "with" was so powerful; I thought it was only used to reference the obj, that's what I get for just glancing through the GML overview; I will definitely use this in the future and dig deeper in the GML docs looking for other nuggets like this.

I was wondering if there was a better approach to instance_find and there it is, thank you.
 
An even better approach would be to use a ds_list to track the player's units at creation, that way you don't have to constantly be looping through all instances just to find those of a certain team.
 

DukeSoft

Member
An even better approach would be to use a ds_list to track the player's units at creation, that way you don't have to constantly be looping through all instances just to find those of a certain team.
I can vouch for this :) Keep track of the ID's in there. Total amount of player = ds_list_get_size();.
Looping through all our own units is now even faster because you know exactly what instances to speak to.
 
I used to believe in for loops in this form until I saw what was going on underneath instance_find(), now I actively argue against it.

Whenever instance_find() is called, it iterates through the list of instances anew to find an instance. In contrast, a with block picks up all instances specified en-passant. Your for loop is covertly a quadratic time lookup, while the equivalent with block is genuinely linear time.
Source? Why would it be quadratic time if it's simply iterating through a list? If instance_find() maintains it's previous position (similar to how a normal iterator works), then iterating through a list of instances with it, should only be linear as well.

Edit: I've googled around a bit, and I cannot find anything on the efficiency of with vs instance_find. Later today I'll create some tests and see what happens.
 
Last edited:

FrostyCat

Redemption Seeker
Source? Why would it be quadratic time if it's simply iterating through a list? If instance_find() maintains it's previous position (similar to how a normal iterator works), then iterating through a list of instances with it, should only be linear as well.

Edit: I've googled around a bit, and I cannot find anything on the efficiency of with vs instance_find. Later today I'll create some tests and see what happens.
instance_find() does NOT maintain its previous position like an iterator. This is what I meant by "until I saw what was going on underneath".

Here's the benchmarking script:
Code:
///instance_find_vs_with(n)
{
  // Create instances
  var n = argument0;
  repeat (n) {
    instance_create(0, 0, obj_subject);
  }
  show_debug_message("Testing with " + string(n) + " instances.");
  // instance_find()
  var instfind_start, instfind_end;
  instfind_start = get_timer();
  for (var i = 0; i < n; i++) {
    (instance_find(obj_subject, i)).variable = 1;
  }
  instfind_end = get_timer();
  show_debug_message("instance_find(): " + string(instfind_end - instfind_start) + " microseconds");
  // with
  var with_start, with_end;
  with_start = get_timer();
  with (obj_subject) {
    variable = 2;
  }
  with_end = get_timer();
  show_debug_message("with: " + string(with_end - with_start) + " microseconds");
  // Clean instances
  with (obj_subject) {
    instance_destroy();
  }
}
And the damning results after a few trials:
Code:
Testing with 10 instances.
instance_find(): 10 microseconds
with: 2 microseconds
Testing with 100 instances.
instance_find(): 51 microseconds
with: 9 microseconds
Testing with 1000 instances.
instance_find(): 2498 microseconds
with: 101 microseconds
Testing with 10000 instances.
instance_find(): 316088 microseconds
with: 1435 microseconds
The growth pattern of the time consumed by the instance_find() loop clearly suggests quadratic complexity. With for being obviously linear time, the added linear time must come from instance_find() restarting itself. On the other hand, the with block demonstrates standard linear growth.
 
S

Shawn Shipton

Guest
Source? Why would it be quadratic time if it's simply iterating through a list? If instance_find() maintains it's previous position (similar to how a normal iterator works), then iterating through a list of instances with it, should only be linear as well.

Edit: I've googled around a bit, and I cannot find anything on the efficiency of with vs instance_find. Later today I'll create some tests and see what happens.
Without testing for sure myself, I could see where it would be quadratic because you are looping through the list but I believe instance_find is doing another loop through the list of object to find the id.

For each increment through instance_find(obj, id) this is happening, not in GML but you get the point...

Code:
function instance_find(objlist, id)
{
    rtn = null;
    len = array_length(objlist);
    for ( i = 0; i < len; i++)
    {
     if (objlist[i].id = id)
     {
         rtn = objlist[i];
         break;
     }
    }
    return rtn;
}
this turns it quadratic, where with() just loops once.
 
Last edited by a moderator:

Hyomoto

Member
So, this is one of those problems that it's best if you actually 'cache' your results instead of doing a loop every time. For example, and quite specifically, why don't you already know which units belong to the player? Something as simple as maintaining a ds_list or an array of all the instances belonging to the player means when it comes time to count you don't have to waste time checking other player's units, and depending on how you've implemented it, would be as simple as ds_list_size( myUnits ), or something along those lines.

Specifically when an instance is created:
Code:
ds_list_add( myUnits, instance_create( x, y, obj_unit ) )
And when it's destroyed:
Code:
var _myPos = ds_list_find_value( myUnits, id );
ds_list_delete( _myPos )
This is just a simplification of course, but it's really pretty much all there is to it. This is a case where a little preparation saves a lot of processing time later on. Basically you are trading memory for speed, and in this case it has to be well worth it.
 
So, this is one of those problems that it's best if you actually 'cache' your results instead of doing a loop every time. For example, and quite specifically, why don't you already know which units belong to the player? Something as simple as maintaining a ds_list or an array of all the instances belonging to the player means when it comes time to count you don't have to waste time checking other player's units, and depending on how you've implemented it, would be as simple as ds_list_size( myUnits ), or something along those lines.

Specifically when an instance is created:
Code:
ds_list_add( myUnits, instance_create( x, y, obj_unit ) )
And when it's destroyed:
Code:
var _myPos = ds_list_find_value( myUnits, id );
ds_list_delete( _myPos )
This is just a simplification of course, but it's really pretty much all there is to it. This is a case where a little preparation saves a lot of processing time later on. Basically you are trading memory for speed, and in this case it has to be well worth it.
... Basically what I suggested, but much more detailed and with actual code ;)
 
instance_find() does NOT maintain its previous position like an iterator. This is what I meant by "until I saw what was going on underneath".

Here's the benchmarking script:
Code:
///instance_find_vs_with(n)
{
  // Create instances
  var n = argument0;
  repeat (n) {
    instance_create(0, 0, obj_subject);
  }
  show_debug_message("Testing with " + string(n) + " instances.");
  // instance_find()
  var instfind_start, instfind_end;
  instfind_start = get_timer();
  for (var i = 0; i < n; i++) {
    (instance_find(obj_subject, i)).variable = 1;
  }
  instfind_end = get_timer();
  show_debug_message("instance_find(): " + string(instfind_end - instfind_start) + " microseconds");
  // with
  var with_start, with_end;
  with_start = get_timer();
  with (obj_subject) {
    variable = 2;
  }
  with_end = get_timer();
  show_debug_message("with: " + string(with_end - with_start) + " microseconds");
  // Clean instances
  with (obj_subject) {
    instance_destroy();
  }
}
And the damning results after a few trials:
Code:
Testing with 10 instances.
instance_find(): 10 microseconds
with: 2 microseconds
Testing with 100 instances.
instance_find(): 51 microseconds
with: 9 microseconds
Testing with 1000 instances.
instance_find(): 2498 microseconds
with: 101 microseconds
Testing with 10000 instances.
instance_find(): 316088 microseconds
with: 1435 microseconds
The growth pattern of the time consumed by the instance_find() loop clearly suggests quadratic complexity. With for being obviously linear time, the added linear time must come from instance_find() restarting itself. On the other hand, the with block demonstrates standard linear growth.

This is incredibly useful and I wish I would have known this earlier. Was literally doing performance enhancements the other day and this will greatly help. I do not know why this is not common knowledge or at least addressed in the documentation.
 
G

gnemnij

Guest
Thanks guys for your discussion here, I learned alot.

I am new to GML so I can't understand all your discussions but I really appreciate that.
 
Top