Search Unity

ArgumentOutofRangeException when trying to find nearest object in an array

Discussion in 'Scripting' started by Sajid, Mar 4, 2015.

  1. Sajid

    Sajid

    Joined:
    Mar 12, 2011
    Posts:
    199
    Hey all.

    I have a method in a class for a game I'm working on that is supposed to find the closest enemy on the map and return it as a GameObject. Here is the method:
    Code (CSharp):
    1.     public GameObject findClosest()
    2.     {
    3.         float tempDistance = 0;
    4.         for(int i = enemiesInScene.Count; i > 0; i--)
    5.         {
    6.             if(enemiesInScene[i] != null)
    7.             {
    8.                 if(Vector3.Distance(transform.position,
    9.                                     enemiesInScene[i].transform.position) <
    10.                    Vector3.Distance(transform.position,
    11.                                     enemiesInScene[i - 1].transform.position))
    12.                 {
    13.                     tempDistance = Vector3.Distance(transform.position,
    14.                                                     enemiesInScene[i].transform.position);
    15.                 }
    16.             }        
    17.         }
    18.  
    19.         for(int j = 0; j < enemiesInScene.Count; j++)
    20.         {
    21.             if (Vector3.Distance(transform.position,
    22.                                  enemiesInScene[j].transform.position) <= range)
    23.             {
    24.                  if(Vector3.Distance(transform.position, enemiesInScene[j].transform.position) == tempDistance)
    25.                 {
    26.                     return enemiesInScene[j];
    27.                 }
    28.             }
    29.                
    30.          
    31.         }
    32.  
    33.         return null;
    34.     }
    It compiles fine, but I get this error when I actually get in game:
    Code (Boo):
    1. ArgumentOutOfRangeException: Argument is out of range.
    2. cs/class/corlib/System.Collections.Generic/List.cs:633)
    3. turretHandlerRevised.findClosest () (at Assets/turretHandlerRevised.cs:92)
    4. turretHandlerRevised.Update () (at Assets/turretHandlerRevised.cs:29)Parameter name: index
    5. System.Collections.Generic.List`1[UnityEngine.GameObject].get_Item (Int32 index) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/m

    I've scoured the code and I don't understand where my error is. Anyone have an idea?

    Thanks.
     
  2. Random_Civilian

    Random_Civilian

    Joined:
    Nov 5, 2014
    Posts:
    55
    Line 4. It should be count-1. And >=0
     
    Kiwasi likes this.
  3. Sajid

    Sajid

    Joined:
    Mar 12, 2011
    Posts:
    199
    It seemed to work after changing that, but now it doesn't work if I have more than 2 enemies in my scene.

    Also, even if I have two, once one is destroyed it all breaks as well.

    When I add 3 or more enemies to my scene I keep getting arguementOutOfRangeExceptions, but not if I add only two.
     
  4. Sajid

    Sajid

    Joined:
    Mar 12, 2011
    Posts:
    199
    I feel like it has something to do with the second for loop but no matter how I change it I'm getting the same error :/
     
  5. makeshiftwings

    makeshiftwings

    Joined:
    May 28, 2011
    Posts:
    3,350
    Actually line 4 should be count-1 and >0. Because you're checking for [i-1], if you actually let i be zero then you'd be checking [-1] which will throw an exception.
     
    Kiwasi likes this.
  6. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    Code or do not code, there is no feel. Which line is the error pointing to?
     
  7. Sajid

    Sajid

    Joined:
    Mar 12, 2011
    Posts:
    199
    Ahh, that removed the error, thanks.

    Now i have a different issue, where as soon as one of them is destroyed, findClosest() returns null, even though there is no reason for it to. I have a method that clears out null GameObjects from my array, so there isn't any reason for the findClosest() to return null, but that's what it's doing:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEditor;
    3. using System;
    4. using System.Collections;
    5. using System.Collections.Generic;
    6.  
    7. public class turretHandlerRevised : MonoBehaviour {
    8.     public List<GameObject> enemiesInScene = new List<GameObject>();
    9.  
    10.     public GameObject projectile;
    11.     public GameObject spawnOrigin;
    12.  
    13.     private GameObject[] taggedEnemies;
    14.     private GameObject target;
    15.     private GameObject currentBulletClone;
    16.  
    17.  
    18.         public float defaultFireRate;
    19.         public float fireSpeed;
    20.         public float range;
    21.         private float fireRate;
    22.  
    23.  
    24.     void Awake()
    25.     {
    26.         target = findClosest();
    27.         fireSpeed = 5;
    28.         populateEnemyList();
    29.         fireRate = defaultFireRate;
    30.      
    31.     }
    32.  
    33.     void Update()
    34.     {
    35.         removeDeadEnemies();
    36.         Debug.Log(findClosest());
    37.      
    38.         target = findClosest();
    39.  
    40.         if (fireRate > 0)
    41.         {
    42.             fireRate -= Time.deltaTime;
    43.         }
    44.         else
    45.         {
    46.             fireRate = 0;
    47.         }
    48.  
    49.         if(fireRate <= 0)
    50.         {
    51.             if(target != null)
    52.             {
    53.                 fire(projectile, fireRate);
    54.                 fireRate = defaultFireRate;
    55.             }
    56.  
    57.         }
    58.         if(target != null)
    59.         {
    60.             spawnOrigin.transform.LookAt(target.transform);
    61.         }
    62.      
    63.     }
    64.  
    65.     public void populateEnemyList()
    66.     {
    67.         taggedEnemies = GameObject.FindGameObjectsWithTag("Enemy");
    68.  
    69.         for (int i = 0; i < taggedEnemies.Length; i++)
    70.         {
    71.             enemiesInScene.Add(taggedEnemies[i]);
    72.         }
    73.     }
    74.  
    75.     public void refreshEnemyList(GameObject inEnemy)
    76.     {
    77.         if(enemiesInScene.Contains(inEnemy) == false)
    78.         {
    79.             enemiesInScene.Add(inEnemy);
    80.         }
    81.  
    82.     }
    83.  
    84.    public void removeDeadEnemies()
    85.    {
    86.        for(int i = 0; i <= enemiesInScene.Count-1; i++)
    87.        {
    88.            if(enemiesInScene[i] == null)
    89.            {
    90.                enemiesInScene.Remove(enemiesInScene[i]);
    91.            }
    92.        }
    93.    }
    94.  
    95.     public GameObject findClosest()
    96.     {
    97.         float tempDistance = 0;
    98.         for(int i = enemiesInScene.Count-1; i > 0; i--)
    99.         {
    100.             if(enemiesInScene[i] != null)
    101.             {
    102.                 if(Vector3.Distance(transform.position,
    103.                                     enemiesInScene[i].transform.position) <
    104.                    Vector3.Distance(transform.position,
    105.                                     enemiesInScene[i - 1].transform.position))
    106.                 {
    107.                     tempDistance = Vector3.Distance(transform.position,
    108.                                                     enemiesInScene[i].transform.position);
    109.                 }
    110.             }
    111.             i--;
    112.         }
    113.  
    114.         for(int j = 0; j <= enemiesInScene.Count-1; j++)
    115.         {
    116.             if (Vector3.Distance(transform.position,
    117.                                  enemiesInScene[j].transform.position) <= range)
    118.             {
    119.                  if(Vector3.Distance(transform.position, enemiesInScene[j].transform.position) == tempDistance)
    120.                 {
    121.                     return enemiesInScene[j];
    122.                 }
    123.             }
    124.              
    125.          
    126.         }
    127.  
    128.         return null;
    129.     }
    130.  
    131.     public void fire(GameObject projectile, float fireRate)
    132.     {
    133.         GameObject projectileClone = Instantiate(projectile, spawnOrigin.transform.position, spawnOrigin.transform.rotation) as GameObject;
    134.         projectileClone.SendMessage("setTarget", target.transform.position);
    135.         projectileClone.SendMessage("setSpeed", fireSpeed);
    136.         fireRate = defaultFireRate;
    137.    
    138.     }
    139. }
    Double clicking on it doesn't even open up my IDE, let alone lead to a line for some reason.

    Every other error I get works fine but this one in particular gives me nothing to work with except the error. That being said, we determined that it was the first for loop :)
     
  8. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
    Your algorithm is not going to work all the time - in fact, a lot of the time, it won't. You have an additional i--; in there... any reason why? In a list of 6 enemies, you simply compare index 5 to 4, then 3 to 2, then 1 to 0, and only do anything if index 5 < 4, etc. What if if 4<5, 2<3, and 0<1, you will return null.

    Even if you remove the i--, if the 0th index is the closest character to you he will never be returned, and if enemies are sorted in order, you will return null.

    Also, you dont need that second for loop. Just keep track of the index in the first loop.
    some psuedocode -

    Code (csharp):
    1.  
    2. var minimumindex = enemiesInScene.Count-1;
    3. minimumDistance = distance of enemies[enemies.Count - 1];
    4. for(int i=enemiesInScene.Count-1;i>=0;i++){
    5.     if distance of enemies[i] < minimumDistance {
    6.           minumumDistance = distance of enemies[i];
    7.           minimumIndex = i;
    8.     }
    9. }
    10. return enemies[minimumIndex];
    11.  
     
  9. lordconstant

    lordconstant

    Joined:
    Jul 4, 2013
    Posts:
    389
    Looking at your remove dead enemies for loop this may fix it:

    Code (CSharp):
    1.  
    2. for(int i = 0; i < enemiesInScene.Count; i++){
    3.     if(enemiesInScene[i] == null){
    4.         enemiesInScene.RemoveAt(i);
    5.         i--;
    6.     }
    7. }
    8.  
    You need to count backwards when you remove one or you can end up skipping.