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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Array index is out of range when it is not

Discussion in 'Scripting' started by ATLAS-INTERACTIVE, Mar 8, 2015.

  1. ATLAS-INTERACTIVE

    ATLAS-INTERACTIVE

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    mid-testing, the console prints that my Array in the following script is our of range, it can't be because it works until this appears.
    Code (JavaScript):
    1. #pragma strict
    2. import UnityEngine.UI;
    3.  
    4. private var material : GUIBuild;
    5. private var canBuild : boolean = false;
    6.  
    7. var BuildText : Text;
    8. var requirementsWood : Text;
    9. var requirementsMetal : Text;
    10. var requirementsBrick : Text;
    11. //var TextToUse = "";
    12. private var drawGUI = false;
    13.  
    14. var prefix : String = "";
    15.  
    16. private var start : int = 0;
    17.  
    18. //var seg1 : boolean = false;
    19. //var seg2 : boolean = false;
    20. //var seg3 : boolean = false;
    21.  
    22. var segments : boolean[] = BooleanArrayTrue(100);
    23. var resourceWood : int[];
    24. var resourceMetal : int[];
    25. var resourceBrick : int[];
    26. var counter : int = 0;
    27. function BooleanArrayTrue (size : int) : boolean[] {
    28.      var boolArray = new boolean[size];
    29.      for (var b in boolArray) b = false;
    30.      return boolArray;
    31. }
    32.  
    33. function Start()
    34. {
    35.     material = GameObject.FindWithTag("Player").GetComponent(GUIBuild);
    36.     /*for(var i : int = 0; i < 100; i++)
    37.     {
    38.         segments[i]=false;
    39.     }*/
    40.     segments[0] = true;
    41. }
    42.  
    43. function OnTriggerEnter (Col : Collider)
    44. {
    45.     if(Col.tag == "Player")
    46.     {
    47.         canBuild = true;
    48.         drawGUI = true;
    49.        
    50.     }
    51. }
    52.  
    53. function OnTriggerExit (Col : Collider)
    54. {
    55.     if(Col.tag == "Player")
    56.     {
    57.         canBuild = false;
    58.         drawGUI = false;
    59.     }
    60. }
    61.  
    62. function Update()
    63. {
    64.  
    65.     if (drawGUI == true)
    66.         {
    67.             BuildText.text = "Press B To Build";
    68.             requirementsWood.text = "Wood: " + resourceWood[counter];
    69.             requirementsMetal.text = "Metal: " + resourceMetal[counter];
    70.             requirementsBrick.text = "Brick: " + resourceBrick[counter];
    71.         }
    72.     else
    73.         {
    74.             BuildText.text = "";
    75.             requirementsWood.text = "";
    76.             requirementsMetal.text = "";
    77.             requirementsBrick.text = "";
    78.         }
    79.  
    80. if(canBuild)
    81.     {
    82.         if(Input.GetKeyDown("b"))
    83.         {
    84.             for(counter = start; counter < segments.Length; counter++)
    85.             {
    86.             Debug.Log (resourceWood[counter]);
    87.             if((material.currWood >= resourceWood[counter] && material.currMetal >= resourceMetal[counter] && material.currBrick >= resourceBrick[counter]) && segments[counter])
    88.             {
    89.                 var temp = prefix + counter;
    90.                
    91.                
    92.                 GameObject.Find(prefix + counter).GetComponent(MeshRenderer).enabled = true;
    93.                 GameObject.Find(prefix + counter).GetComponent(MeshCollider).enabled = true;
    94.                
    95.                 //Set materials to zero
    96.                 material.currWood-=resourceWood[counter];
    97.                 material.currMetal-=resourceMetal[counter];
    98.                 material.currBrick-=resourceBrick[counter];
    99.                
    100.                 //segments[counter] = false;
    101.                 segments[counter+1] = true;
    102.                 start = counter +1;
    103.                 counter = 9999;
    104.             }
    105.             }
    106.         }
    107.     }
    108. }
     
  2. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    Where exactly do you get the error? When the the exception is thrown, what is the size of the array and which index are you trying to access?
     
  3. ATLAS-INTERACTIVE

    ATLAS-INTERACTIVE

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    We are trying to get this fixed as soon as possible, anyone with any ideas on this and why the required materials are no re-displayed once re-entering the trigger, don't hesitate to post.
     
  4. ATLAS-INTERACTIVE

    ATLAS-INTERACTIVE

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    @Dantus It is on line 69...supposedly.

    Code (JavaScript):
    1. requirementsWood.text = "Wood: " + resourceWood[counter];
     
  5. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    I guess once your if statement is done, update tries to access resourceWood[9999] (and thus it would also try it with the other ressource arrays) due to your assignment 'counter = 9999' ...
     
  6. Ohrm

    Ohrm

    Joined:
    Dec 3, 2012
    Posts:
    4
    Don't see why your using an array here as it seems like just a basic resource count but i might be wrong. However you never declare how big the array actual is. You need to use something like "resourceWood = new int[100]" in the start method.
     
  7. ATLAS-INTERACTIVE

    ATLAS-INTERACTIVE

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    So your saying that removing counter=9999; would fix this problem?
     
  8. Timelog

    Timelog

    Joined:
    Nov 22, 2014
    Posts:
    528
    If you debug the resourceWood array, are you sure there is data during runtime? I assume that you have filled it via the editor?
     
  9. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Why don't you try it?
    If you want to break out of the loop, use a 'break' instead. Also your ressource array should have at least the same size as your segment array (probably even one more) as you're counting up to segments.Length.
     
    Last edited: Mar 8, 2015
  10. ATLAS-INTERACTIVE

    ATLAS-INTERACTIVE

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    Removing counter=9999; makes the whole building build instantly, instead of in stages with the resources required, I remember why I added it now after seeing it without.
     
  11. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    Have you tried using 'break' as i suggested above?
     
  12. Ohrm

    Ohrm

    Joined:
    Dec 3, 2012
    Posts:
    4
    If you have an array that has the number 9999 in the square brackets then you need to initialize an array with that number but no where in the code is a "resourceWood = new int[10000];" unless you have populated it in the editor. Seriously though why are you using an array for this it looks like it could be done with a simple int if its just a resource count
     
  13. ATLAS-INTERACTIVE

    ATLAS-INTERACTIVE

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    If you were to use the script, you may understand why it is laid out this way, I am trying to build something (A house in this case) using modular pieces, and requiring resources to do that, until now, you just needed 1 of everything until I got it all working, now I want each module to require a different amount of each resource, I do populate the resources list in the editor to set how much of each item is required for each specific module.

    I could not find anything with the breaks added in and I have read through the code repeatedly to find anything that may be causing these problems, but I cannot see anything.

    Declaring the resources as new ints or giving them numbers in the square brackets brings up massive amounts of errors.
     
  14. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    What do you mean by 'i could not find anything with the breaks added in' ?
    It's not supposed to log or show something, it's just to cancel the loop instead of having counter=9999 not fullfilling the loop's condition anymore.
    What exactly do you get when you replace 'counter = 9999;' with 'break;'?

    If there's still an error (the indexOutOfBoundsException), than your ressource arrays are not big enough. As stated above, with your current logic those have to have the size 101 (max index 100) because after the last possible iteration, counter will be 100.

    I think the main problem is that it's quite messy although there's not going on a lot yet.
     
  15. ATLAS-INTERACTIVE

    ATLAS-INTERACTIVE

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    When I replace counter = 9999; with break; the resources the player has are allowed to go below 0, they display as 0 but you are still able to build the next level of the building. Once you go below 0 on one of the resources it throws the out of range thing on lines 102 and 69.

    102:
    Code (JavaScript):
    1. segments[counter+1] = true;
    69:
    Code (JavaScript):
    1. requirementsWood.text = "Wood: " + resourceWood[counter];
     
  16. Suddoha

    Suddoha

    Joined:
    Nov 9, 2013
    Posts:
    2,824
    First one: still the issue with your array size...

    When your counter is 99, the last iteration of the loop takes place, and down there you try to access segments[99+1] then, which is not allowed because valid indeces are 0-99 in your case.

    Second error may be the same, but rather due to the fact that your counter is able to count to 100 after the last iteration has completed.
    #########

    *big edit
    I still don't understand why you use the array of booleans.

    The code in the 'if' can only be executed when segments[counter] is true plus the other conditions as well, but once you entered the 'if' it is sure the next segment (which is segments[counter+1]) will be set to true, which means that the condition will be fullfilled in the next iteration step in any case unless you control it from somewhere else, too.

    I can imagine that your intention was to prevent your system from skipping one segment due to the lack of ressources and build the following instead if they need less, but there's probably a better solution to this.
    Also, a question that came up is: do you want to build continuously after pressing the key (which explains the use of the for-loop, but not the solution to cancel it with the value of 9999) or do you want to build one segment per keyDown (which contradicts the need of the loop here)?
     
    Last edited: Mar 8, 2015
  17. ATLAS-INTERACTIVE

    ATLAS-INTERACTIVE

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    The array of booleans controls what segments of the building are visible/have been built.
    And yes, I did want to build one step at a time, regardless of having the resources for multiple segments, I do want to do one segment per KeyDown.

    Like I said, I have never used Arrays before, this was a huge trial-and-error effort on my part, I don't know what parts are necessary and some parts I don't even know what they do, I am not the only person working on this, but the other guy doesn't do Unity script, mostly Web Development, which has left us kind of stuck, we know exactly what we want, but are repeatedly stumbling at each hurdle trying to get there.
     
  18. zeidrich

    zeidrich

    Joined:
    Dec 28, 2013
    Posts:
    1
    Your problem is mostly that you are uncomfortable with arrays, and data structures generally.

    When you create your arrays, you don't give them a size. This means they're going to be given a size when they're initialized. Since you don't initialize them in this script they're probably being initialized with the inspector.

    So when you type it into the inspector you give it a size, say 3. Now there is space for three ints, requirementsWood[0], requirementsWood[1], and requirementsWood[2].

    If you were to at some point ask what the value of requirementsWood[9999] it wouldn't make any sense. The bounds of the indices of the requirementsWood array are from 0 to 2, and asking for 9999 is out of bounds.

    So there's a couple of trouble spots. First is counter, second is segments.

    First I'm going to nitpick about your function named BooleanArrayTrue. This looks like a function that creates an array of 100 booleans which are all set to false. The function name BooleanArrayTrue leads someone to believe that it would create an array of booleans set to true. It's actually named describing the opposite of what it's doing.

    So next segments[0] is set to true when the script starts. So it starts off with segments[0] being true, and segments[1] through segments[99] being false.

    Counter is set to 0 when the script initializes. Update runs every frame. It checks if b is down and canBuild, and then goes into a for loop. It has counter go from start, which is initialized as 0, to segments.Length which is 100 and increment by one each loop.

    It checks material.currWood, metal, brick against resourceWood[0 to 100], resourceMetal[0 to 100] and resourceBrick[0 to 100] and that segment[0 to 100] is true.

    If all of those pass, it enables a couple of components, and removes the values from currWood, Metal and Brick from resourceWood, Metal and Brick.

    Then it sets segment[counter+1] to true, sets start to counter+1, and counter to 9999.

    The next time update comes around, it tries to read resourceWood[9999] which gives you the out of bounds error.

    You put the counter = 9999 in because without it, it would loop through all of the resources right away, since you've just set segments[counter+1] to true, and the next for loop is going to check to just see if it's true.


    So instead lets look at what you're trying to do, and find a simpler way to do it. Since you're advancing through the segment array without storing any data, there's no reason segment should be an array of bools instead of an integer. You're starting as 1000000... then going 11000000... then going 111000000. For this implementation that's no different than just saying 0, 1, 2. If you have segments indicating the number of segments completed as an int or something that would work better.

    Second, the integer that stores how many segments are completed can act as your counter. You won't need another counter, because the segments can store the number of segments completed.

    I would also recommend building an array of objects rather than 3 different arrays for your resources. If you have different arrays of different lengths, you could accidentally make one of them longer than the others and run into another out of bounds issue. So something like:

    class buildingResources{
    var wood : int;
    var metal : int;
    var brick : int;
    }
    var resource : buildingResources[];

    then you can say resource[0].wood is the wood requirement for the first segment, resource[0].brick is the brick requirement for the first segment, resource[1].wood is the wood requirement for the second segment, etc.

    Similarly, resource.Length would tell you how many segments there are.

    Then you could start with

    segment = 0

    and if(canBuild) you could say something like:

    if segment is less than resource.Length,

    check for the correct number of resources to build from resource[segment].wood, resource[segment].brick and resource[segment].metal

    Then if it succeeds, you can reduce the current materials and then increment segment by one

    It doesn't even have to be a loop. This way each time this update runs when B has been pressed, it will do one segment, and then do the next one.

    This way segment will go from 0 to resource.Length - 1, So if you have 5 entries for resources, segment will go from 0 to 4, resource[0] to resource[4] will hold the values for each segment, and each time you handle the key press, segment will increment once if it successfully completed the build. Once segment is 5, it will stop doing anything since 0-4 have been completed and 5 is out of bounds anyways, so you won't crash.

    The other thing you would have to do is somewhere check to see if segment was greater or equal to resource.Length before you tried to read data resource[segment] such as when you are drawing your build text. You could just check to see if segment >= resource.length and if so, say 0 or "complete" or something.
     
    Deleted User likes this.
  19. ATLAS-INTERACTIVE

    ATLAS-INTERACTIVE

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    This all looks very complex, but not being a coding wizard, I have no idea how I am to fix any of the problems you have pointed out without destroying it further.
     
  20. Deleted User

    Deleted User

    Guest

    Sometimes you have to destroy in order to repair.
     
    Dantus likes this.
  21. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    The console never lies. It just speaks a different language. First you need to do some basic debugging.

    Add this code at line 66

    Code (CSharp):
    1.  
    2. Debug.Log("Accessing element " + counter + " of " + resourceWood.Length);
    3.  
    For a quick fix you can simply abort out if the counter is to high. Something like this.

    Code (CSharp):
    1.  
    2. if(counter < resourceWood.Length){
    3.     // Update the texts here
    4. }
    5.  
    Its a hack, and I haven't looked closely at your overall structure to see what effect it might have. But its the simplest way to do the job. Just be wary if you end up with a bunch of hacks strung together. Sometimes its better to throw the whole thing away and start from scratch.