Title says it all: What is the best way to find out how many children a gameObject has via script.
EDIT 1:
A simple variable transform.hierarchyCount, has been added to Unity 5.4 and above. This should simplify this.
OLD answer for Unity 5.3 and Below:
transform.childCount provided by Adrea is usually the way to do this but it does not return a child under the child. It only returns a child that is directly under the GameObject transform.childCount is been called on. That's it.
To return all the child GameObjects, whether under the child of another child which is under another child then you have to do some more work.
The function below can count child:
public int getChildren(GameObject obj)
{
int count = 0;
for (int i = 0; i < obj.transform.childCount; i++)
{
count++;
counter(obj.transform.GetChild(i).gameObject, ref count);
}
return count;
}
private void counter(GameObject currentObj, ref int count)
{
for (int i = 0; i < currentObj.transform.childCount; i++)
{
count++;
counter(currentObj.transform.GetChild(i).gameObject, ref count);
}
}
Let's say below is what your hierarchy looks like:
With a simple test script:
void Start()
{
Debug.Log("Child Count: " + transform.childCount);
int childs = getChildren(gameObject);
Debug.Log("Child Count Custom: " + childs);
}
This is the result between transform.childCount and the custom function:
Child Count: 2
Child Count Custom: 9
As, you can see the transform.childCount will not count childs of child but only child of the transform. The custom function was able to count all the child GameObjects.
You can access its Transform and use transform.childCount.
Update: this method works for retrieving all children at the first level. If you want to retrieve the children in the overall hierarchy (also at the deeper levels), follow Programmer's answer.
Related
I want to make a scene manager script and to random change scene on trigger enter 2D. But I don't want to load the active scene. I managed to get all the scenes on a list, but i don't know how to remove the active one from the list. I use this code:
int sceneCount = SceneManager.sceneCountInBuildSettings;
Debug.Log("sceneCount: " + sceneCount);
string[] scenes = new string[sceneCount];
for (int i = 0; i < sceneCount; i++)
{
scenes[i] = System.IO.Path.GetFileNameWithoutExtension(SceneUtility.GetScenePathByBuildIndex(i));
scenes[i].Remove(SceneManager.GetActiveScene().buildIndex);
Debug.Log(scenes[i]);
}
Instead of adding all scenes and then removing,a better approach would be that before adding the scene to the array you could check if the index is the same:
if(i != SceneManager.GetActiveScene().buildIndex)
{
scenes[i] = System.IO.Path.GetFileNameWithoutExtension(SceneUtility.GetScenePathByBuildIndex(i));
}
But now there is a small issue the array element of that index is null, you could possible just check if the element is null and don't instantiate it but then we may get some code more complex that it should be...
Instead of an array everything will be easier using a List, in summary List are like arrays but you don't need to specify the size.
List<string> scenes = new List<string>();
for (int i = 0; i < sceneCount; i++)
{
if(i != SceneManager.GetActiveScene().buildIndex)
{
scenes.Add(System.IO.Path.GetFileNameWithoutExtension(SceneUtility.GetScenePathByBuildIndex(i)));
}
}
Finally, you want to know if everything is ok, so we access each element of the List:
//Acces each element of the list
for(int i = 0; i < scenes.Count; i++)
{
Debug.Log(scenes[i]);
}
You can read more of the List class here.
I have a CustomEditor with a ReorderableList that displays a nested ReorderableList for each element. When I drag elements in the outer, parent ReorderableList to change their order, the inner lists don't change their order correspondingly. Here is a gif of what happens:
As you can see, the first item always has one Connected Waypoints, and the second item always has two.
This is the Pathing Agent script:
public class PathingAgent : MonoBehaviour
{
[System.Serializable]
public class ConnectedWaypointsListContainer
{
public List<WaypointObject> connections = new List<WaypointObject>();
}
public List<WaypointObject> waypoints = new List<WaypointObject>();
public List<ConnectedWaypointsListContainer> connectedWaypoints = new List<ConnectedWaypointsListContainer>();
}
These are the relevant parts of the CustomEditor:
waypointsList = new ReorderableList(serializedObject, serializedObject.FindProperty("waypoints");
SerializedProperty connectedWaypointsProperty = serializedObject.FindProperty("connectedWaypoints");
...
waypointsList.onReorderCallbackWithDetails = (ReorderableList list, int oldIndex, int newIndex) =>
{
connectedWaypointsProperty.arraySize++;
connectedWaypointsProperty.GetArrayElementAtIndex(connectedWaypointsProperty.arraySize - 1).objectReferenceValue = connectedWaypointsProperty.GetArrayElementAtIndex(oldIndex).objectReferenceValue;
if(newIndex < oldIndex)
{
for(int i = oldIndex; i > newIndex + 1; --i)
{
connectedWaypointsProperty.MoveArrayElement(i - 1, i);
}
connectedWaypointsProperty.MoveArrayElement(connectedWaypointsProperty.arraySize - 1, newIndex);
}
else
{
for(int i = oldIndex; i < newIndex - 1; ++i)
{
connectedWaypointsProperty.MoveArrayElement(i + 1, i);
}
connectedWaypointsProperty.MoveArrayElement(connectedWayointsProperty.arraySize - 1, newIndex);
}
if(connectedWaypointsProperty.GetArrayElementAtIndex(connectedWaypointsProperty.arraySize - 1) != null)
{
connectedWaypointsProperty.DeleteArrayElementAtIndex(connectedWaypointsProperty.arraySize - 1);
}
connectedWaypointsProperty.DeleteArrayElementAtIndex(connectedWaypointsProperty.arraySize - 1);
My attempt was to manually shuffle along the ConnectedWaypointsListContainer(s), which required caching the first one to be overwritten and overwriting the last with that saved data. However, I get an error when I try to duplicate the to-be-cached list as the last element in the serialized array by assigning the objectReferenceValue: "type is not a supported pptr value".
How can I cause the connectedWaypoints to reorder along with the waypoints? If I'm on the right track by shuffling the arrays manually, how do I properly make a temp copy so the first element overwritten isn't lost?
Make sure you are making a call to
serializedObject.ApplyModifiedProperties();
so that the changes applies back to the original object.
The symptom hints that this is the case.
Further reading:
https://docs.unity3d.com/Manual/editor-CustomEditors.html
https://docs.unity3d.com/ScriptReference/SerializedObject.html
currently i do it manually by checking every loop of array with if statement
float addGap = gapFix;
foreach (Transform child in topViewPlan.transform)
{
if (child.name.Substring(5, 2).Equals("45") || child.name.Substring(5, 2).Equals("46") || child.name.Substring(5, 2).Equals("47") || child.name.Substring(5, 2).Equals("48"))
{
//rearrange child position
if (!child.name.Substring(5, 2).Equals("45"))
child.transform.localPosition += Vector3.right * addGap * 2;
else
child.transform.localPosition += Vector3.right * addGap;
}
}
is there any possibilities to get last few element of topViewPlan.transform ?
as an example letsay topViewPlan.transform consist of 10 element, and I want last 4 element (which is element 7,8,9 and 10) so maybe I could write :-
foreach (Transform child in topViewPlan.transform.getLast(4)){} //this is just example
so i could get last 4 element of the topViewPlan.transform
You can use GetComponentsInChildren< Transform >() which will give you and array with child and the object itself.
for exemple :
var childs = GetComponentsInChildren<Transform>();
int offset = 5; // want the five last elements
for(int i=childs.Length - offset; i<childs.Length; ++i)
{
// Do something with childs[i]
}
But I would assume that this code is inefficient and unstable, I can't assure that the array returned by GetComponentsInChildren is sorted in the right way (parent at 0 and childrens afterwards). A more sure and easy way would be that every child of your GameObject have a particular component, let's say :
class ChildrenIdentificator : MonoBehavior
{
public int id = 0;
}
You would set the id in every child so it have the behavior you want and then you can do :
var childs = GetComponentsInChildren<ChildrenIdentificator>();
for(int i=0 ; i<childs.Length ; ++i)
{
if(childs[i].id == // I let you figure out the rest
}
This way you have a better control and you can see directly what you are doing. You also avoid doing string comparison.
In any case I strongly recommand to not use GetComponentsInChildren at each frame, to solve that you can store the childs array in the Start() function before using it afterward.
Use
transform.childCount
to get number of childs then to get a child do this :
for example this will return child number 5 :
transform.GetChild(5);
the code you need :
int numberOfChildNeeded = 5;
for (int i = transform.childCount - 1; i > transform.childCount - numberOfChildNeeded ; i--)
{
//do something
transform.GetChild(i)
}
class Node
{
int number;
Vector2 position;
public Node(int number, Vector2 position)
{
this.number = number;
this.position = position;
}
}
List<Node>nodes = new List<Node>();
for (int i = 0; i < nodes.Count; i++) //basically a foreach
{
// Here i would like to find each node from the list, in the order of their numbers,
// and check their vectors
}
So, as the code pretty much tells, i am wondering how i can
find a specific node from the list, specifically one with the attribute "numbers" being i (Eg going through all of them in the order of their "number" attribute).
check its other attribute
Have tried:
nodes.Find(Node => Node.number == i);
Node test = nodes[i];
place = test.position
they cant apparently access node.number / node.position due to its protection level.
Also the second one has the problem that the nodes have to be sorted first.
Also looked at this question
but [] solution is in the "Tried" caterology, foreach solution doesn't seem to work for custom classes.
I'm a coding newbie (Like 60 hours), so don't
Explain in a insanely hard way.
Say i am dumb for not knowing a this basic thing.
Thanks!
I would add properties for Number and Position, making them available to outside users (currently their access modifier is private):
class Node
{
public Node(int number, Vector2 position)
{
this.Number = number;
this.Position = position;
}
public int Number { get; private set; }
public Vector2 Position { get; private set; }
}
Now your original attempt should work:
nodes.Find(node => node.Number == i);
However, it sounds like sorting the List<Node> and then accessing by index would be faster. You would be sorting the list once and directly indexing the list vs looking through the list on each iteration for the item you want.
List<Node> SortNodes(List<Node> nodes)
{
List<Node> sortedNodes = new List<Node>();
int length = nodes.Count; // The length gets less and less every time, but we still need all the numbers!
int a = 0; // The current number we are looking for
while (a < length)
{
for (int i = 0; i < nodes.Count; i++)
{
// if the node's number is the number we are looking for,
if (nodes[i].number == a)
{
sortedNodes.Add(list[i]); // add it to the list
nodes.RemoveAt(i); // and remove it so we don't have to search it again.
a++; // go to the next number
break; // break the loop
}
}
}
return sortedNodes;
}
This is a simple sort function. You need to make the number property public first.
It will return a list full of nodes in the order you want to.
Also: The searching goes faster as more nodes are added to the sorted nodes list.
Make sure you that all nodes have a different number! Otherwise it would get stuck in an infinite loop!
I want to be able to go through a loop with if statements that will store a set of data. After the first loop gets its values I want to make a loop that goes through the list of values just created. In that loop there will be if statements that add children to the respect current digit in the list. Also each child will add the value of its respective parent. After that occurs the program will go through another loop adding a set of children to each new child previously dynamically made. The new set of children will add the value of the respected parent to there current value.
Some nodes may have 0 children some could have as many as 100. After the tree is created I need to go back through and compare each set of children for each parent and set the respective child sets value that I find to the parent, until eventually the root value will be determined.
Below is a generic outline of part of sort of what I am looking to do.
Basically i'm looking to make a dynamic tree(or any structure that works), then compare its values and find one root optimal value. Any suggestions/ help is welcome, I can't seem to rap my head around how to do this.
class Program
{
static void Main(string[] args)
{
List<int> numbers = new List<int>();
for (int i = 0; i <= 7; i++)
{
for (int j = 0; j <= 7; j++)
{
if (j == 4 || j == 2)
{
numbers.Add(j);
}
}
}
Console.WriteLine();
foreach (int x in numbers)
{
for (int i = 0; i <= 7; i++)
{
for (int j = 0; j <= 7; j++)
{
if (j == 4 || j == 2)
{
// add a child to the current digit
// add current digit to its child
}
}
}
}
// for loop to go through all children just created
// repeat similar process process add parent number to all respective children values
//repeat this process n number of times
Console.Read();
}
}