- Home /
Arguement out of range exception on a lists index
Hi unityAnswers. I'm trying to go through a list of files (that resets to the first model after the last model is done) and I have a plus and minus button for going forwards and backwards. But I can't figure out why clones index is out of range when I click the plus button. Any help is greatly appreciated.
public int UPPER_BOUNDS; // set to 3 in unity
public TouchRotation tRotation;
public List<GameObject> models = new List<GameObject>(); // three game objects loaded
// in unity
private List<GameObject> clones = new List<GameObject>();
private GameObject temp;
private int count = 0;
// Use this for initialization
void Start()
{
ObjReader.use.autoCenterOnOrigin = true;
count = 0;
clones.Add(temp);
Debug.Log(clones.Count); // outputs 1
Debug.Log(clones.Capacity); // outputs 4
TouchRotation tRotation = this.gameObject.AddComponent<TouchRotation>();
tRotation.camera = this.camera;
}
public void loadModel()
{
clones[count] = Instantiate(models[count]) as GameObject;
}
public void unloadModel()
{
Destroy(clones[count]);
}
public void incrementCounter()
{
// try
// {
Debug.Log(count + " pre in");
if (count == UPPER_BOUNDS)
{
Debug.Log(count + " upper_bounds pre");
count--;
unloadModel();
count--;
loadModel();
count++;
Debug.Log(count + " upper_bounds");
}
else if (count > UPPER_BOUNDS)
{
Debug.Log(count + "greater upper_bounds pre");
count = UPPER_BOUNDS;
unloadModel();
count = 0;
clones = null;
loadModel();
Debug.Log(count + " count > upper_bounds");
}
else
{
Debug.Log(count + " pre else");
if (clones[count] == temp)
{
Debug.Log(count + "clones[count] temp");
count++;
loadModel(); // it appears to be breaking on this line
}
else
unloadModel();
count++;
loadModel();
Debug.Log(count + " else inc");
}
}
// catch (System.Exception)
// {
// count = 0;
// }
// }
public void decrementCounter()
{
// try
// {
Debug.Log(count + " pre dec");
if (count <= 0)
{
Debug.Log(count + " pre count reset");
count = 0;
Debug.Log(count + " post count reset");
unloadModel();
Debug.Log(count + " post unload model");
}
else
{
unloadModel();
count--;
loadModel();
Debug.Log(count + " else dec");
}
}
// catch (System.Exception)
// {
// count = 0;
// }
// }
}
I don't see the "count" variable declared anywhere in the code you have provided. $$anonymous$$aybe you forgot to declare it?
try adding clones.Count to your Debug.Log so you can see how many items are in the clones list.
It's very easy to get the logic wrong and go one over when the list starts counting at 0.
I accidentally deleted the count variable when I posted this, it's definitely declared in the actual code. And I tried clones.count right before it does anything in the increment code and it came back with a 1? I'm still not understanding why it's one and why the index is getting out of range.
@Glynax you must check the error message, which will indicate a line number where the error occurred. This line number should be your starting point in your debugging efforts.
Then, you have to open this script in $$anonymous$$onoDevelop and, starting at the error line number, put break points in your code wherever you consider relevant.
Then, attach $$anonymous$$onoDevelop to the Unity Process, start the game in the editor, and when the code reaches the first break point, execution will stop and the $$anonymous$$onoDevelop window will open while highlighting that break point.
Now, you must start exa$$anonymous$$ing run-time values of your variables. One way to do that is to place the mouse cursor over the variables, and a popup will tell you the value. With some practice you'll see how it works, it's not that hard, and it's a very useful tool.
After you exa$$anonymous$$e some relevant values, start stepping through code. Use F11 to step to the next line of code. If you are in a line with a method call, you can either F11 to go inside the method, or F10 to "skip" the method and go to the next line. Of course the code inside the method will be executed normally, but with F10 you won't have to single-step through each line of code of the method, if you do really need to.
So, step through your code line by line. Change your break points as you see fit, and I'm sure you'll find the bug.
You will benefit more if you debug this yourself, than if I debug it for you and give you the answer.
I'm using visual studio 2013 (ultimate I think) It gives me an error with breakpoints after I attach it to unity, "The breakpoint will not currently be hit. No symbols have been loaded for this document. And I've googled the error and tried to fix it myself but to no avail. So the debugging is quite problematic without breakpoints.
Answer by pako · Dec 28, 2014 at 07:27 PM
UPPER_BOUNDS = 3
There are 3 models loaded, indexed 0,1,2
Whenever loadModel() or unloadModel() is called and count is greater than 2 you will get “index out of range error”.
Lines 53, 54:
count = UPPER_BOUNDS; //(i.e. count = 3)
unloadModel();
=> Count = 3, unloadModel() but no model exists at index 3 => error
Lines 71, 72:
If count < 3 (UPPER_BOUNDS) this will execute. So, when count = 2, it will execute but will increase to 3
count++; //increases count to 3
loadModel(); //error no model at index 3
Make sure that loadModel() and unloadModel() never get called with count greater than 2, or the maximum number of models in clones.
The following code for the FileLoader script should do what you want. It's quite different -and much simpler- from your original code but should do the job fine. I provided plenty of comments in each part of the code, so you can understand the changes I made, as well as the functionality. A major change was removing the clones List, and replacing it with a currentModel GameObject, which also makes very easy referencing this from the GameController. In effect, a 'currentIndex' replaced 'count', and this always points to the element in the models List that is loaded in currentModel, and made available to the GameController through a readonly property.
One important thing that I want to say is that although this code provides the functionality you specify, it will not perform so well. The reason for this is that as the user goes forwards and backwards in the list of models, the Garbage Collector will be called all the time to clear all objects that are destroyed (or lose their reference). When the Garbage Collector kicks in, the performance of the application will drop, until the Garbage Collector finishes its job. A better way to implement this is to have all models inside the scene and disabled. Then instead of Instantiating and Destroying, you just disable the previous and enable the next model. Such a solution would perform much better because there is no Garbage Collector involved. To implement this, create an Empty GameObject in the scene and add to it -as children- all the models you have in Assets/Models. Then disable all of the children.
Then, all the GameObjects inside the models List will be instantiated GameObjects (and disabled), rather than prefabs. So, Inside the loadModel() method you don't need to use Instantiate(), but currentModel.SetActive(false) to disable and currentModel.SetActive(true) to enable. I have included the relevant code for these changes, but I have commented it out, so you decide what you want.
So here is the code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class FileLoader : MonoBehaviour {
//It seems that the only use of the following two fields is to get a reference to the Main Camera (SceneCamera)
//and then assing it to the GameController's TouchRotation (assigned in Start())
//Since FileLoader has no other use of either SceneCamera or TouchRotation, it is better to remove these fields from FileLoader
//and have a public Camera field directly in TouchRotation
public Camera SceneCamera; //should be moved to TouchRotation script
public TouchRotation tRotation; //remove
public List<GameObject> models = new List<GameObject>(); // three game objects loaded in unity
//You don't need this!
//You just need ONE currentModel field where GameObjects from the models List are loaded
//as you move to the next or previous element in Models
//so, you don't even need an Unload method. Every time you move in "models" the relative model will be loaded
//as the currentModel
//private List<GameObject> clones = new List<GameObject>(); // Remove!!!
//private GameObject temp; //no need for this, so remove
//Renamed UPPER_BOUNDS to highestIndex and made private
//This will always point to the highest index in the models List,
//because the number of elements in the models List determines the highest possible index
//for the availability of an element to be loaded as currentModel
private int highestIndex;
//This is the index of the element in models List that has already been loaded to currentModel
private int currentIndex = -1; // initalize to -1, because no model loaded at start
//Backing field for CurrentModel read-only property,
//so that GameController can always read the CurrentModel through this property
private GameObject currentModel;
//GameController must have a reference to the FileLoader script in order to access this property
//i.e. public FileLoader in GameController (probably in TouchRotation script)
//then drag-n-drop the FileManager GameObject from the Hierarchy
//to the FileLoader field in the Inspector of GameController
public GameObject CurrentModel
{
get { return currentModel; }
}
// Use this for initialization
void Start()
{
ObjReader.use.autoCenterOnOrigin = true; //it seems this doesn't belong here... maybe moved to TouchRotation?
TouchRotation tRotation = this.gameObject.AddComponent<TouchRotation>(); //remove
tRotation.camera = this.camera; //remove
if (models.Count > 0)
{
//Set the highest index possible, which depends on the number of models available in the models List
highestIndex = models.Count - 1;
//Load the first model, so the user has something to see when the app starts
//without having to click on any buttons
currentModel = Instantiate(models[0]) as GameObject;
////If you decide to populate the models List with instantiated GameObjects in the scene (in a disabled state),
////rather than prefabs from Assets\Models, then replace the above line currentModel = Instantiate(models[0]) as GameObject;
////with the following:
//currentModel = models[0];
//currentModel.SetActive(true);
currentIndex = 0;
}
else
{
Debug.LogError("No Models found! Please add at least one model in the Models List.");
Debug.Break();
}
}
//Instantiates the GameObject at index position "index" in the models List
//and references it with the currentModel variable
public void loadModel(int index)
{
//make sure that when index is higher than highestIndex
//it will be reset to 0 (according to specification)
//
//Since Previous() is the only point where currentIndex is decremented
//but it will never make a loadModel() call unless currentIndex > 0,
//the following condition index < 0 will always be FALSE.
//So condition might appear redundant at this point, and may be removed.
//However, it was included as a safety measure, in case of future code changes
if (index > highestIndex || index < 0)
{
index = 0;
}
//Previous GameObject instance referenced by currentModel will be Garbage Collected
currentModel = Instantiate(models[index]) as GameObject;
////If you decide to populate the models List with instantiated GameObjects in the scene (in a disabled state),
////rather than prefabs from Assets\Models, then replace the above line currentModel = Instantiate(models[index]) as GameObject;
////with the following:
////
////Disable current model - no need to check for null, since initialized in Start()
//currentModel.SetActive(false);
//currentModel = models[index]; //reference new model
//currentModel.SetActive(true); //activate new model
currentIndex = index;
}
//No need for an unloadModel() method
//Just assign a new model from the model List to the currentModel field
//
//public void unloadModel()
//{
// Destroy(clones[currentIndex]);
//}
//Renamed "incrementCounter" to Next because it's more descriptive of what it does
//e.g. when the counter is at the highest number and then it goes to 0, it's actually decrementing the counter...
//
//Load the Next model
public void Next()
{
//Since loadModel(0) is called in Start() this will always be TRUE
//So the if{} statement might appear redundant at this point, and may be removed.
//However, it was included as a safety measure, in case of future code changes
if (currentIndex >= 0)
{
//after the model at the highestIndex has been previously loaded in currentModel
//the parameter for the following loadModel() call will equal to highestIndex + 1
//but it will be reset to 0 inside the loadModel() method,
//in which case the first model (at index 0) will be loaded.
//Therefore, when "Next" is called, it will call loadModel(),
//with its parameter ranging from 0 to highestIndex + 1
loadModel(currentIndex + 1);
}
}
//Renamed "decrementCounter" to Previous because it's more descriptive of what it does
public void Previous()
{
//if Next() has not be called at least once after the first element in the Models List is loaded
//and currentIndex increased to a value greater than 0, calling the Previous() method will not do anything
//so, loadModel() will never be called with a parameter that is smaller than 0.
//Therefore, when "Previous" is called, it will call loadModel(),
//with its parameter ranging from 0 to highestIndex - 1
if (currentIndex > 0)
{
loadModel(currentIndex - 1);
}
}
}
By the way, the error will not happen at line 67, because when that part of the code runs count = 0, because that's the only time that temp is stored in clones (initialized in Start()).
Alright I changed upper bounds to 2 but I'm still getting the same error, debugging has shown me exactly where it breaks but I don't understand quite yet how to fix it.. I click the plus once and it loads element 1 into the scene, and the out of range exception is from the else in increment right after it increments the count and then tries to loadmodel.
Setting UPPER_BOUNDS = 2 would not help! Change it to 10 will. Or load (say) 10 models in clones! The rule is this: Your UPPER_BOUNDS must be greater than your loaded models in clones, otherwise you'll get the error at the lines I showed you.
Alternatively, you should change the logic in those sections of the code.
I'll have another look at your code to see what can be done, and I'll let you know.
As I'm looking at your code I see several possible improvements. However, I need to know how you have setup your hierarchy, and on which GameObject this script is attached. $$anonymous$$y guess is (for the simplest configuration) that you have an empty GameObject in the scene with this script on it. You also have "Next" and "Previous" buttons that are referencing this script.
Lot of guesswork here. It would help to know the exact setup.
Thank you so much pako!!! You have gone much further then I expected anyone to, and I appreciate this very much! Thank you!!
Answer by Mmmpies · Dec 27, 2014 at 09:33 PM
When you start a list there's a count of zero and null items in the list.
If you add an item then you have a count of 1 but the item number is 0.
Try moving the count++; to after loadModel();
I think you're trying to add a model that's not there.
Sorry if this is a bit vague it's been a long day of cancelled trains and missed connections so my brain isn't functioning at, I was going to say 100% but I think, at all is closer to the truth.
I tried moving count++ to after load$$anonymous$$odel(); and it does something different.. for whatever reason clicking the plus button once loads element 0 and element 2 in the list at once (so they are inside eachother) The error is still argument out of range Parameter name index.
But it's still saying line 67 is what's throwing that index out of range?
Yeah, which is making me wonder if the index is out of range in clones or in models. Because it's calling the load$$anonymous$$odel() function.
Best advice is to Debug.Log wherever you increase or decrease count.
Out of range is pretty literal it means the value you've given me isn't in the range so it's got to be the count value and the ListName.Count.
Create a Debug.Log line that shows all of that so
Debug.Log("count = " + count + " clones.Count = " + clones.Count + " models.Count = " + models.Count + " in ...");
replace ... with which part of the script your in so the output is obvious.
If you get any points where count is a value not within clones or models Count range then look at that bit.
Should be able to copy and paste most of that Debug line and just change the last bit.
Answer by Nadafy · Dec 29, 2014 at 11:34 AM
Problem is in this line:
clones[count] = Instantiate(models[count]) as GameObject;
You should use clones.Add and clones.Remove!
You can use clones[i] after adding item to the list! ( 0 <= i < clones.Count )
This is not the line that throws the error!!! But as I have said in my comments, the use of Instantiate in this way is questionable. Your answer takes for granted that the GameObjects in models list are already instantiated in the scene. What if they are prefabs that need to be instantiated at the same time as adding them to clones List? This is why I asked for clarifications.
Your answer
Follow this Question
Related Questions
A node in a childnode? 1 Answer
Function throws 'argument out of range, parameter: index' error 0 Answers
Cycling through a List with constantly changing element count. Running into problems D: 2 Answers
Runtime IndexOutOfRangeException when testing on iPhone 1 Answer
List of type Button has size set to zero at runtime 0 Answers