- Home /
enums and array indexing - weird issue, maybe compiler bug?
This one is properly crazy - I'm using a fairly standard practice of ending my enum lists with a 'maxValue' entry, so I can easily create arrays using that value which resize as the enum list changes. This has been working fine, but suddenly today I hit play and am getting index out of bounds errors where it shouldn't be possible.
So the code - I have this enum:
public enum DamageType
{
// general damaging types
none, fire, slice, crush, physical, pierce, ice, spirit, nature, light, dark, death, energy, explosive,
// special types
healing, blinding,
// generic magic resistance
magic,
// custom type for room spawning
roomBuilder,
maxValue
}
and in a class I have this variable:
public float[] damageModifiers = new float[(int)DamageType.maxValue];
also in this class I call a method to set the defaults:
public virtual void setDefaults()
{
// set default values in case they are left blank in the ini
Debug.Log("dmg type max value is " + (int)DamageType.maxValue);
for (int i = 0; i < (int)DamageType.maxValue; ++i)
{
Debug.Log("Setting dmg mod default for " + (DamageType)i + " (" + i + ")");
damageModifiers[i] = 1f;
}
}
I've added in the debug logs to help track down the issue. So the console output now is:
dmg type max value is 18
Setting dmg mod default for none (0)
Setting dmg mod default for fire (1)
// ..... //
Setting dmg mod default for magic (16)
Setting dmg mod default for roomBuilder (17)
IndexOutOfRangeException: Array index is out of range.
Character.setDefaults () (at Assets/Scripts/Behavioural/Character.cs:959)
This is crazy. It's not only a very standard practice, but has been working great for ages. It went wrong after I added a new enum to the list (it was 'magic' not that it should matter). Now even if I remove magic from the list it still bombs out.
This should not happen. All I can think is that somehow Unity is not re-compiling properly and is keeping the value of 'maxValue' as 17 when it should now be 18. But that wouldn't explain why it fails when I remove 'magic' form the list.
I also tried setting maxValue=64, but it still bombs out on 'roomBuilder' (index 17). The debug output did show maxValue as 64 though, so I have no idea how the array could only be 17 elements long!
Is there a way in Unity to 'clean' the compiled object files? (Like in Visual Studio you can go to Project->clean and it will remove all intermediate compile data).
Or does anyone have an idea why this is happening?
i was testing your code here, and i don't get any error add or remove. You could Try right click on script, and Reimport.
Hmm, that would suggest there's something in my code that's somehow modifying the size of the array. I did a search and there's only a handful of spots where the variable is referenced, and none of them do anything that could change the array size.
I tried reimporting, and I even tried deleting the script and replacing it, but still get the same error.
Thanks for trying the code, it's good to know it's something specific to my project. At least I'm one step closer now.
Your console tells you, that maxValue has the correct value. Somehow your array is initialized to short. Try printing dam$$anonymous$$odifiers.length.
Is this all in one class, or is the enum in an interface/abstract class and probably overwritten?
Right now I think I've narrowed it down a corrupted symbols table. I can rename the variable and it will run fine. If I rename it back to the original name, the problem returns.
So I tried cleaning out the intermediary files, but no luck. I think Unity might store the symbols table in some obscure location. So I've submitted a bug report to Unity and will just have to wait and see what comes of it.
I know, the obvious question is: why not just rename and move on? Annoyingly my project makes very heavy use of reflection to match names in data files to names in run-time objects, so rena$$anonymous$$g this variable is a large undertaking :(
I tested your code too, it works fine on my machine as well. It prints all expected values correctly. The only conclusion I have is that the error is somewhere outside the snippet you've posted. If I were you, I would do as @Piflik suggested and carefully track the length of your float array. Put multiple printouts between the place where it is assigned and the loop that fills it with values, and see if you can pinpoint exactly where, along the way, its length changes.
Answer by Wolfram · Jan 07, 2013 at 01:12 PM
You are reserving (int)DamageType.maxValue (=18) entries for your array, but your enum has 19 entries (0..18). Increase the size of your array by 1.
EDIT: Oh, wait, I see your maxValue entry is already supposed to be a marker, not an actual value. In that case I assume a different cause: When I try the code as posted in your question, I don't get an error. So my guess is, you started off with a smaller number of enum entries when you started your script. While you're trying to initialize your array to (int)DamageType.maxValue entries, the very first time your script successfully compiles, this value is carried over to the inspector - where it sticks, even if you change your enum afterwards. Check your inspector - it probably shows 17 for "damageModifiers". To prevent this, initialize your array size in Awake() or Start(), instead of globally (unless you need to modify it in the inspector).
I disagree with this. If the array hadn't been explicitly assigned, maybe. (That is, if his code had just said public float[] damage$$anonymous$$odifiers;) and he had then used the inspector to assign it, then yes. But his code explicitly sets the length of that array at runtime. The editor shouldn't have any power there.
No, but the Inspector/Editor overrides any globally initialized values in scripts. Once the variable was visible in the inspector at least once, any script initializations within the class outside any methods are ignored.
That was it - thanks Wolfram. Unity had indeed serialized the length of the array. It was because when I first made the variable ages ago it was visible in the inspector and got serialized. It's no longer visible, but it still keeps the array size as part of the prefab data. Re-initializing the array before use fixes the issue.
Does he essentially need the
@System.NonSerialized
directive? (Sorry, I don't precisely understand the question.)
As Wolf says "No, but the Inspector/Editor overrides any globally initialized values in scripts..." an annoying gotchya!
@Fattie: Yes, that's another possibility. However, since we're talking C#, that would be [System.NonSerialized].