Legacy GM Targeting the Same Object as an Enemy - Need Help!

I am working on a multiplayer Real time strategy game engine, and to simplify the process, I am trying to use the same core object for a playable unit. Basically what I am inquiring about is how to have the unit shoot automatically at an enemy unit, with both units being essentially the same type of object, yet not have each player's armies attack their own soldiers in confusion, because if I do use distance_to_object, then it would end up like this. So if anyone can help me, I would greatly appreciate it.
 

andulvar

Member
Not to discourage you, but an RTS is going to take a lot of complexity to program effectively and the nature of this question likely means this is one of your first games. I'd recommend starting a little less ambitious but hey, it's your time.

You will need each object to have a local variable like "faction" so that you can distinguish forces of another player. When the object is created you should assign the appropriate faction value to that object so it can easily be identified during all future object checks. Something like the following:

Code:
o_soldier = instance_create(x,y,o_soldier_parent)
o_soldier.faction = "Red Player"
and then later on when checking for any objects that do not belong the the red players own objects:
Code:
if(object.faction <> "Red Player")
 
Last edited:
You will need each object to have a local variable like "faction" so that you can distinguish forces of another player. When the object is created you should assign the appropriate faction value to that object so it can easily be identified during all future object checks. Something like the following:

Code:
o_soldier = instance_create(x,y,o_soldier_parent)
o_soldier.faction = "Red Player"
and then later on when checking for any objects that do not belong the the red players own objects:
Code:
if(object.faction <> "Red Player")
I'm not saying this is wrong, and I realize that you were just giving an example, but giving them a boolean (?) value (true / false) is maybe better than a string. If I'm correct - using strings takes a bit more effort for GMS to do, and is a bit slower. With a lot of units that might make a noticeable difference.

Code:
o_soldier = instance_create(x,y,o_soldier_parent)
o_soldier.is_enemy = true;

o_soldier = instance_create(x,y,o_soldier_parent)
o_soldier.is_enemy = false;
@Craig Davidson
Unfortunately the functions provided by GMS don't allow for differences in objects, such as being the same object type but (in your case) having different "allegiances". Something like instance_nearest won't cycle through instances if they are found to be "friendly" - it just literally returns the closest instance regardless of anything else.

So you will want to build an equivalent function. A relatively straightforward way to do this is by using a priority queue, or by looping through the instances.

create_event
Code:
is_enemy = true (or false)
step event
Code:
var max_dist = 5000; // sets a value to compare instance distance to - the amount doesn't really matter, as it is just setting a baseline for comparison
var closest_obj = noone;
var obj, dist;
for (var a = 0; a < instance_number(whatever); a++) // loop to go through all the instance number of object
{
obj = instance_find(whatever, a); // finds each instance of that object
if obj != id // is not self
{
if obj.is_enemy // is enemy
{dist = point_distance(x, y, obj.x, obj.y);
if dist < max_dist // is less than max dist
{
closest_obj = obj;
max_dist = dist; // shrinks max_dist down to current dist. If next objects distance is larger it will not trigger this part. If it's still smaller, then it will
}
}
}
}
// when loop is finished 'closest_obj' should be the nearest enemy instance, and is an id you can then use for the next step. Or it will not have found an "enemy" in range, and will be "noone"
// in which case it should be ignored.
However, doing that for every instance is probably unnecessary. You could instead have a controller object with a ds list. Put the controller object at the top of the instance creation order. Every enemy created afterwards puts its id onto the list. If it's destroyed it takes its id off the list.

Instead of having to go through every instance and see if it is an enemy or not, you would just loop through this one list that is solely enemy id. This reduces the number of checks: no need to check that it's not "self" - it won't be. No need to check if its friendly or not - if it's on the list it's an enemy. You then only need to get the distance.

Code:
var max_dist = 5000;
var closest_obj = noone;
var control_list = controller.enemy_list;
var list_size = ds_list_size(control_list)
var obj, dist;
for (var a = 0; a < list_size; a++) // loop through list
{
obj = control_list [| a]; // gets id from list
dist = point_distance(x, y, obj.x, obj.y);
if dist < max_dist // is less than max dist
{
closest_obj = obj;
max_dist = dist;
}
}
 
Last edited:

FrostyCat

Redemption Seeker
@the_dude_abides:

Don't loop through instances using instance_find(), there's a hidden quadratic time lookup if you do it that way. Use with blocks for linear time performance. One of my articles document how to find a minimum-value instance using with.

Also, priority queues should not be used for finding a single minimum or maximum. That turns a linear time lookup into a linear-logarithmic time lookup. They are only worth it for finding multiple minimums or maximums in series.
 
@the_dude_abides:

Don't loop through instances using instance_find(), there's a hidden quadratic time lookup if you do it that way. Use with blocks for linear time performance. One of my articles document how to find a minimum-value instance using with.

Also, priority queues should not be used for finding a single minimum or maximum. That turns a linear time lookup into a linear-logarithmic time lookup. They are only worth it for finding multiple minimums or maximums in series.
Thanks for the reminder, it is appreciated. But to be honest, I don't understand the technical aspects of that, though after seeing you say it before I have witnessed that WITH is faster.

I suggested using instance_find because of wanting to not check for "self" and also because of wanting to check the "enemy" variable. Whether those two things would slow down the process. Also I personally don't know how every instance using WITH in the same step works - if they are all the same object, and so referencing themselves at the same time, how does that work?
 
Top