How to preview pooled GameObjects in the scene view

I’m using a pooling system for my enemy units in a 2d platformer. Practically, it means I can’t simply place my enemies in the scene anymore because of this (as it would mean they are instantiated when the game starts, instead of being taken from the pool).

And since I can’t see a preview of the units, I cannot place them correctly on the terrains (I can’t see their Collider2D’s bounds - each enemy has a different size).

I thought using an Editor script by getting the bounds of the Prefab would work but it the bounds of the Collider2d object is zero when the Prefab is not yet instantiated.

Any ideas?

Why do you need to pool your enemies? Pooling is generally for things you’re rapidly recycling like projectiles.

Pooling is objectively better that’s why, plus I have quite a few enemies.

I found a decent solution, for anyone interested:

using System;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;

public class Spawner : Spawnable
{
    private GameObject SpawnEffect;
    private float AccuTime;
    [DisableInEditorMode, DisableInPlayMode] public float TimeBeforeSpawn = 1f;


    void OnEnable()
    {
        if (SpawnEffect != null)
        {
            Instantiate(SpawnEffect, transform.position, Quaternion.identity);
        }

        if (SpawnEffect == null)
        {
            SpawnEffect = Resources.Load<GameObject>("Misc/SpawnEffect");
        }
    }

    private void PoolSpawn()
    {
        var unit = Pool.GetPoolable(Prefab);
        unit.transform.position = transform.position;
    }

    void Update()
    {
        AccuTime += Time.deltaTime;
        if (AccuTime > TimeBeforeSpawn)
        {
            PoolSpawn();
            gameObject.SetActive(false);
        }
    }
}

#if UNITY_EDITOR
[CustomEditor(typeof(Spawnable), true)]
public class SpawnerEditor : Editor
{
    private Spawner Target;
    void OnEnable()
    {
        Target = (Spawner) target;
    }

    // We want to display the bounds + Sprite of the future SpawnedCreature.
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        Spawnable.ShowPreview = EditorGUILayout.Toggle("Preview", Spawnable.ShowPreview);
    }

    public void OnSceneGUI()
    {
        if (Target.Prefab)
        {
            var prefabName = Target.Prefab.name;
            if (prefabName.Contains("Group"))
            {
                prefabName = prefabName.Remove(prefabName.IndexOf("Group", StringComparison.InvariantCultureIgnoreCase));
            }

            Target.gameObject.name = "Spwn: " + prefabName;
        }
          
        else
        {
            Target.gameObject.name = "!! Empty Spwn";
        }

        SpawnPreview();
    }

    private void SpawnPreview()
    {
        var spawnables = FindObjectsOfType(typeof(Spawnable));

        foreach (var sp in spawnables)
        {
            var spawner = (Spawner) sp;

            if (Spawnable.ShowPreview)
            {
                if (spawner.SpawnedPreview == null)
                    spawner.BuildPreview();
            }
            else
            {
                spawner.DeletePreview();
            }
        }
    }
}
#endif
using UnityEngine;

public class Spawnable : MonoBehaviour
{
    public static bool ShowPreview;
    public UnitAIGroup Prefab;
    [HideInInspector] public UnitAIGroup SpawnedPreview;

    public virtual void Awake()
    {
        ShowPreview = false;
        DeletePreview();
    }

    public virtual void BuildPreview()
    {
        if (Prefab)
        SpawnedPreview = Instantiate(Prefab, transform.position, Quaternion.identity, transform);
    }

    public virtual void DeletePreview()
    {
        if (SpawnedPreview != null) DestroyImmediate(SpawnedPreview.gameObject);
    }
}

Glad you found a solution, just be aware of the downsides. Your solution now causes an extreme spike in garbage on the first frame when it destroys all of your temporary placeholder copies, which defeats the purpose of using a pool in the first place. Loading will also be affected: a bunch of temporary copies load, then you get a first frame spike when it destroys them all and pulls in brand new ones which need to reset all of their data to a neutral state, which may create a visible freeze depending on the amount of enemies. You’re also going to run into trouble if you need instance specific values, such as already wounded enemies or enemies starting on ladders, or whatever else is appropriate for your game because everything comes fresh from the pool.

I can’t speak for your game because you never elaborated on exactly how it works, but there’s rarely objective truths in programming.

It’s a bit of extra programming but these issues can mostly be dealt with I think, for example destroying the pool’s object over more than a single frame or destroying one pool at a time then using GC.Collect(). But I see your point, I’m creating a metroidvania with no loading time so it’s something I’ll have to be mindful of.

I must say that the problem of the already wounded enemy seems problematic in the standard Unity way already (if using prefabs). Say I make an Enemy with 10 hp, but also have another instance of that prefab that’s wounded (5hp).
The minute I click “Apply” on the 10hp prefab, any instance of that prefab will be set to 10hp. Thus deleting the wounded enemy or rather replace it with a non-wounded one (5 to 10hp).

I haven’t looked around much for this but it seems to need another prefab (which has problems too because you will/might have to update several prefabs if you want to change values that these prefabs share). I’m not sure what clean solution there is to that problem but it’s mildly annoying.

A metroidvania is definitely a good candidate for pooling, absolutely, but you definitely don’t want to load an entire game worth of enemies and then delete them all on frame 1. You may want to break your map down into rooms, much like Metroid has with the coloured doors, and work your way out from there.

As for the prefab health issue, Unity is pretty smart about how it treats instances of objects. If you drag a prefab out into the scene and change its health, Unity considers everything except that health value as still part of the prefab. If you were to edit the prefab’s mana, for example, it would update in all of your scene’s instances. You can try it for yourself fairly quickly in the editor.

Right now it’s supposed to not have any rooms and thus be completely devoid of any black loading screen but we’ll see if I can make it work.
What about making Pools slowly shrink down over time if they’re unused for a long periods of time? Isn’t that a good safety measure? Surely that would work well with the idea of a completely seamless playthrough (no loading screen)?

I didn’t know that about prefabs but that doesn’t change the fact that if I were to want to change the HP of most everyone (the 10hp to 12hp), then the few wounded guys would be affected too. It seems ScriptableObjects that would have the hp and a few other variables is the better solution for this specific case.

Streaming an entire game of content is going to require very specific solutions tailored to your game. You’ll definitely need to rig up a system like rooms or chunks that load on demand. You can take inspiration from voxel games or other streaming games.

If you change a prefab’s health from 12 to 10, everyone’s health changes except those who had their health changed to 5 in the scene. Instance values will always be the prefab’s value unless you explicitly changed that value – so in this case, your wounded enemies would remain wounded and the new health will be set for all other instances. You can tell if an instance is getting its value from a prefab or not by if it’s bold in the inspector.

1 Like