Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Array Index out of range. Select random gameobject from array

Discussion in 'Scripting' started by n_soufiani, Jun 5, 2014.

  1. n_soufiani

    n_soufiani

    Joined:
    Apr 15, 2014
    Posts:
    16
    Hello all

    I have searched and searched and read solutions yet I am still getting nowhere with this problem. I cannot understand why I get a "Indexoutofrangeexception: array index is out of range" problem.

    Here is the code

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class SpawnEnemy : MonoBehaviour {
    5.  
    6.     public Vector3 spawnStar;
    7.     public Vector3 spawnValuesTop;
    8.     public Vector3 spawnValuesBottom;
    9.     public Vector3 spawnValuesLeft;
    10.     public Vector3 spawnValuesRight;
    11.    
    12.     public bool stage1;
    13.  
    14.     public float randomTimeLowS1;
    15.     public float randomTimeHighS1;
    16.  
    17.    
    18.     public GameObject[] enemyAsteroids;
    19.     GameObject currentAsteroid;
    20.     int index;
    21.    
    22.     // Use this for initialization
    23.     void Start () {
    24.        
    25.         enemyAsteroids = GameObject.FindGameObjectsWithTag ("Asteroid");
    26.         currentAsteroid = enemyAsteroids [index];
    27.        
    28.     }
    29.    
    30.     // Update is called once per frame
    31.     void Update () {
    32.        
    33.        
    34.         if (GameObject.Find ("GameAttribute").GetComponent<StartMenu> ().startButton == false) {
    35.        
    36.             if (!GameObject.Find ("GameAttribute").GetComponent<EarthKeepScore> ().gameOver && gameObject.GetComponent<KeepScore> ().score < 10 && stage1 == false) {
    37.                 InvokeRepeating ("SpawnAsteroidTopS1", 2.0f, Random.Range (randomTimeLowS1, randomTimeHighS1));
    38.                 stage1 = true;
    39.                 }
    40.             }
    41.            
    42.             //------------------------------------------------------------------------------------
    43.  
    44.            
    45.             if (GameObject.Find ("GameAttribute").GetComponent<EarthKeepScore> ().gameOver) {
    46.                 CancelInvoke ("SpawnAsteroidTopS1");
    47.             }
    48.  
    49.     }
    50.  
    51.    
    52.     void SpawnAsteroidTopS1 () {
    53.        
    54.        
    55.         Vector3 spawnPositionTop = new Vector3 (Random.Range (-spawnValuesTop.x, spawnValuesTop.x), spawnValuesTop.y, spawnValuesTop.z);
    56.         Quaternion spawnRotationTop = Quaternion.identity;
    57.         Instantiate (currentAsteroid, spawnPositionTop, spawnRotationTop);
    58.        
    59.     }
    60.    
    61.  
    62.    
    63. }
    64.  
    Unity mentions the problem being at line 26 where the code reads: currentAsteroid = enemyAsteroids [index];
    I have 3 prefabs with the Asteroid tag on them. I simply want any one of those 3 prefabs selected and instantiated. I want this process to keep repeating itself so that I get a stream of randomly instantiated prefabs with the asteroid tag on them.

    In the inspector I have a size of 3 with 3 asteroid prefabs attached to them as can be seen in the picture yet I still get this problem. I thought that I was searching for gameobjects with the tag "Asteroid" so I didn't need to specify, unity would just find the gameobjects with the tag "Asteroid" so I set it as 0 as well but I still had the same exact problem. In fact, when I start the game, the size is automatically set to 0 every time thus giving me a "ArgumentException: the prefab you want to instantiate is null".

    I thought I was slowly becoming an expert at unity until I encountered this problem :(
    Thank you for reading. Hopefully I will be able to get this resolved.
     

    Attached Files:

  2. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    What is the value of index?
     
  3. n_soufiani

    n_soufiani

    Joined:
    Apr 15, 2014
    Posts:
    16
    the value of index is

    index = Random.Range(0, enemyAsteroids.Length);

    Sorry, it seems that it didn't copy across that part of the code for some reason. But I have

    index = Random.Range(0, enemyAsteroids.Length); one line before line 26
     
  4. TheSniperFan

    TheSniperFan

    Joined:
    Jul 18, 2013
    Posts:
    712
    What does the debugger say about the value?
     
  5. Zaladur

    Zaladur

    Joined:
    Oct 20, 2012
    Posts:
    392
    Edit: Did not realize Random.Range functioned differently with ints than with floats. As was pointed out, with ints the last value is exclusive. My mistake.
     
    Last edited: Jun 5, 2014
    n_soufiani likes this.
  6. DanielQuick

    DanielQuick

    Joined:
    Dec 31, 2010
    Posts:
    3,137
    Random.Range (int, int) is [inclusive], (exclusive) meaning it will never return the second value (unless min and max is the same). The code is already correct.

    The likely problem is that FindGameObjectsWithTag is not finding anything, giving the array a length of 0. Random.Range (0, 0) will return 0 even though there is nothing there.

    A check is needed to ensure that the array's length is greater than 0.
     
  7. TheSniperFan

    TheSniperFan

    Joined:
    Jul 18, 2013
    Posts:
    712
    Damn I'm getting tired. How could I have missed that? *facepalm*

    The array length starts counting at 1 while the indices start at 0.
     
  8. n_soufiani

    n_soufiani

    Joined:
    Apr 15, 2014
    Posts:
    16
    I have added:

    debug.log (index); so the void start looks like this:

    Code (CSharp):
    1. void Start () {
    2.  
    3.         enemyAsteroids = GameObject.FindGameObjectsWithTag ("Asteroid");
    4.         index = Random.Range(0, enemyAsteroids.Length);
    5.         currentAsteroid = enemyAsteroids [index];
    6.         Debug.Log (index);
    7.  
    8.    
    9.  
    10.    
    11.         InvokeRepeating ("SpawnStar", Random.Range (20.0f, 60.0f), Random.Range (30.0f, 700.0f));
    12.  
    13.     }
    and I get nothing in the console. Just the same "Indexoutofrangeexception: array index is out of range" and "ArgumentException: the prefab you want to instantiate is null".
     
  9. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Because the exception is being thrown before your debug statement. Log the length of enemyAsteroids after your call to Find() to make sure you found something.
     
  10. n_soufiani

    n_soufiani

    Joined:
    Apr 15, 2014
    Posts:
    16
    I have done exactly as you said and I get "0" in the console.

    Do my prefabs actually have to be in the scene itself for this to work? I have my prefabs stored in the prefabs folder and they do not exist in the scene at the start. My thinking is that unity is searching for the tag "Asteroid" within the scene unless I'm mistaken
     
  11. DanielQuick

    DanielQuick

    Joined:
    Dec 31, 2010
    Posts:
    3,137
    It will only find GameObjects that are both in the scene and active.

    To load Prefabs you can either link them in the inspector or use Resources.Load.
     
    n_soufiani likes this.
  12. n_soufiani

    n_soufiani

    Joined:
    Apr 15, 2014
    Posts:
    16
    Even in your tired state you are far outsmarting me as I'm trying to comprehend what you said! ;)
     
  13. n_soufiani

    n_soufiani

    Joined:
    Apr 15, 2014
    Posts:
    16
    This I think may be my problem. I will give this a go and report back.
     
  14. DanielQuick

    DanielQuick

    Joined:
    Dec 31, 2010
    Posts:
    3,137
    It was in response to
    Which is incorrect information.
     
  15. n_soufiani

    n_soufiani

    Joined:
    Apr 15, 2014
    Posts:
    16
    oh i see, I thought he was responding to me! sorry about that!
     
  16. TheSniperFan

    TheSniperFan

    Joined:
    Jul 18, 2013
    Posts:
    712
    It's really simple:
    Code (CSharp):
    1. int[] banana = new int[3];    // The size of the array is 3. It means this array stores 3 numbers
    2.  
    3. banana[0] = 1;
    4. banana[1] = 2;
    5. banana[2] = 3;     // Note the index. While the banana.Length is 3, the biggest index is 2 since we start counting indices from 0 and not 1.
    As far as your issue goes:
    Here's a general tip: If Debug.Log() doesn't help, use the debugger. Maybe some other value is wrong or will give you an idea.
     
    n_soufiani likes this.
  17. n_soufiani

    n_soufiani

    Joined:
    Apr 15, 2014
    Posts:
    16
    Excellent! I understand what you meant now. Thank you very much.
     
  18. n_soufiani

    n_soufiani

    Joined:
    Apr 15, 2014
    Posts:
    16
    Managed to solve it thanks to everyone here.
    The problem was, as danielquick pointed out, that I didn't load the objects were not active.

    so at the start I did the following:

    Code (CSharp):
    1.  
    2.  
    3. GameObject [] asteroidprefa = new GameObject[3];
    4.        
    5.         asteroidprefa [0] = asteroid;
    6.         asteroidprefa [1] = asteroid2;
    7.         asteroidprefa [2] = asteroid3;
    8.         index = Random.Range (0, asteroidprefa.Length);
    9.         currentAsteroid = asteroidprefa [index];
    I made asteroid, asteroid2 and asteroid3 into public GameObjects and attached the prefabs onto them.
    I'm sure there is a far better way of solving this such as resources.reload as danielquick mentioned but I tried implementing it and I was having no luck. The above addition makes the code work fine though. For ease of use, here is the full code:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class SpawnEnemy1 : MonoBehaviour {
    5.  
    6.     public GameObject asteroid;
    7.     public GameObject asteroid2;
    8.     public GameObject asteroid3;
    9.  
    10.     public Vector3 spawnStar;
    11.     public Vector3 spawnValuesTop;
    12.     public Vector3 spawnValuesBottom;
    13.     public Vector3 spawnValuesLeft;
    14.     public Vector3 spawnValuesRight;
    15.    
    16.     public bool stage1;
    17.  
    18.     public float randomTimeLowS1;
    19.     public float randomTimeHighS1;
    20.  
    21.    
    22.     public GameObject[] enemyAsteroids;
    23.     GameObject currentAsteroid;
    24.     int index;
    25.    
    26.     // Use this for initialization
    27.     void Start () {
    28.        
    29.         GameObject [] asteroidprefa = new GameObject[3];
    30.        
    31.         asteroidprefa [0] = asteroid;
    32.         asteroidprefa [1] = asteroid2;
    33.         asteroidprefa [2] = asteroid3;
    34.         index = Random.Range (0, asteroidprefa.Length);
    35.         currentAsteroid = asteroidprefa [index];
    36.        
    37.     }
    38.    
    39.     // Update is called once per frame
    40.     void Update () {
    41.        
    42.        
    43.         if (GameObject.Find ("GameAttribute").GetComponent<StartMenu> ().startButton == false) {
    44.        
    45.             if (!GameObject.Find ("GameAttribute").GetComponent<EarthKeepScore> ().gameOver && gameObject.GetComponent<KeepScore> ().score < 10 && stage1 == false) {
    46.                 InvokeRepeating ("SpawnAsteroidTopS1", 2.0f, Random.Range (randomTimeLowS1, randomTimeHighS1));
    47.                 stage1 = true;
    48.                 }
    49.             }
    50.            
    51.             //------------------------------------------------------------------------------------
    52.  
    53.            
    54.             if (GameObject.Find ("GameAttribute").GetComponent<EarthKeepScore> ().gameOver) {
    55.                 CancelInvoke ("SpawnAsteroidTopS1");
    56.             }
    57.  
    58.     }
    59.  
    60.    
    61.     void SpawnAsteroidTopS1 () {
    62.        
    63.        
    64.         Vector3 spawnPositionTop = new Vector3 (Random.Range (-spawnValuesTop.x, spawnValuesTop.x), spawnValuesTop.y, spawnValuesTop.z);
    65.         Quaternion spawnRotationTop = Quaternion.identity;
    66.         Instantiate (currentAsteroid, spawnPositionTop, spawnRotationTop);
    67.        
    68.     }
    69.    
    70.  
    71.    
    72. }
    73.  
    This would select only one type of asteroid throughout the game though. If you wanted to instantiate different types of asteroids while you are playing the game you can modify it as follows:

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class SpawnEnemy1 : MonoBehaviour {
    5.  
    6.     public GameObject asteroid;
    7.     public GameObject asteroid2;
    8.     public GameObject asteroid3;
    9.  
    10.     public Vector3 spawnStar;
    11.     public Vector3 spawnValuesTop;
    12.     public Vector3 spawnValuesBottom;
    13.     public Vector3 spawnValuesLeft;
    14.     public Vector3 spawnValuesRight;
    15.    
    16.     public bool stage1;
    17.  
    18.     public float randomTimeLowS1;
    19.     public float randomTimeHighS1;
    20.  
    21.    
    22.     public GameObject[] enemyAsteroids;
    23.     GameObject currentAsteroid;
    24.     int index;
    25.    
    26.     // Use this for initialization
    27.     void Start () {
    28.        
    29.  
    30.        
    31.     }
    32.    
    33.     // Update is called once per frame
    34.     void Update () {
    35.        
    36.        
    37.         if (GameObject.Find ("GameAttribute").GetComponent<StartMenu> ().startButton == false) {
    38.        
    39.             if (!GameObject.Find ("GameAttribute").GetComponent<EarthKeepScore> ().gameOver && gameObject.GetComponent<KeepScore> ().score < 10 && stage1 == false) {
    40.                 InvokeRepeating ("SpawnAsteroidTopS1", 2.0f, Random.Range (randomTimeLowS1, randomTimeHighS1));
    41.                 stage1 = true;
    42.                 }
    43.             }
    44.            
    45.             //------------------------------------------------------------------------------------
    46.  
    47.            
    48.             if (GameObject.Find ("GameAttribute").GetComponent<EarthKeepScore> ().gameOver) {
    49.                 CancelInvoke ("SpawnAsteroidTopS1");
    50.             }
    51.  
    52.     }
    53.  
    54.    
    55.     void SpawnAsteroidTopS1 () {
    56.         GameObject [] asteroidprefa = new GameObject[3];
    57.        
    58.         asteroidprefa [0] = asteroid;
    59.         asteroidprefa [1] = asteroid2;
    60.         asteroidprefa [2] = asteroid3;
    61.         index = Random.Range (0, asteroidprefa.Length);
    62.         currentAsteroid = asteroidprefa [index];
    63.        
    64.         Vector3 spawnPositionTop = new Vector3 (Random.Range (-spawnValuesTop.x, spawnValuesTop.x), spawnValuesTop.y, spawnValuesTop.z);
    65.         Quaternion spawnRotationTop = Quaternion.identity;
    66.         Instantiate (currentAsteroid, spawnPositionTop, spawnRotationTop);
    67.        
    68.     }
    69.    
    70.  
    71.    
    72. }
    The difference being is that the whole:

    GameObject [] asteroidprefa = new GameObject[3];

    asteroidprefa [0] = asteroid;
    asteroidprefa [1] = asteroid2;
    asteroidprefa [2] = asteroid3;
    index = Random.Range (0, asteroidprefa.Length);
    currentAsteroid = asteroidprefa [index];

    is now in the void SpawnAsteroidTopS1 method. This ensures that different types of asteroids, that you have specified in the inspector, are being instantiated while you are playing the game.

    Thank you all for helping me out! I hope this helps someone else in a similar situation to me in the future.
     
  19. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Instead of making asteroid, asteroid2, and asteroid3 why don't you just use enemyAsteroids directly in the Inspector by assigning it a size of 3 and then assigning the 3 elements in it and skipping the code assignments completely?