I have , for example, 9 point soruce lights. Sometimes I want to access a specific instance of them; say light no. 3.
I have a code similar to this;
//first I setup a temporary array where I find all instances of the script I want.
ArmorLightScript[] armorLightTemp = lights.GetComponentsInChildren<ArmorLightScript>() ;
//I now know how many instances I have, so I initialize my array
armorLightSc = new ArmorLightScript[armorLightTemp.Length] ;
int i;
//then I access each of the instances, find the id, and sort my array accordingly.
foreach (ArmorLightScript tempSc in armorLightTemp )
{
i = tempSc.slotID;
armorLightSc[i] = tempSc;
}
I now know exactly whitch instance of the lightScript that is placed at each index of the armorLightSc array.
The above method works, but it has some obvious weaknesses;
If I don’t give each instance an increasing ID, starting at 0 (since the ID must match the index in the final array) I will get an error.
The ID can be set from a constructor script, or in the inspector as a public variable. The latter is not ideal, as any changes to the prefab will mess up the public variables.
This is complicated. Complicated things break in a complicated way.
Is it an option to set everying up in the inspector? It is tempting to assume that the hirachy is representative for the indexation of GetComponentsInChildren, but is that correct?
Parent
light 0
light 1
light 2
Will GetComponentsInChildren on that hirachy always place light 1 at index 1?
How do you expect to access these lights when you do so?
Is it going to be by the id?
If it is the id MUST be unique, and the order is unnecessary.
You could just stick them in a dictionary if you wanted.
In this example I change ‘slotID’ to ‘ID’ and I assume it’s of type ‘string’ so you can give it unique names rather than integer values
using UnityEngine;
using System.Collections.Generic;
public class SomeScript : MonoBehaviour
{
[System.NonSerialized()]
private Dictionary<string, ArmorLightScript> _table = new Dictionary<string, ArmorLightScript>();
void Start()
{
_table.Clear();
foreach(var light in lights.GetComponentsInChildren<ArmorLightScript>())
{
_table[light.ID] = light; //note - if 2 lights have the same ID, the last light found with that ID is stored
}
}
public void DoSomethingWithLight(string id)
{
ArmorLightScript light;
if(!_table.TryGetValue(id, out light)) return; //stop now if no light with id is found
//do what you need with the light
}
}
There’s absolutely no reason to have to do this from a script. If you want 9 lights to be in 9 specific positions in the array, then make the array public, drop off the initialization in the script, and add all of them using the Inspector directly with dragging and dropping. If you drag them into the array in the order you want, they’ll always be in that order, and you can call them using the array index without all of this silliness.
For future reference, however, pulling items in using GetComponentsInChildren pulls them in the same order that they’re in your scene asset list (I think), but if you’re relying on this order for some reason then there’s a good chance you’re doing something wrong.
GetComponentsXYZ does not have a guaranteed order, you could run into an issue where it returns light5 before light3 after light7 further down the line. Do not depend on that order of you’re going to have a massive headache somewhere in the future.
Out of curiosity, if it doesn’t go by the hierarchy placement, do you think/know if it goes by the instance ID number? The order being unreliable is not in question, but I’m sure there’s SOME sort of logic to it.
I imagine it goes off the order in which they’re added to the scene / hierarchy but I couldn’t tell you offhand, just that it’s not guaranteed to be in a specific order.
What it means is that Unity may at any time decide to change the way they get the components, which would change the order.
Currently it appears to be by hierarchy placement. But Unity does not guarantee it is.
When dealing with ordered collections, you want guaranteed definitions.
This is more of a documentation/support thing. When programming against an API you program against what they tell you the interface (the functions you call) are said to do. Not what the inner guts appear to do. As they may change the inner guts at any time, while preserving the interface as defined in the documentation.
Yes, I access them by armorLightSc*.*DoSomething().
So, indexing by hirachy is out.
Using public arrays is also not very tempting. Manually dragging and dropping 9 elements might be ok; but what if I have 20… or 50? That is just inconvinient, manual and quite probably I’ll drag the same light in twice or something.
The dictonary solution seems neat. It’s slightly inconvinient that the public vars reset if I apply to the prefab. Would it be a good idea to use transform.name as the ID? The script is vulourable to changes of the name, but perhaps that is OK?