How to Use TEngine's Object Pool for GameObjects: A Complete Guide to Zero-Allocation Pooling

TEngine's ObjectPoolModule eliminates runtime Instantiate and Destroy overhead by recycling GameObject instances through a generic pool system that leverages the ObjectBase lifecycle and IObjectPoolModule factory methods.

TEngine (alex-rachel/tengine) provides a production-ready pooling framework designed specifically for Unity's GameObject management challenges. By implementing the abstract ObjectBase class and utilizing the ObjectPoolModule, you can maintain stable frame rates and minimize garbage collection stalls during intense gameplay moments. This guide walks through the exact implementation pattern used in TEngine's core runtime, referencing the actual source files that power the system.

Understanding TEngine's Object Pool Architecture

The pooling system consists of three tightly integrated components defined across four key source files.

  • IObjectPoolModule.cs (lines 61-76): Defines the contract for creating, retrieving, and destroying pool instances. This interface exposes factory methods like CreateMultiSpawnObjectPool<T>() and CreateSingleSpawnObjectPool<T>().

  • ObjectPoolModule.cs (lines 740-775): The concrete implementation that manages pool lifecycle and provides the internal ObjectPool<T> generic class. This file contains the capacity management and auto-release logic.

  • ObjectBase.cs: The abstract base class that all pooled objects must inherit. It defines the critical OnSpawn() and OnDespawn() virtual methods that handle GameObject instantiation and deactivation.

  • ObjectPool.cs (embedded in ObjectPoolModule): The generic container that tracks active and inactive instances, ensuring thread-safe operations on the Unity main thread.

Step 1: Define a Pooled GameObject Type

To pool a GameObject, create a wrapper class that inherits from ObjectBase. This class stores your prefab reference and implements the spawn/despawn lifecycle hooks.

using TEngine.Runtime.Module.ObjectPoolModule;
using UnityEngine;

/// <summary>
/// Wrapper for pooling GameObject prefabs. 
/// References ObjectBase defined in ObjectBase.cs.
/// </summary>
public sealed class PooledGameObject : ObjectBase
{
    public GameObject Prefab;   // Assigned during pool initialization
    private GameObject _instance;

    // Called automatically when Spawn() pulls from pool
    protected override void OnSpawn()
    {
        _instance = Object.Instantiate(Prefab);
    }

    // Called automatically when Despawn() returns to pool
    protected override void OnDespawn()
    {
        if (_instance != null)
        {
            _instance.SetActive(false);
            _instance.transform.SetParent(null);
        }
    }

    public GameObject GameObject => _instance;
}

The OnSpawn method handles instantiation only when the pool needs a new active instance, while OnDespawn deactivates rather than destroys, preparing the object for immediate reuse.

Step 2: Create and Configure the Pool

Use the IObjectPoolModule interface to create either a multi-spawn or single-spawn pool. Multi-spawn pools support many simultaneously active objects (e.g., bullets), while single-spawn pools enforce only one active instance at a time (e.g., UI popups).

using TEngine.Runtime.Module.ObjectPoolModule;
using UnityEngine;

public class BulletPoolManager : MonoBehaviour
{
    [SerializeField] private GameObject bulletPrefab;
    private IObjectPool<PooledGameObject> _bulletPool;

    private void Awake()
    {
        // Retrieve the module implementation from ObjectPoolModule.cs
        var poolModule = ModuleSystem.GetModule<IObjectPoolModule>();

        // CreateMultiSpawnObjectPool<T> defined at lines 740-775 in ObjectPoolModule.cs
        _bulletPool = poolModule.CreateMultiSpawnObjectPool<PooledGameObject>(
            name: "BulletPool",
            capacity: 200);

        // InitObject runs once per internal slot creation
        _bulletPool.InitObject(obj => obj.Prefab = bulletPrefab);
    }

    public IObjectPool<PooledGameObject> GetPool() => _bulletPool;
}

The capacity parameter (set to 200 in this example) caps the maximum number of live objects. When capacity is reached, the pool recycles the oldest inactive instance rather than allocating new memory.

Step 3: Spawn and Despawn at Runtime

With the pool initialized, retrieve instances via Spawn() and return them via Despawn(). This pattern eliminates runtime allocations entirely after the initial warm-up phase.

Spawning a Bullet

public class WeaponController : MonoBehaviour
{
    private BulletPoolManager _poolManager;

    private void Start()
    {
        _poolManager = FindObjectOfType<BulletPoolManager>();
    }

    public void Fire(Vector3 position, Vector3 direction)
    {
        // Spawn() invokes OnSpawn() automatically
        var pooledObj = _poolManager.GetPool().Spawn();
        
        var bulletGO = pooledObj.GameObject;
        bulletGO.transform.position = position;
        bulletGO.transform.forward = direction;
        bulletGO.SetActive(true);
    }
}

Returning to the Pool

public class Projectile : MonoBehaviour
{
    private IObjectPool<PooledGameObject> _ownerPool;
    private float _spawnTime;

    public void SetPool(IObjectPool<PooledGameObject> pool) 
    {
        _ownerPool = pool;
        _spawnTime = Time.time;
    }

    private void Update()
    {
        // Return after 5 seconds or on impact
        if (Time.time - _spawnTime > 5f)
        {
            var pooledComponent = GetComponent<PooledGameObject>();
            _ownerPool.Despawn(pooledComponent); // Invokes OnDespawn()
        }
    }
}

The Despawn method immediately recycles the object, making it available for the next Spawn() call without triggering garbage collection.

Performance Characteristics and Optimization

TEngine's implementation addresses specific Unity performance constraints through several architectural choices:

  • Zero Runtime Allocation: After initial pool warm-up, no Instantiate or Destroy calls occur, eliminating GC.Alloc spikes.
  • Fixed Memory Budget: The capacity argument in CreateMultiSpawnObjectPool<T>() creates a hard ceiling on memory usage.
  • Main Thread Safety: All internal list operations in ObjectPool.cs execute on Unity's main thread, preventing race conditions with the engine's native GameObject API.
  • Automatic Lifecycle Hooks: The OnSpawn/OnDespawn pattern ensures pooled objects reset their state correctly without manual tracking.

Monitor pool utilization at runtime by checking the Count and InactiveCount properties exposed through ObjectPoolBase, allowing you to tune capacity values based on actual gameplay metrics.

Summary

  • ObjectBase subclasses wrap GameObject prefabs and define instantiation logic via OnSpawn() and cleanup via OnDespawn().
  • IObjectPoolModule provides factory methods CreateMultiSpawnObjectPool<T>() and CreateSingleSpawnObjectPool<T>() (implemented in ObjectPoolModule.cs lines 740-775).
  • Capacity limits prevent unbounded memory growth; excess spawn requests recycle inactive instances.
  • Spawn() and Despawn() operations are allocation-free after pool initialization, maintaining consistent frame times during high-intensity spawning scenarios.

Frequently Asked Questions

What is the difference between CreateMultiSpawnObjectPool and CreateSingleSpawnObjectPool?

CreateMultiSpawnObjectPool allows an unlimited number of simultaneously active objects from the pool, making it ideal for projectiles, enemies, or particles. CreateSingleSpawnObjectPool enforces that only one instance of type T can be active at any moment, which is useful for singleton UI panels or exclusive audio sources that must not overlap.

How does TEngine handle the actual GameObject instantiation?

According to ObjectBase.cs, the pool calls the virtual OnSpawn() method when a new instance is needed. Your derived class implements this to run Object.Instantiate(Prefab), giving you full control over instantiation parameters. When despawning, OnDespawn() handles deactivation or parenting logic without destroying the underlying GameObject.

Can I set a maximum capacity for my GameObject pool?

Yes. The CreateMultiSpawnObjectPool<T>() method accepts a capacity parameter (as shown in ObjectPoolModule.cs lines 740-775). This integer defines the maximum number of objects the pool will maintain. Once reached, spawning either recycles the oldest inactive object or fails gracefully depending on your configuration, preventing memory bloat during unexpected gameplay spikes.

Where are the core ObjectPool files located in the TEngine repository?

The interface resides in UnityProject/Assets/TEngine/Runtime/Module/ObjectPoolModule/IObjectPoolModule.cs (lines 61-76). The concrete implementation is in ObjectPoolModule.cs (lines 740-775 for factory methods, lines 1172-1239 for destruction logic). The base class definition is in ObjectBase.cs, and the generic pool container logic is in ObjectPool.cs.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →