I have been playing around with this command, which finds all children with the tag equal to “Active” of a given parent:
public GameObject getActive()
{
GameObject active_obj = null;
foreach (Transform child in parent.transform)
{
if (child.tag == "Active")
{
active_obj = child.gameObject;
Debug.Log(active_obj);
break;
}
}
Debug.Log(active_obj);
return active_obj;
}
I noticed, that due the hierarchy of game objects:
The method always returned null. This makes sense, since I was only looking at the children[Green, Yellow, Blue Red], which do not have a tag that is equal to “Active”.
However, the children of [Green, Yellow, Blue Red] which are called GoalOn will at Random have such a tag set. So I was curious if it would be possible to adapt the method above so that instead of only searching through the child of a parent, if it is possible to search through all objects below a parent as indicated by the green line.
I was either thinking that since 1) [Green, Yellow, Blue Red] are siblings and at the same level at the hierarchy whether it is possible to use that or 2) to simply search through the agents children children’s.
Here’s the cleanest easiest way to search all children:
Transform[] AllChildren = GetComponentsInChildren<Transform>();
List<Transform> OnesThatIWant = new List<Transform>();
foreach( var child in AllChildren)
{
// here is where you decide if you want this (replace it with whatever you like)
if (child.gameObject.tag == "Active")
{
OnesThatIWant.Add( child);
}
}
Now you can either return the list OnesThatIWant or else return the array of it with:
return OnesThatIWant.ToArray();
NOTE: this searches itself too (Which ever GameObject this script is on)
How would the above change if there is always only one object at a given time that has the tag set to “Active” ? If we use a list, dont we need to keep track of indexing as well ? Or are we simply replacing the first element of the list with whichever child has the active tag ?
You could sort the list by some stable value (like name?) then return the first one.
Or you could just iterate the list like any other indexed collection.
Another way is to make it a generator object that yields on thing at a time:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GeneratorExample : MonoBehaviour
{
// this is the generator
IEnumerable<string> Sayings()
{
yield return "This";
yield return "That";
yield return "The other";
}
void Start ()
{
// this is pumping an instance of it:
foreach( string saying in Sayings())
{
Debug.Log( saying);
}
}
}
I tried out your suggestion however I do obtain the following error (line 15 below):
Assets/Game_Manager.cs(155,16): error CS0029: Cannot implicitly convert type 'UnityEngine.Transform[]' to 'UnityEngine.GameObject'
Did I miss something ?
public GameObject getActive()
{
Transform[] AllChildren = GetComponentsInChildren<Transform>();
List<Transform> OnesThatIWant = new List<Transform>();
foreach (var child in AllChildren)
{
// here is where you decide if you want this (replace it with whatever you like)
if (child.gameObject.tag == "Active")
{
OnesThatIWant.Add(child);
}
}
return OnesThatIWant.ToArray();
}
You asked for code to return all the objects that are active, so obviously it is a collection of some kind, in this case an array of Transforms.
A collection does not have a single gameObject associated with it. That’s like asking, “What is the engine type of all the cars on the freeway right now?”
You can iterate them as any collection, or make a generator the way I showed above. But you must understand what you are actually working with. It’s not optional.
I see your point, but maybe thats where we talked past each other. I am sorry, I should have been clearer. But thanks a lot for helping. I learned a lot and this community is really great. Give me a chance to explain though.
There are 4 objects, only one of them at a given time has the tag active. There are never two or more objects, which have this tag at the same time. So we never really have a collection. Also we then use the relative position of the active target as input for a robotic agent. Since the robot only sees one object at a time a returning a gameobject is more intuitive in this case.
I personally dont mind what we would use, sadly the robotic agent does. It creates quite a few input problems and issues with other methods. In this case, it would really be more convenient to return a gameobject instead. What do you think ? Does that somewhat make sense?
Maybe I should have been clearer with what I want to do. I want to iterate over the parents, children and the children’s children to find the single object that has the tag == “Active”, store the game object. That is to find the gameobject that has the tag active, by checking each gameobject below the parent, including siblings branches and their respective children.
If you are sure that only precisely one object is active, then you know this will return a one-element array.
Instead of returning the list or the array, you can just return the first one:
return OnesThatIWant[0];
Now if you have ZERO objects in that list, it will cause an error, so when there are zero objects it is traditional to return null, so instead do this:
if (OnesThatIWant.Count > 0)
{
return OnesThatIWant[0].gameObject;
}
return null; // never found an active!
Another way is to just return the first one you find during the search:
Transform[] AllChildren = GetComponentsInChildren<Transform>();
foreach( var child in AllChildren)
{
// here is where you decide if you want this (replace it with whatever you like)
if (child.gameObject.tag == "Active")
{
return child.gameObject; // return the first one we find
}
}
return null; // didn't find one
Kurt, thank you so much. I believe it works. I will tackle the Null reference problem you mentioned in the other tread just now. For those interested, this should also work :
public GameObject getActive()
{
Transform[] AllChildren = GetComponentsInChildren<Transform>();
GameObject active_obj = null;
List<Transform> active_obj_list = new List<Transform>();
//foreach (Transform child in parent.transform)
foreach (var child in AllChildren)
{
//if (child.tag == "Active")
if (child.gameObject.tag == "Active")
{
active_obj_list.Add(child);
active_obj = active_obj_list[0].gameObject;
break;
}
}
return active_obj;
}