Search Unity

Question Index was outside the bounds of the array/System.Collections.Generic.List'1[T] - [SOLVED]

Discussion in 'Scripting' started by noclipstudio, Dec 16, 2022.

  1. noclipstudio

    noclipstudio

    Joined:
    Mar 2, 2021
    Posts:
    73
    Hi devs, although I know the index exceeded the array limit, I don't see anything wrong with my code, but if any of you find something that makes sense for this error, please point me where I'm going wrong. Just an observation, I'm not referencing any array in the ispector, but I've tried referencing by dragging the gameobjects to the empty field of the array and it still didn't solve this error, so I know it's some incorrect logic in my code.













    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine.SceneManagement;
    4. using UnityEngine.UI;
    5. using System;
    6. using UnityEngine;
    7.  
    8. public class CarCheckpointManager : MonoBehaviour
    9. {
    10.     public int carNumber;
    11.     public int checkpointCrossed = 0;
    12.     public RaceManager raceManager;
    13.     public int carPosition;
    14.     public float timeAtLastPassedCheckpoint = 0;
    15.     public Text PositionTxt;
    16.     public static CarCheckpointManager instance;
    17.     public event Action<CarCheckpointManager> OnPassCheckpoint;
    18.  
    19.     void Start()
    20.     {
    21.         instance = this;
    22.     }
    23.  
    24.     private void OnTriggerEnter2D(Collider2D collision)
    25.     {
    26.         if(collision.gameObject.CompareTag("Checkpoint"))
    27.         {
    28.             checkpointCrossed += 1;
    29.             raceManager.CarCollectedCheckpoints(carNumber, checkpointCrossed);
    30.             timeAtLastPassedCheckpoint = Time.time;
    31.             OnPassCheckpoint?.Invoke(this);
    32.         }
    33.     }
    34.  
    35.     public int GetNumberOfCheckpointsPassed()
    36.     {
    37.         return checkpointCrossed;
    38.     }
    39.  
    40.     public float GetTimeAtLastPassedCheckpoint()
    41.     {
    42.         return timeAtLastPassedCheckpoint;
    43.     }
    44.  
    45.     public void SetCarPosition(int position)
    46.     {
    47.         carPosition = position;
    48.     }
    49.  
    50.     public void UpdateScore()
    51.     {
    52.         PlayerPrefs.SetInt("carPosition", carPosition);
    53.     }
    54. }
    CODE 2°:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.Linq;
    5.  
    6. public class PositionHandler : MonoBehaviour
    7. {
    8.     LeaderBoardUIHandler leaderBoardUIHandler;
    9.  
    10.     [HideInInspector]
    11.     public List<CarCheckpointManager> carCheckpointManagers = new List<CarCheckpointManager>();
    12.  
    13.     private void Awake()
    14.     {
    15.         CarCheckpointManager[] carCheckpointManagerArray = FindObjectsOfType<CarCheckpointManager>();
    16.  
    17.         carCheckpointManagers = carCheckpointManagerArray.ToList<CarCheckpointManager>();
    18.  
    19.         foreach (CarCheckpointManager carCheckpoint in carCheckpointManagers)
    20.             carCheckpoint.OnPassCheckpoint += OnPassCheckpoint;
    21.  
    22.         leaderBoardUIHandler = FindObjectOfType<LeaderBoardUIHandler>();
    23.     }
    24.  
    25.     private void Start()
    26.     {
    27.         leaderBoardUIHandler.UpdateList(carCheckpointManagers);
    28.     }
    29.  
    30.     private void OnPassCheckpoint(CarCheckpointManager carCheckpoint)
    31.     {
    32.         carCheckpointManagers = carCheckpointManagers.OrderByDescending(s => s.GetNumberOfCheckpointsPassed()).ThenBy(s => s.GetTimeAtLastPassedCheckpoint()).ToList();
    33.  
    34.         int carPosition = carCheckpointManagers.IndexOf(carCheckpoint) + 1;
    35.  
    36.         carCheckpoint.SetCarPosition(carPosition);
    37.  
    38.         leaderBoardUIHandler.UpdateList(carCheckpointManagers);
    39.     }
    40. }
    41.  
    CODE 3°:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using TMPro;
    5. using UnityEngine.UI;
    6.  
    7. public class LeaderBoardUIHandler : MonoBehaviour
    8. {
    9.     public GameObject leaderBoardItemPrefab;
    10.  
    11.     SetLeaderBoardItemInfo[] setLeaderBoardItemInfo;
    12.  
    13.     private void Awake()
    14.     {
    15.         VerticalLayoutGroup leaderBoardLayoutGroup = GetComponentInChildren<VerticalLayoutGroup>();
    16.  
    17.         CarCheckpointManager[] carCheckpointManager = FindObjectsOfType<CarCheckpointManager>();
    18.  
    19.         setLeaderBoardItemInfo = new SetLeaderBoardItemInfo[carCheckpointManager.Length];
    20.  
    21.         for (int i = 0; i < carCheckpointManager.Length; i++)
    22.         {
    23.             GameObject leaderBoardInfoGameObject = Instantiate(leaderBoardItemPrefab, leaderBoardLayoutGroup.transform);
    24.  
    25.             setLeaderBoardItemInfo[i] = leaderBoardInfoGameObject.GetComponent<SetLeaderBoardItemInfo>();
    26.  
    27.             setLeaderBoardItemInfo[i].SetPositionText($"{i + 1}.");
    28.         }
    29.     }
    30.  
    31.     public void UpdateList(List<CarCheckpointManager> carCheckpointManager)
    32.     {
    33.         for (int i = 0; i < carCheckpointManager.Count; i++)
    34.         {
    35.             setLeaderBoardItemInfo[i].SetDriverNameText(carCheckpointManager[i].gameObject.name);
    36.         }
    37.     }
    38. }
    39.  
     
  2. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,065
    I can't conclusively tell from your scripts but
    setLeaderBoardItemInfo
    is likely not the same length as
    carCheckpointManager
    .

    I'd recommend attaching a debugger, breaking on on the IndexOutOfRangeException and checking the lengths. Being able to inspect the variables and check the stack gives you a different perspective and often makes issues much more obvious.

    Note that there's two
    carCheckpointManager
    , an array you allocate in
    LeaderBoardUIHandler
    but also a list allocated in
    PositionHandler
    . The first is guaranteed to match in length to
    setLeaderBoardItemInfo
    but the second could well be different because of objects being unloaded or inactive and the second one gets passed into and used in
    UpdateList
    .
     
  3. noclipstudio

    noclipstudio

    Joined:
    Mar 2, 2021
    Posts:
    73
    Thanks for the help, I'm still trying to solve it, but since you commented on the length of the other script, I'll send it here:

    Code (CSharp):
    1. public class SetLeaderBoardItemInfo : MonoBehaviour
    2. {
    3.     public TextMeshProUGUI positionText;
    4.     public TextMeshProUGUI driverNameText;
    5.  
    6.     public void SetPositionText(string newPosition)
    7.     {
    8.         positionText.text = newPosition;
    9.     }
    10.  
    11.     public void SetDriverNameText(string newDriverName)
    12.     {
    13.         driverNameText.text = newDriverName;
    14.     }
    15. }
     
  4. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    38,674
    Here are some notes on IndexOutOfRangeException and ArgumentOutOfRangeException:

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

    Steps to success:
    - find which collection it is and what 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
    - 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
     
    noclipstudio likes this.
  5. noclipstudio

    noclipstudio

    Joined:
    Mar 2, 2021
    Posts:
    73
    I had just read your post about this, but I'm still trying to solve it, without success so far.
     
  6. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,065
    You might be missing my point.
    LeaderBoardUIHandler.UpdateList
    assumes the
    setLeaderBoardItemInfo
    array and the
    carCheckpointManager
    list have the same size, which doesn't have to be the case. Have you tried logging both sizes at the beginning of
    UpdateList
    ?
     
    Bunny83 likes this.
  7. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,065
    Try putting this before the for loop in UpdateList:
    Code (CSharp):
    1. Debug.Log($"setLeaderBoardItemInfo.Length = {setLeaderBoardItemInfo.Length}, carCheckpointManager.Count = {carCheckpointManager.Count}");
    Then trigger the out of range exception and check in the log if the two numbers don't match.

    The log statement you tried to add is after the exception occurs, always put your log statement before the exception occurs and be careful not to trigger a new exception in the log statement itself. The exception will stop execution and anything after it will not be logged.
     
    noclipstudio likes this.
  8. noclipstudio

    noclipstudio

    Joined:
    Mar 2, 2021
    Posts:
    73
    An interesting observation I had, I added the size of 3 in setLeaderBoardItemInfo[3], this way the errors don't appear, however the script doesn't show the names as it should.



     
  9. Adrian

    Adrian

    Joined:
    Apr 5, 2008
    Posts:
    1,065
    As you can see in the log line right before the exception,
    setLeaderBoardItemInfo
    has length 4 but
    carCheckpointManager
    has count 5. When the for loop reaches the last item of
    carCheckpointManager
    (at index 4), it tries to access the corresponding item on
    setLeaderBoardItemInfo
    , but since that is only 4 items long (up to index 3), it tries to access index 4 that is out of bounds (4 > 3).

    As I pointed out before, the fundamental issue is that
    FindObjectsOfType
    is only a snapshot. If you call it a different points in your game, it can return a different result because objects got instantiated, deleted, activated or deactivated. You create the
    setLeaderBoardItemInfo
    array based on the result of
    FindObjectsOfType
    at one point but then
    UpdateList
    is called at a different point in your game where
    FindObjectsOfType
    returns more results.

    What the right approach to fixing this issue is really depends on your situation, you could:
    - Ignore additional elements (change the for loop condition to
    i < carCheckpointManager.Count && i < setLeaderBoardItemInfo.Length
    )
    - Instantiate additional
    leaderBoardItemPrefab
    and extend the
    setLeaderBoardItemInfo
    array so it contains enough elements (maybe turn it into list?)
    - Don't pass in
    carCheckpointManager
    into
    UpdateList
    , instead store the
    carCheckpointManager
    you create in
    LeaderBoardUIHandler.Awake
    in a field and iterate that, since it will always stay the same length as
    carCheckpointManager
    .
     
    Bunny83 and noclipstudio like this.
  10. noclipstudio

    noclipstudio

    Joined:
    Mar 2, 2021
    Posts:
    73
    Thank you very much for your help, this information will be very useful for me to solve this problem, I'll come back here when I have good news.
     
    Bunny83 likes this.
  11. noclipstudio

    noclipstudio

    Joined:
    Mar 2, 2021
    Posts:
    73
    I would like to thank you again for the help, I changed the for loop as you suggested, and that alone has prevented the problem, and the good part is that even without the Debug.Log, the error does not appear.