0
\$\begingroup\$

I'm wondering how I can organize instantiated GameObjects that are spawned in sort of a nested structure. I'm spawning these GameObjects via Containers, a set of classes that either spawn the GameObjects directly, or spawn other Containers with some kind of logic. I want to be able to spawn a number of Containers, and then spawn a parent in the Unity hierarchy to which each of the GameObjects spawned by these containers will be parented. However, some Containers will also be parented to other Containers, creating this kind of structure:

Container
    Container
        Prefab
        Prefab
    Container
        Prefab
        Prefab

For example, I may have classes like these:

public abstract class Container : ScriptableObject
{
     public static event Action<Container> OnContainerSpawned;

     public abstract void Spawn(Vector2 position);

     protected void RaiseSpawnEvent(Container container)
     {
        OnContainerSpawned(container);
     }
}

// Spawns a single prefab; this is the smallest "unit" in my system
public class GameObjectContainer: Container
{
    public static event Action<GameObject> OnGameObjectSpawned;

    [SerializeField]
    private GameObject prefab;
    
    public override void Spawn(Vector2 position)
    {
        var spawnedPrefab = Instantiate(prefab, position, Quaternion.identity);
        OnGameObjectSpawned?.Invoke(spawnedPrefab);
        RaiseSpawnEvent(this);
    }
}

// Spawns a container in a row
public class RowContainer : Container
{
    [SerializeField]
    private Container container;  
    [SerializeField]
    private int length;  

    public override void Spawn(Vector2 position)
    {
        var spawnPosition = position;

        for (int i = 0; i < length; i++)
        {
            container.Spawn(spawnPosition);
            spawnPosition += Vector2.right;
        }

        RaiseSpawnEvent(this);
    }
}

// Spawns a container in a column
public class ColumnContainer: Container
{
    [SerializeField]
    private Container container;  
    [SerializeField]
    private int length;  

    public override void Spawn(Vector2 position)
    {
        var spawnPosition = position;

        for (int i = 0; i < length; i++)
        {
            container.Spawn(spawnPosition);
            spawnPosition += Vector2.down;
        }

        RaiseSpawnEvent(this);
    }
}

I'm currently using events to alert my debugging class that something has been spawned so that it can parent things accordingly:

public class Debugger : MonoBehaviour
{
    [SerializeField]
    private GameObject prefabParent;

    private List<GameObjects> recentlySpawnedGameObjects = new List<GameObject>();

    private void OnEnable()
    {
        Container.OnContainerSpawned += TrackSpawns;
        GameObjectContainer.OnGameObjectSpawned += recentlySpawnedGameObjects.Add;
    }

    private void TrackSpawns(Container container)
    {
        if (container is GameObjectContainer) // Don't bother making a parent for a single GameObject
            return;

        // Make a new parent in the hierarchy, and update its name
        var parent = Instantiate(prefabParent); 
        parent.name = container.name;
        
        // Parent each recently spawned GameObject (from the event in the GameObjectContainer class) to the parent we just created
        foreach (var gameObject in recentlySpawnedGameObjects)
            gameObject.transform.parent = parent;

        // Clear the list, and await next container to spawn
        recentlySpawnedGameObjects.Clear();
    }
}

This might produce a result that looks like this:

Row Container
    GameObject
    GameObject
    GameObject

This is fine, but it only shows the topmost Container. What if I have a RowContainer that spawns a ColumnContainer that spawns a GameObjectContainer, attempting to get something that looks like this:

Row Container
    Column Container
        GameObject
        GameObject
        GameObject
    Column Container
        GameObject
        GameObject
        GameObject
    Column Container
        GameObject
        GameObject
        GameObject

I have no idea how to achieve this. In my actual game I'm spawning things using object pools, so I only need to track the pooled objects and reuse them as necessary, which I can do. However, it would be very nice to debug my system in this way, creating an easy-to-understand visual, especially as my game becomes more complex.

\$\endgroup\$
2
  • \$\begingroup\$ Didyou took a look at the description of the overload parameter for Instantiate? docs.unity3d.com/ScriptReference/Object.Instantiate.html All you need to do is pass the parent, under which it should be. \$\endgroup\$
    – Zibelas
    Sep 14 at 7:00
  • \$\begingroup\$ As I mentioned in the last bit of my question, I'm using pooled objects in my actual game, so I'm not instantiating most of the time, just moving around pre-instantianted objects. I need a solution that works in that context, so I can't use the Instantiate method to assign a parent. \$\endgroup\$ Sep 14 at 7:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.