Search Unity

Select target from nearest enemy

Discussion in 'Scripting' started by Dragon Rider, Jan 17, 2008.

  1. Dragon Rider

    Dragon Rider

    Joined:
    Jan 17, 2008
    Posts:
    280
    I've been playing around with Unity for a few weeks, and I've cobbled together a rough shooter-type game. I used a slightly modified version of the SentryGun.js script to create auto-cannons. Very cool, but they only lock on to one enemy: the player. This is OK for FPS games but if you need something that will find and destroy enemies in a crowd or army then it simply will not do. I need something that will find the closest and/or most powerful enemy and destroy it.

    I think I should use an array of enemies and select the closest one, but I'm not sure. Any advice is welcome.
     
  2. seon

    seon

    Joined:
    Jan 10, 2007
    Posts:
    1,441
    This is where Tags really shine.

    Make all of your "targets" or enemies use a specific tag, like TARGET.

    then what you do is have your gun do a:

    Code (csharp):
    1. var targets = gameObject.FindGameObjectsWithTag("TARGET");
    this will give you a collection of gameobjects in your level that are fair game to shoot at.

    Then you can do a:

    Code (csharp):
    1.  
    2.  
    3. var mytarget : gameObject;
    4. var maxdistance : int = 1000; // dummy large distance
    5. var maxarmour : int = 0; // dummy armour
    6.  
    7. for (var enemy : GameObject in targets)
    8. {
    9.     //work out which item is the closest and shoot at it, or find out which enemy has the most armour left or the most power and shoot it.
    10.  
    11.      var distance = Vector3.Distance(enemy.transform.position, transform.position);
    12.      if (distance < maxdistance)
    13.           mytarget = enemy;
    14.  
    15. // or something like
    16.  
    17.      var armour = enemy.GetComponent("status").armour;
    18.      
    19.      if (armour > maxarmour)
    20.           mytarget = enemy;
    21.  
    22. }
    These will iterate though your targets and select either the closest target or the target with the highest armour (assuming you have a status script on each target that has an armour value :)

    I hope this helps...
     
  3. Dragon Rider

    Dragon Rider

    Joined:
    Jan 17, 2008
    Posts:
    280
    Thanks, but it doesn't seem to do anything... :?
     
  4. seon

    seon

    Joined:
    Jan 10, 2007
    Posts:
    1,441
    This code is an example.. you need to adapt it to your scripts.. your enemies etc.

    have you tagged all your enemy prefabs?
     
  5. Dragon Rider

    Dragon Rider

    Joined:
    Jan 17, 2008
    Posts:
    280
    Yes, I have tagged all of my enemies. I adapted the code as necessary and I get no errors but it simply doesn't do anything. Here's the entire Cannon script:

    Code (csharp):
    1.  
    2.  
    3. var attackRange = 200.0;
    4. var target : GameObject;
    5. var targets = gameObject.FindGameObjectsWithTag("TARGET");
    6. var muzzle : Transform;
    7.  
    8. function Update(){ 
    9.  
    10.    
    11.    
    12.  
    13. var mytarget : GameObject;
    14. var maxdistance : int = 1000; // dummy large distance
    15. var maxarmour : int = 0; // dummy armour
    16.  
    17. for (var enemy : GameObject in targets)
    18. {
    19.     //work out which item is the closest and shoot at it, or find out which enemy has the most armour left or the most power and shoot it.
    20.  
    21.     var distance = Vector3.Distance(enemy.transform.position, transform.position);
    22.     if (distance < maxdistance)
    23.         mytarget = enemy;
    24.    
    25.     var armour = enemy.GetComponent("status").armour;
    26.     if ( armour )
    27.         if (armour > maxarmour)
    28.             mytarget = enemy;
    29.  
    30. }
    31.     if ( mytarget != null)
    32.         target = mytarget;
    33.    
    34.     if (target == null)
    35.         return;
    36.            
    37.     if (!CanSeeTarget ())
    38.         return;
    39.  
    40.      // Rotate towards target  
    41.     var targetPoint = target.transform.position;
    42.     var targetRotation = Quaternion.LookRotation (targetPoint - transform.position, Vector3.up);
    43.     transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 2.0);
    44.    
    45.     var muzzleRotation = Quaternion.LookRotation (targetPoint - muzzle.position, Vector3.right);
    46.     muzzle.rotation = Quaternion.Slerp(muzzle.rotation, muzzleRotation, Time.deltaTime * 2.0);
    47.    
    48.     // If we are almost rotated towards target - fire one clip of ammo
    49.     var forward = transform.TransformDirection(Vector3.forward);
    50.     var targetDir = target.transform.position - transform.position;
    51.     if (Vector3.AngleBetween(forward, targetDir) * Mathf.Rad2Deg < 10.0)
    52.         BroadcastMessage("Fire");
    53. }
    54.  
    55. function CanSeeTarget () : boolean
    56. {
    57.     if (Vector3.Distance(transform.position, target.transform.position) > attackRange)
    58.         return false;  
    59.     var hit : RaycastHit;
    60.     if (Physics.Linecast (transform.position, target.transform.position, hit))
    61.         return hit.transform == target.transform;
    62.        
    63.     return false;
    64. }
    65.  
    66.  
    67.  
    Please point out any mistakes I made...
     
  6. seon

    seon

    Joined:
    Jan 10, 2007
    Posts:
    1,441
    Code (csharp):
    1.    if ( mytarget != null)
    2.       target = mytarget;
    3.    
    4.    if (target == null)
    5.       return;
    This is repeating itself.... if mytarget is null then target will also be null.

    So, you need to do 2 things...

    1) make sure your code has actually found some targets, or the loop wont run.

    Code (csharp):
    1. function start() {
    2.       var targets = gameObject.FindGameObjectsWithTag("TARGET");
    3.      print ("number of enemies found: "+targets.length);
    4. }
    2) Check that it is finding the targets and checking the distance

    Code (csharp):
    1.  
    2. if (targets.length>0)
    3. {
    4. for (var enemy : GameObject in targets)
    5. {
    6.     //work out which item is the closest and shoot at it, or find out which enemy has the most armour left or the most power and shoot it.
    7.  
    8.      print ("Checking enemy: "+enemy.transform.name);
    9.      var distance = Vector3.Distance(enemy.transform.position, transform.position);
    10.    if (distance < maxdistance)
    11.    {
    12.        maxdistance = distance;
    13.        mytarget = enemy;
    14.        print("My closest selected enemy so far is: "+mytarget.transform.name);
    15.     }
    16. }
    17. }
    18.  

    You need to include debug info to see what your code it doing.

    Also keep in mind that TAGS are case sensitive.
     
  7. Hojiman

    Hojiman

    Joined:
    Jan 19, 2008
    Posts:
    6
    My confusion notwithstanding but regarding the top section of the code here:

    Code (csharp):
    1. var attackRange = 200.0;
    2. var target : GameObject;
    3. var targets = gameObject.FindGameObjectsWithTag("TARGET");
    4. var muzzle : Transform;
    5.  
    6. function Update(){    
    7.  
    shouldn't the call to FindGameObjectsWithTag be inside of Update() instead of before it? Like so..

    Code (csharp):
    1. var attackRange = 200.0;
    2. var target : GameObject;
    3. var muzzle : Transform;
    4.  
    5. function Update(){    
    6.    var targets = gameObject.FindGameObjectsWithTag("TARGET");
    7.  
    Otherwise, it seems as if "targets" will be initialized but never updated so it will never detect when new Targets spawn.

    <Goes back to forum Lurking ... >
     
  8. Dragon Rider

    Dragon Rider

    Joined:
    Jan 17, 2008
    Posts:
    280
    Again, thanks. I made a few more modifications as suggested and it works now. Here's the working code:

    Code (csharp):
    1.  
    2.  
    3. var attackRange = 200;
    4. var target : GameObject;
    5. var muzzle : Transform;
    6.  
    7. function Start() {
    8.      var targets = gameObject.FindGameObjectsWithTag("TARGET");
    9.     print( "Targets found:" +targets.length );
    10. }
    11.  
    12.  
    13. function Update(){ 
    14.  
    15.    
    16. var targets = gameObject.FindGameObjectsWithTag("TARGET");
    17.  
    18. var mytarget : GameObject;
    19. var maxdistance : int = 1000; // dummy large distance
    20. var maxarmour : int = 0; // dummy armour
    21.  
    22.  
    23. if (targets.length>0)
    24. {
    25.     for (var enemy : GameObject in targets)
    26.     {
    27.     //work out which item is the closest and shoot at it, or find out which enemy has the most armour left or the most power and shoot it.
    28.     print ("Checking enemy: "+enemy.transform.name);
    29.     var distance = Vector3.Distance(enemy.transform.position, transform.position);
    30.     if (distance < attackRange)
    31.         target = enemy;
    32.        
    33.     if (target == enemy)
    34.         print("My closest selected enemy so far is: "+target.transform.name);
    35.     }
    36. }
    37.    
    38.     if (target == null)
    39.         return;
    40.            
    41.     if (!CanSeeTarget ())
    42.         return;
    43.  
    44.      // Rotate towards target  
    45.     var targetPoint = target.transform.position;
    46.     var targetRotation = Quaternion.LookRotation (targetPoint - transform.position, Vector3.up);
    47.     transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 2.0);
    48.    
    49.     var muzzleRotation = Quaternion.LookRotation (targetPoint - muzzle.position, Vector3.right);
    50.     muzzle.rotation = Quaternion.Slerp(muzzle.rotation, muzzleRotation, Time.deltaTime * 2.0);
    51.    
    52.     // If we are almost rotated towards target - fire one clip of ammo
    53.     var forward = transform.TransformDirection(Vector3.forward);
    54.     var targetDir = target.transform.position - transform.position;
    55.     if (Vector3.AngleBetween(forward, targetDir) * Mathf.Rad2Deg < 10.0)
    56.         BroadcastMessage("Fire");
    57. }
    58.  
    59. function CanSeeTarget () : boolean
    60. {
    61.     if (Vector3.Distance(transform.position, target.transform.position) > attackRange)
    62.         return false;  
    63.     var hit : RaycastHit;
    64.     if (Physics.Linecast (transform.position, target.transform.position, hit))
    65.         return hit.transform == target.transform;
    66.        
    67.     return false;
    68. }
    69.  
    70.  
    71.  
    This will now select and attack the first enemy within attackRange. When enemy #1 is dead, enemy #2 becomes the target and so on. I will make a few more modifications to see if I can get it to select the nearest one, but I don't know where I'll end up, so...
     
  9. Hojiman

    Hojiman

    Joined:
    Jan 19, 2008
    Posts:
    6
    In the search code here:
    Code (csharp):
    1. if (targets.length>0)
    2. {
    3.    for (var enemy : GameObject in targets)
    4.    {
    5.     //work out which item is the closest and shoot at it, or find out which enemy has the most armour left or the most power and shoot it.
    6.    print ("Checking enemy: "+enemy.transform.name);
    7.    var distance = Vector3.Distance(enemy.transform.position, transform.position);
    8.    if (distance < attackRange)
    9.       target = enemy;
    10.        
    11.    if (target == enemy)
    12.       print("My closest selected enemy so far is: "+target.transform.name);
    13.    }
    14. }
    15.  
    try changing the distance test to find the actual Closest one instead of the last one in the list that's within range:

    Code (csharp):
    1.  
    2. if (targets.length>0)
    3. {
    4.    var curdist : float = AttackRange;
    5.  
    6.    for (var enemy : GameObject in targets)
    7.    {
    8.       //work out which item is the closest and shoot at it, or find out
    9.       //which enemy has the most armour left or the most power and shoot it.
    10.       print ("Checking enemy: "+enemy.transform.name);
    11.       var distance = Vector3.Distance(enemy.transform.position, transform.position);
    12.  
    13.       if (distance < curdist) {
    14.           curdist = distance;
    15.           target = enemy;
    16.       }
    17.        
    18.       if (target == enemy) {
    19.         print("My closest selected enemy so far is: "+target.transform.name);
    20.       }
    21. }
    Thus the code starts at the maximum attack range and only updates the enemy if it's closer than the one it already had selected.

    One problem with this that I can envision is the gun "twitching" between targets that are nearly at the same range. I would imagine that the turret should only switch targets if the current target is killed/despawned or maybe if another target has advanced MUCH closer. You could set a tolerance with
    Code (csharp):
    1. if (distance < (curdist - attackdelta)) {
    or something similar.

    Looks very interesting in any case.

    Cheers.
     
  10. Dragon Rider

    Dragon Rider

    Joined:
    Jan 17, 2008
    Posts:
    280
    Thanks Hojiman, this works great. As it turns out, I don't have any problem with "twitching"... Yet. :) I'll run this through several more tests to make sure though... Again, thanks.
     
  11. rooster2

    rooster2

    Joined:
    Mar 23, 2015
    Posts:
    25
    how would this work if your enemy is set to random so its not always the same target in the same spot?
     
  12. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    This thread is over 10 years old. You may as well try to search for an answer elsewhere online that can answer your question, or post your own thread. :)

    I didn't read the contents of the thread, but from the sounds of your question, you just want to update a dynamic variable for your enemy. Perhaps your spawn script can send the correct reference to your other script (ie: the target ).