Entity Factories and Object Pooling

Summary

Use a ksEntityFactory to implement object pooling.

Description

Entity factories can be used to override the behaviour for spawning and destroying game objects for entities. ksEntityFactory has three methods that can be overriden:

GameObject GetGameObject(uint assetId, string entityType, GameObject prefab) Gets the game object for an entity. The returned game object must have a ksEntityComponent and must not be assigned to another non-destroyed entity.

void InitializeEntity(ksEntity entity) Called when an entity is constructed. Override this to implement your own initialization logic.

void ReleaseGameObject(ksEntity entity) Called when an entity is destroyed and ksEntity.DestroyWithServer is true. Override this to implement your own destruction logic.

Entity Factories are registered on the room with a regex pattern that determines which entity types use the entity factory. For example, calling room.AddEntityFactory("Bullet", new MyEntityFactory()); will register the factory for entities of type 'Bullet'. room.AddEntityFactory(".*Bullet", new MyEntityFactory()); will register it for all entity types the end with 'Bullet', and room.AddEntityFactory(".*", new MyEntityFactory()); will register it for all entity types. If multiple factories are registered with a regex pattern that matches the same entity type, the first one registered will be used for that type. Factories must be registered before connecting to the room.

The following example shows how to implement object pooling for entity game objects using an entity factory. You should see one disabled game object in the hierarchy for each entity type you register the factory for. As entities are deleted, their game objects will be added as children to their respective pool objects, and as new entities spawn, they will be removed from their pool.

Connect Script

    ***
    // Before connecting, register the entity pool factory for all entities.
    // Change the regex pattern ".*" if you only want to pool certain entity types.
    room.AddEntityFactory(".*", new EntityPool());
    ***

EntityPool

using System.Collections.Generic;
using UnityEngine;
using KS.Reactor.Client.Unity;

public class EntityPool : ksEntityFactory
{
    // Maps asset ids to object pools.
    private Dictionary<uint, Transform> m_pools = new Dictionary<uint, Transform>();

    public override GameObject GetGameObject(uint assetId, string entityType, GameObject prefab)
    {
        // Get the object from its pool if there is one.
        Transform pool;
        if (m_pools.TryGetValue(assetId, out pool) && pool != null && pool.childCount > 0)
        {
            GameObject gameObject = pool.transform.GetChild(pool.childCount - 1).gameObject;
            gameObject.transform.SetParent(null);
            return gameObject;
        }
        // Create a new object from the prefab.
        return base.GetGameObject(assetId, entityType, prefab);
    }

    public override void ReleaseGameObject(ksEntity entity)
    {
        // Return the object to its pool.
        Transform pool;
        if (!m_pools.TryGetValue(entity.AssetId, out pool) || pool == null)
        {
            pool = new GameObject(entity.Type + " Factory").transform;
            pool.gameObject.SetActive(false);
            m_pools[entity.AssetId] = pool;
        }
        entity.GameObject.transform.SetParent(pool);
    }
}