Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Implementing a function to step through a hierarchy one node at a time

Discussion in 'Scripting' started by Deleted User, Jan 29, 2017.

  1. Deleted User

    Deleted User

    Guest

    Lets say we have some hierarchy built up in the Hierarchy window of Unity.

    Consider this function:

    Code (CSharp):
    1.  
    2. public void TraverseTree(Transform t)
    3. {
    4.     Debug.Log(t.name);
    5.  
    6.     foreach (Transform child in t)
    7.     {
    8.         TraverseTree(child);
    9.     }
    10. }
    11.  
    If I call this function on the transform of the root of my hierarchy it will print the names of the game objects in the hierarchy in a depth first fashion (using recursion).

    What I would like to do is instead of having the names all print out at once is to have the functionality that only when I press the space bar does the next name get printed. This way I can step through the entire heirarchy by pressing the space bar for each step.

    Could someone offer an idea on how to do this?
     
  2. vothka

    vothka

    Joined:
    Mar 27, 2015
    Posts:
    59
    You have transform.GetChild(int index) and transform.childcount to work with

    so if you have an index
    Code (csharp):
    1.  
    2. int index = 0;
    3.  
    you could cycle through your childs with
    Code (csharp):
    1.  
    2. Debug.Log(transform.GetChild(index++ % transform.childCount).name);
    3.  
     
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,199
    Nope, that's not recursive like OP wanted.

    @terryjackson73, here's a prototype for an IEnumerator-based setup:

    Code (csharp):
    1.  
    2. public class TestScript : MonoBehaviour {
    3.  
    4.     private IEnumerator<string> DataEnumerator;
    5.  
    6.     private void Update() {
    7.         if (Input.GetKeyDown(KeyCode.Space)) {
    8.             if (DataEnumerator == null) {
    9.                 DataEnumerator = PrintChildrenNames();
    10.                 DataEnumerator.MoveNext();
    11.             }
    12.  
    13.             Debug.Log(DataEnumerator.Current);
    14.             if (!DataEnumerator.MoveNext()) {
    15.                 DataEnumerator = null;
    16.             }
    17.  
    18.         }
    19.     }
    20.  
    21.     private IEnumerator<string> PrintChildrenNames() {
    22.         yield return "Printing names of children of: " + transform.name;
    23.  
    24.         if (transform.childCount == 0) {
    25.             yield return "no children!";
    26.             yield break;
    27.         }
    28.  
    29.         var parent = transform;
    30.         var child = parent.GetChild(0);
    31.  
    32.         while (true) {
    33.             yield return child.name;
    34.             if (child.childCount > 0) {
    35.                 parent = child;
    36.                 child = child.GetChild(0);
    37.             }
    38.             else {
    39.                 while (child.GetSiblingIndex() == parent.childCount - 1) {
    40.                     if (parent == transform) {
    41.                         yield break;
    42.                     }
    43.  
    44.                     child = parent;
    45.                     parent = parent.parent;
    46.                 }
    47.  
    48.                 child = parent.GetChild(child.GetSiblingIndex() + 1);
    49.             }
    50.         }
    51.     }
    52. }
    53.  
    I think it's much easier to handle than having a field that's a pointer to the current transform, which is what you'd end up doing otherwise. It's also neat to know the inner workings of IEnumerators when you work in Unity, since they're used so much for Coroutines.
     
    Kiwasi, Deleted User and AndyGainey like this.
  4. Deleted User

    Deleted User

    Guest

    Thank you @Baste, that works perfectly!

    To be honest I am not familiar with IEnumerator and I don't quite understand how it works.
    I will spend some time on it.

    P.S. Your games "Teslagrad" and "World to the West!" look great!
     
  5. Kiwasi

    Kiwasi

    Joined:
    Dec 5, 2013
    Posts:
    16,860
    That's cool. But you can also do this with a coroutine by adding just a couple of lines to the OPs code and calling it with StartCoroutine.

    Code (CSharp):
    1. public IEnumerator TraverseTree(Transform t)
    2. {
    3.     while (!Input.GetKeyDown(Keycode.space)) yield return null;
    4.     Debug.Log(t.name);
    5.  
    6.     foreach (Transform child in t)
    7.     {
    8.         yield return TraverseTree(child);
    9.     }
    10. }
     
    Baste likes this.
  6. Deleted User

    Deleted User

    Guest

    That is an interesting idea @BoredMormon

    When I tried it, it often prints several nodes at once instead of one at a time.
    The idea is there though, I am sure it can be modified.

    Thanks.