Problem with closest enemy AI

T

TaxFree

Guest
Hi,
Noob coder in need of help here. I'm using one tower defense tutorial AI code as basis of my own AI and I'm facing small problem with it. In the game there are 2 monsters that want to find nearest monster as their target. The code is working fine with only 1 monster, because it's getting the right target with it but when I have other instance of that same monster object, the second monster won't do anything. So basically only one instance of that monster objective is doing the right thing and I can't figure why the code is working like that. Here is the code I'm using and thanks for help if someone can give tips what is causing that. :)

Code:
/// scr_target_closest(target,n)

var e = argument0;          // Enemy to target
var n = argument1;          // Target Position

inrange = ds_list_create(); // Create the "inrange" DS List
dist = ds_list_create();    // Create the "dist" DS List
closest = ds_list_create(); // Create the "closest" Ds List

if (instance_number(e) > 0) {                       // Make sure there are atleast enemies on the screen
    for (var i = 0; i < instance_number(e); i++) {
        var currentinst = instance_find(e, i);      // Get the ID of the current enemy
        enemy[i] = currentinst;                             // Put that enemy in an "enemy" array
    }
   
    for (var i = 0; i < array_length_1d(enemy); i++) {
        var currentinst = instance_find(e, i);                      // Get the ID of the current enemy
        var distance = point_distance(x,y,currentinst.x,currentinst.y);     // Get the distance from the monster to the current enemy
        if (distance <= range) {                                            // Check to see if the enemy is in range
            ds_list_add(inrange, currentinst);                              // Add the enemy in the "inrange" DS List
            ds_list_add(dist, distance);                                    // Add the distance in the "dist" DS List
        }
    }
    ds_list_sort(dist, true);                                               // Sort the DS list in acending order
   
    for (var i = 0; i < array_length_1d(enemy); i++) {
        for (var j = 1; j < array_length_1d(enemy) j++) {                   // Start with i = 1, so it will ignore own id which is also on the list
            var currentinst = ds_list_find_value(inrange, j);               // Get the ID of the current enemy
            var distance = point_distance(x,y,currentinst.x,currentinst.y); // Get the distance of the current enemy
            if (ds_list_find_value(dist, i) == distance) {                  // Check the "dist" DS list for the distance of the current enemy
                ds_list_add(closest, currentinst);                          // If it matches then add the current enemy into the closest array
            }
        }
    }
    if (ds_list_size(inrange) < n) n = ds_list_size(inrange);               // If "n" is greater than the ammount of enemis, set "n" to the number of enemies.
    if (n < 1) n = 1;                                                       // If "n" is less than 1, set "n" to 1.
    target = ds_list_find_value(closest, n - 1);                            // Set the target to the closest enemy plus "n"
    if (is_undefined(target)) target = id;                                  // If there is no enemy at that position, set the target to our own ID
    ds_list_destroy(inrange);                                               // Destroy the "inrange" DS List
    ds_list_destroy(dist);                                                  // Destroy the "dist" DS List
    ds_list_destroy(closest);                                               // Destroy the "closest" DS List
    return target;                                                          // Return the target

} else {
    target = id;                // Set the target to our ID because there are no enemies in range
    ds_list_destroy(inrange);   // Destroy the "inrange" Ds List
    ds_list_destroy(dist);      // Destroy the "dist" DS List
    ds_list_destroy(closest);   // Destroy the "closest" DS List
    return target;              // return the target
}
 

TheouAegis

Member
Um, you created an array of all the enemies, but then you don't use that array anywhere else except to check its length, which kind of defeats the purpose of creating that array. You should only be using instance_find in that first loop, then the rest of the time you should be using enemy[ i ].

Anyway, you sort the dist list, which is fine and all, except that only sorts the dist list, not the instance list. The instance list is still in the same order. Maybe use a ds_grid instead, since that will reorder the whole row when you use ds_grid_sort.
 
I also recommend the use of a ds grid.
Also, you can achieve what you need with half as much code as you've pasted here, and it will be more flexible as you can add additional variables like hitpoints, speed, power etc. and sort on these to get different behaviour.
 
T

TaxFree

Guest
@TheouAegis Thanks for answer. I think I will try to use ds_grid later today if it works better with that. But even if the coding is kinda glumsy can't figure out why it works on one instance and not on the second one.

@Nathan Laing Thanks for answer. Is there any tips you could give to make it shorter and more flexible? Would be cool to have easy possibility to make targeting depending on hitpoints, power etc. Full code is not needed as it might be too much work for you but maybe some pointers that I could try to follow.
 

TheouAegis

Member
It only works on one enemy because you sorted your list that stores all the distances. So the shortest distance is always the first one. Since the shortest distance is the first distance in the list, the first instance in the instances list is the one that gets affected. Like I said, you should use a ds_grid so that both the instances and the distances will be sorted in tandem and then you won't get this error.
 
Sorry for the delay. So, here is a script I wrote to find a strongest, slowest, etc. anything.
You can simply change the sort order to get a "best" or "worst" result, and of course, change the variable you're testing for, i.e. "damage" "speed" etc. It is short and sweet---it just returns the result--what you do with that result in the remainder of your code elsewhere is of course up to you.

Code:
/// Script, Find strongest candidate (object)

var candidate = argument0;

if (instance_number(candidate) > 0)  // Check for the existence of candidates
{
  var number_of_candidates = instance_number(candidate);  // Count the number of candidates
  candidates_alive = ds_grid_create(2, number_of_candidates);  // Create a DS Grid, 2 columns wide by the number of candidates long
  ds_grid_clear(candidates_alive, -1);  // Clear the entire DS Grid with arbitrary value -1

  for (i = 0; i < number_of_candidates; i++)  // For the number of candidates counted...
  {
  var current_candidate = instance_find(candidate, i);  // Get the ID
  ds_grid_set(candidates_alive, 0, i, current_candidate.id);  // Store the ID into column 0, row i
  ds_grid_set(candidates_alive, 1, i, current_candidate.damage_out);  // Store the damage value into column 1, row i [*** this is the object's damage variable ***]
  }

  ds_grid_sort(candidates_alive, 1, false);  // Sort the DS Grid by column 1, in descending order (highest value first)
  strongest_candidate = ds_grid_get(candidates_alive, 0, 0);  // Read column 0, row 0 to get the ID and store this in "strongest_candidate"
  ds_grid_destroy(candidates_alive);  // Destroy the DS Grid
  return strongest_candidate;  // Return the value (ID) of the strongest candidate
}
else
  return noone;  // No candidates exist, so return "noone" (or -4)
 
T

TaxFree

Guest
@TheouAegis Ah ok, really have to learn to use ds_grid then if that's the case. I appreciate your help!

@Nathan Laing Thank you very much for the example code! I bet it helps alot with my game and learning in general. Will give it a better look and test it later today when I get out of work. And the result is only thing I need from this code so seems perfect :)
 
Top