For each child of parent - do X

Hello. Everyone here has been so helpful so far, but it appears I am stuck once more. How would I write code that is essentially:

'For each child of public GameObject ‘Deck’, that is also of the class ‘CardDisplay’ return the name and save it to public Array ‘Deck1’.

I, of course, wouldn’t ask without trying first so here is my completely non working attempt:

using UnityEngine;
using UnityEngine.UI;
//using System;

public class DeckLoader : MonoBehaviour
{
    public Text deckName;
    public GameObject DeckLeader;
    public string[] Deck1;
    public Text Leader;
    public GameObject deck;

    public void CreateDeck()
    {

        Leader.text = DeckLeader.name;

        //Find anything with the class CardDisplay. Why is there the array [] symbol???
        CardDisplay[] deck = GetComponentsInChildren<CardDisplay>();
        foreach (CardDisplay child in deck)
        {
            //'this' seems to reference this GameObject and no the children
            string Name = this.name;
            int i = 0;
            Deck1 = Name;
            i++;
        }
    }
}

The error I am getting is:

IndexOutOfRangeException: Index was outside the bounds of the array.
(wrapper stelemref) System.Object.virt_stelemref_sealed_class(intptr,object)

The error is likely because you’re trying to assign a string (Name) to an array (Deck1). For that to work, you’d need to assign it to some position in Deck1, such as Deck1[0] = Name; Since you created the variable i', maybe you intended to assign the Name to Deck1*?* *But I wouldn't expect there's any good reason to expect that Deck1 will contain exactly as many elements as there are CardDisplay objects as children of the given object. You'd need to make sure that Deck1 has space for all of the items. You can't just assign values to Deck1 if you haven't allocated space for it first. Something like Deck1 = new string[deck .Count]’ might be sufficient, called just before entering your foreach loop.*

You were correct. For anyone wondering, I needed to declare a size for the array or else it has no spaces in it by default. You do this by adding the following to what I have above:

public string[] Deck1 = new string[15];

This makes it so Deck1 is an array that has 15 slots. Now the other problem I am having is the ‘this.name’ portion of the code only references ‘deck’. The foreach loop doesn’t actually reference the children. What do I do?

It would appear that you are mixing up GameObjects (which are objects that contain other objects and components), and components themselves. All your GameObjects are members of the class GameObject. But these objects may have components of many different classes attached - and that is what you are really looking for: All the objects in your deck that have a component of a certain class attached to them.

So, to create a deck you will not only need to create an object with n components (one for each card) attached, but one child object per card (of type GameObject) with a component of type CardDisplay attached.

The GetComponentsInChildren method then returns references to the components that each have a reference to their Object. Now, beware of unity’s shortcuts.

Not knowing about the Definition of your class CardDisplay, you should be aware of some implicit shortcuts that are there to help you, but usually confuse the unwary.

First, be aware that ‘this’ really means ‘this’, i.e. the script that is executing, NOT the script you are processing. In your Loop

foreach (CardDisplay child in deck)  {
          //'this' seems to reference this GameObject and no the children
           string Name = this.name; // <- WARNING: Accessing this (DeckLoader), not child (CardDisplay)
           int i = 0; // WARNING: re-setting i to Zero each loop
           Deck1 = Name; //<- WARNING: assigning a string to an Array - should probably be Deck1[i];
           i++;
       }

this.name is automatically extended to ‘this.gameObject.name’ (VERY confusing, because this only happens if you do not have an attribute ‘name’ in your component) and refers to the gameobject that contains the script running from, e.g. the object that holds the DeckLoader, not the child object (of type CardDisplay) that you probably intended. To access the GameObject attached to the component, use

string cardName = child.gameObject.name

Also, you seem to be mixing foreach and an index i – usually a sign that something is amiss. You’d probably be better off using a classic for Loop with the upper bound derived from deck.Length.In your Loop, you are also re-Setting I to 0 during each Iteration, so each Access to Deck[i] will be to Deck[0]. The i++ will be forgotten in the next Loop, when the line

i = 0;

is encountered.

And finally, you seem to be assigning a single string value (Name) to an entire Array. That shouldn’t even compile…

Thank you for the reply. I’ll try to go through each piece careful to see if I understand. This is what I left off at after the other person’s answer (which was very helpful):

int i = 0;
        foreach (CardDisplay child in deck)
        {
            string Name = deck[i].name.ToString();
            Deck1[i] = Name;
            i++;
            Debug.Log(Name + " added to the deck '" + deckName + "'.");
        }

It ‘works’ in that it compiles and actually iterates through by the exact number of children. But as you said… it’s not actually referencing the children so it just puts generic names into the array. ‘Name (UnityEngine.UI.Text)’

I’m going to try what you said (child.gameObject.name) right now. And your note about a GameObject and components was very helpful.

Success. Thank you both, it works perfectly. For anyone else that needs the answer, the final code I used is:

{Edit} Don’t use my code. Use the reply below this one.

CardDisplay[] deck = GetComponentsInChildren<CardDisplay>();

        int i = 0;
        foreach (CardDisplay child in deck)
        {
            string Name = child.gameObject.name;
            Deck1[i] = Name;
            i++;
            Debug.Log(Name + " added to the deck '" + deckName + "'.");
        }

That technically works, but it would make more sense to someone looking at it if you used a for loop instead of a foreach loop. They’re very similar, but a for loop handles the `i’ variable itself. So, something like this:

for (int = 0; i < deck.Length; i++)
{
    string Name = deck[i].gameObject.name;
    Deck1[i] = Name;
    Debug.Log(Name + " added to the deck '" + deckName + "'.");
}

This avoids the possibility that you forget to increment `i’ somewhere in your loop, which becomes a bigger concern as the content of your loop gets more complex.

Also, just be careful about hard-coding the array length like that (via `Deck1 = new string[15];') If GetComponentsInChildren ever returns more than 15 things, you’ll get an error running your code.

Your code is shorter and easier to understand. I’ll edit my comment to let people know to use yours.