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

Bug IndexOutOfRangeException

Discussion in 'Scripting' started by diggerplus, Apr 20, 2023.

  1. diggerplus

    diggerplus

    Joined:
    Apr 5, 2016
    Posts:
    3
    Hello,

    I have this IndexOutOfRangeException error popping up when I play my game. Everything works as it should, but there is still this error. I was told if it works to just ignore it, but I'd rather there be no errors.
    Any help is appreciated.
    Here is my code :

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3.  
    4. public class CharacterSelection : MonoBehaviour
    5. {
    6.     [SerializeField] private GameObject[] maleCharacters;    // Array of male characters
    7.     [SerializeField] private GameObject[] femaleCharacters;  // Array of female characters
    8.     [SerializeField] private int selectedCharacterIndex = 0; // Index of the currently selected character
    9.     [SerializeField] private bool isFemale;                   // Flag to indicate whether the currently selected character is female
    10.  
    11.     private GameObject[] activeCharacters;                     // Array of currently active characters
    12.  
    13.     private void Start()
    14.     {
    15.         // Determine which array of characters to use based on the isFemale flag
    16.         activeCharacters = isFemale ? femaleCharacters : maleCharacters;
    17.  
    18.         // Activate the currently selected character
    19.         activeCharacters[selectedCharacterIndex].SetActive(true);
    20.        
    21.         //Debug.Log("Active characters length: " + activeCharacters.Length);
    22.     }
    23.  
    24.     public void ToggleGender()
    25.     {
    26.         // Toggle the isFemale flag
    27.         isFemale = !isFemale;
    28.  
    29.         // Deactivate the currently selected character
    30.         activeCharacters[selectedCharacterIndex].SetActive(false);
    31.  
    32.         // Determine which array of characters to use based on the isFemale flag
    33.         activeCharacters = isFemale ? femaleCharacters : maleCharacters;
    34.  
    35.         // Activate the currently selected character from the new array
    36.         activeCharacters[selectedCharacterIndex].SetActive(true);
    37.     }
    38.  
    39.     public void NextCharacter()
    40.     {
    41.         // Deactivate the currently selected character
    42.         activeCharacters[selectedCharacterIndex].SetActive(false);
    43.  
    44.         // Increment the selected character index and wrap around to the beginning of the array if necessary
    45.         selectedCharacterIndex = (selectedCharacterIndex + 1) % activeCharacters.Length;
    46.  
    47.         // Activate the newly selected character
    48.         activeCharacters[selectedCharacterIndex].SetActive(true);
    49.     }
    50.  
    51.     public void PreviousCharacter()
    52.     {
    53.         // Deactivate the currently selected character
    54.         activeCharacters[selectedCharacterIndex].SetActive(false);
    55.  
    56.         // Decrement the selected character index and wrap around to the end of the array if necessary
    57.         selectedCharacterIndex = (selectedCharacterIndex + activeCharacters.Length - 1) % activeCharacters.Length;
    58.  
    59.         // Activate the newly selected character
    60.         activeCharacters[selectedCharacterIndex].SetActive(true);
    61.     }
    62.  
    63.     public void StartGame()
    64.     {
    65.         // Store the selected character index in player prefs
    66.         PlayerPrefs.SetInt("selectedCharacter", selectedCharacterIndex);
    67.  
    68.         // Load the next scene
    69.         SceneManager.LoadScene(1, LoadSceneMode.Single);
    70.     }
    71.  
    72.     public void BackButton()
    73.     {
    74.         // Load the main menu scene
    75.         SceneManager.LoadScene(0, LoadSceneMode.Single);
    76.     }
    77. }
    Any help is appreciated. Thanks.
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    Here are some notes on IndexOutOfRangeException and ArgumentOutOfRangeException:

    http://plbm.com/?p=236

    Steps to success:
    - find which collection it is and what line of code accesses it <--- critical first step!)
    - find out why it has fewer items than you expect
    - fix whatever logic is making the indexing value exceed the collection size
    - remember that a collection with ZERO elements cannot be indexed at all: it is empty
    - remember you might have more than one instance of this script in your scene/prefab
    - remember the collection may be used in more than one location in the code
    - remember that indices start at ZERO (0) and go to the count / length minus 1.

    This means with three (3) elements, they are numbered 0, 1, and 2 only.
     
  3. seejayjames

    seejayjames

    Joined:
    Jan 28, 2013
    Posts:
    685
    Gotta be an off-by-one somewhere. I'd debug selectedCharacterIndex every time you use it as an index. Also narrow down when the exception actually happens, is there a line number in the error?
     
  4. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    Maybe check where you call Previous character because your game start with 0 and then can become -1. But it is hard to say , why you dont provide all related information in error message you have written where is issue
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    Excellent point... modulo on a signed value can return a negative.

    Code (csharp):
    1. Debug.Log( (-1 % 10).ToString());
    Screen Shot 2023-04-20 at 1.43.02 PM.png

    That may be your error. Either way the steps listed in my first post will show the error 100% of the time.
     
  6. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,357
    Don't ignore errors! The Unity editor catches errors for you, but if you were to make a build this type of error could cause your game to just crash.
     
  7. diggerplus

    diggerplus

    Joined:
    Apr 5, 2016
    Posts:
    3
    sorry for the late response, got busy with life.

    This is the full error message :
    IndexOutOfRangeException: Index was outside the bounds of the array.
    CharacterSelection.Start () (at Assets/Scripts/UIScripts/CharacterSelection.cs:19)

    it happens when I click the new game button and go to the next menu.

    I neglected to mention I'm still a bit new to the world of code, so I ask for patience if I don't understand right away.
    thanks.
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,756
    We only ask that you read and follow the steps we have already posted for you. There's nothing really more to say about this error.

    Read this post:

    https://forum.unity.com/threads/indexoutofrangeexception.1427628/#post-8963007\

    And if you continue to be puzzled why your modulo operator fails to keep your index positive, read this post:

    https://forum.unity.com/threads/indexoutofrangeexception.1427628/#post-8963217

    Keep in mind that if you're making a character selector / generator, that is Very Hard Thing(tm) and not a beginner operation at all.

    These things (inventory, shop systems, character customization, crafting, etc) are fairly tricky hairy beasts, definitely deep in advanced coding territory.

    Inventory code never lives "all by itself." All inventory code is EXTREMELY tightly bound to prefabs and/or assets used to display and present and control the inventory. Problems and solutions must consider both code and assets as well as scene / prefab setup and connectivity.

    Inventories / shop systems / character selectors all contain elements of:

    - a database of items that you may possibly possess / equip
    - a database of the items that you actually possess / equip currently
    - perhaps another database of your "storage" area at home base?
    - persistence of this information to storage between game runs
    - presentation of the inventory to the user (may have to scale and grow, overlay parts, clothing, etc)
    - interaction with items in the inventory or on the character or in the home base storage area
    - interaction with the world to get items in and out
    - dependence on asset definition (images, etc.) for presentation

    Just the design choices of such a system can have a lot of complicating confounding issues, such as:

    - can you have multiple items? Is there a limit?
    - if there is an item limit, what is it? Total count? Weight? Size? Something else?
    - are those items shown individually or do they stack?
    - are coins / gems stacked but other stuff isn't stacked?
    - do items have detailed data shown (durability, rarity, damage, etc.)?
    - can users combine items to make new items? How? Limits? Results? Messages of success/failure?
    - can users substantially modify items with other things like spells, gems, sockets, etc.?
    - does a worn-out item (shovel) become something else (like a stick) when the item wears out fully?
    - etc.

    Your best bet is probably to write down exactly what you want feature-wise. It may be useful to get very familiar with an existing game so you have an actual example of each feature in action.

    Once you have decided a baseline design, fully work through two or three different inventory tutorials on Youtube, perhaps even for the game example you have chosen above.

    Breaking down a large problem such as inventory:

    https://forum.unity.com/threads/weapon-inventory-and-how-to-script-weapons.1046236/#post-6769558

    If you want to see most of the steps involved, make a "micro inventory" in your game, something whereby the player can have (or not have) a single item, and display that item in the UI, and let the user select that item and do things with it (take, drop, use, wear, eat, sell, buy, etc.).

    Everything you learn doing that "micro inventory" of one item will apply when you have any larger more complex inventory, and it will give you a feel for what you are dealing with.

    Breaking down large problems in general:

    https://forum.unity.com/threads/opt...n-an-asteroid-belt-game.1395319/#post-8781697
     
    Bunny83 likes this.
  9. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,528
    Welcome to the world of debugging. So the error is in this line:

    Code (CSharp):
    1. activeCharacters[selectedCharacterIndex].SetActive(true);
    That error means your Start method will forcefully be terminated at this line because the code can not be executed since it contains instructions that work on faulty data. There's only one direct reason why you would get an IndexOutOfBounds exception here. That is that
    selectedCharacterIndex
    does not represent a valid index. This on the other hand can have two causes:
    1. selectedCharacterIndex is too small (less than 0) or too large (greater or equal to the array's length)
    2. the array you try to index is empty. That means it has a length of 0 and doesn't contain any elements. So there can't be any valid index. A valid index, as mentioned in point 1 has to be 0 or greater AND smaler than the length of the array. When the length is 0 the index would need to be smaller than 0 but also 0 or greater. That's impossible. Also it should be logically when you don't have any elements, you can't pick one.
    Now lets get to the debugging part. The runtime exception that is thrown already tells you where the error is and essentially tells you what the error is. This is much more compared to what you got in the distant past. Errors like that would either crash or cause funky random errors which are hard to track down. You already know what the error is and where it is. You just don't know the exact cause yet. So just find out. Add a Debug.Log statement like this BEFORE the offending line of code:


    Code (CSharp):
    1.  
    2. Debug.Log("Trying to select character with index " + selectedCharacterIndex + " from an array with " +activeCharacters.Length + " elements", this);
    3. activeCharacters[selectedCharacterIndex].SetActive(true);
    With this log statement you should get all the informations necessary to continue searching for the reason of your error.

    We don't know where and when you set your "selectedCharacterIndex". However what I can see is that you use PlayerPrefs to save the selected character index, but you never load that value again. Also currently you don't save the selected gender which is kinda strange as well. Besides that you have a lof of redundant / repeated code which probably should be placed into a single method that does the proper checks. For example

    Code (CSharp):
    1.  
    2. public void UnsetCharacter()
    3. {
    4.     if (activeCharacters != null && selectedCharacterIndex >= 0 && selectedCharacterIndex < activeCharacters.Length)
    5.         activeCharacters[selectedCharacterIndex].SetActive(false);
    6. }
    7.  
    8. public void SetCharacter(int aCharacterIndex, bool aIsFemale)
    9. {
    10.     UnsetCharacter();
    11.     isFemale = aIsFemale;
    12.     activeCharacters = isFemale ? femaleCharacters : maleCharacters;
    13.     selectedCharacterIndex = (aCharacterIndex + activeCharacters.Length) % activeCharacters.Length;
    14.     if (activeCharacters != null && selectedCharacterIndex >= 0 && selectedCharacterIndex < activeCharacters.Length)
    15.         activeCharacters[selectedCharacterIndex].SetActive(true);
    16.     else if (activeCharacters == null)
    17.         Debug.LogError("activeCharacters array is null. Selected gender was: " + isFemale?"female":"male", this);
    18.     else if (activeCharacters.Length == 0)
    19.         Debug.LogError("activeCharacters array is empty. Selected gender was: " + isFemale?"female":"male", this);
    20.     else
    21.         Debug.LogError("This should never be reached. The only way would be when the character index is set to a large negative value that exceeds the array length.", this);
    22. }
    Those methods would be the only place where you actually activate / deactivate your characters. So all other places where you want to change the character you simply use this method:

    Code (CSharp):
    1.  private void Start()
    2. {
    3.     SetCharacter(selectedCharacter, isFemale);
    4. }
    5. public void ToggleGender()
    6. {
    7.     SetCharacter(selectedCharacter, !isFemale);
    8. }
    9. public void NextCharacter()
    10. {
    11.     SetCharacter(selectedCharacter + 1, isFemale);
    12. }
    13. public void PreviousCharacter()
    14. {
    15.     SetCharacter(selectedCharacter - 1, isFemale);
    16. }
    This should not throw any errors and would indicate issues with a log message.
     
    Ryiah and spiney199 like this.
  10. diggerplus

    diggerplus

    Joined:
    Apr 5, 2016
    Posts:
    3
    Hello again, I apologize for the delayed reply.

    I inserted the debug line:
    Debug.Log("Trying to select character with index " + selectedCharacterIndex + " from an array with " +activeCharacters.Length + " elements", this);
    into the script as recommended.

    When I hit the play button the debug message pops up right away and is fine. No errors.
    The error occurs when I hit the "New Game" button. This basically tells me that my script is running as it is (or perhaps not), but it's being triggered before the menu that uses it is active, and then get the error when it is activated.

    Side note that probably needs mentioning, I'm attempting to keep all UI elements in one scene as I was told this is the best / most efficient way to go about it.

    So, I suppose this now comes down to a question of how to disable the script until it's supposed to fire. If there's a simple solution, please feel free to share, otherwise I will do things the hard way and research till I get it right.

    Also, I'd like to thank everyone for your help thus far. I appreciate your patience with me.
     
    Last edited: May 4, 2023
  11. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    You could add Debug.Log to Button listener and check what index it is trying to get.
     
    Bunny83 likes this.