using DunGen.Pooling; using UnityEngine; namespace DunGen.Generation { public delegate void TileInstanceSpawnedDelegate(Tile tilePrefab, Tile tileInstance, bool fromPool); public delegate void TileInstanceDespawnedDelegate(Tile tileInstance); /// /// A class responsible for the spawning and despawning of tiles /// public class TileInstanceSource { public event TileInstanceSpawnedDelegate TileInstanceSpawned; public event TileInstanceDespawnedDelegate TileInstanceDespawned; protected readonly BucketedObjectPool tilePool; protected bool enableTilePooling; protected GameObject dungeonRoot; protected Transform tilePoolRoot; protected TilePoolPreloader tilePoolPreloader; public TileInstanceSource() { tilePool = new BucketedObjectPool( objectFactory: template => { var tileObj = Object.Instantiate(template); if (tileObj.TryGetComponent(out var tile)) tile.RefreshTileEventReceivers(); return tileObj; }); } /// /// Initialises the TileInstanceSource /// /// Should tile pooling be used? /// The root GameObject of the dungeon public virtual void Initialise(bool enableTilePooling, GameObject dungeonRoot) { this.enableTilePooling = enableTilePooling; this.dungeonRoot = dungeonRoot; if (!enableTilePooling) return; // Try to find a TilePoolPreloader in the scene, and pre-warm the pool with its contents if (tilePoolPreloader == null) TryPreloadTilePool(); // Create a GameObject to parent all pooled tiles to if (tilePoolRoot == null) { var tilePoolObj = new GameObject("Tile Pool"); tilePoolObj.SetActive(false); tilePoolRoot = tilePoolObj.transform; } } protected void TryPreloadTilePool() { tilePoolPreloader = UnityUtil.FindObjectByType(); if(tilePoolPreloader == null) return; // Make the tile preloader the pool root tilePoolPreloader.gameObject.SetActive(false); tilePoolRoot = tilePoolPreloader.transform; // Insert each preloaded tile into the correct bucket in the pool foreach (var entry in tilePoolPreloader.Entries) { var prefab = entry.TilePrefab; if (prefab == null) continue; var instances = tilePoolPreloader.GetTileInstancesForPrefab(prefab); if(instances == null) continue; foreach (var instance in instances) { // The tile should be active as the tile pool root itself is inactive instance.gameObject.SetActive(true); instance.RefreshTileEventReceivers(); tilePool.InsertObject(prefab, instance); } } } /// /// Spawns a tile at the specified position and rotation. If pooling is enabled, the tile will /// be taken from the pool. If pooling is disabled, or the pool is empty, a new tile will be instantiated /// /// Tile prefab to spawn /// World-space position /// World-space rotation /// The spawned tile instance public virtual Tile SpawnTile(Tile tilePrefab, Vector3 position, Quaternion rotation) { if (enableTilePooling) { bool fromPool = tilePool.TryTakeObject(tilePrefab, out var tile); var tileTransform = tile.transform; tileTransform.parent = dungeonRoot.transform; tileTransform.localPosition = position; tileTransform.localRotation = rotation; tile.TileSpawned(); TileInstanceSpawned?.Invoke(tilePrefab, tile, fromPool); return tile; } else { var tileObj = GameObject.Instantiate(tilePrefab, position, rotation, dungeonRoot.transform); if (tileObj.TryGetComponent(out var tile)) { tile.RefreshTileEventReceivers(); tile.TileSpawned(); TileInstanceSpawned?.Invoke(tilePrefab, tile, false); } return tileObj; } } /// /// Despawns a tile. If pooling is enabled, the tile will be returned to the pool. /// If pooling is disabled, the tile will be destroyed /// /// The tile instance to despawn public virtual void DespawnTile(Tile tileInstance) { bool returnedToPool = false; // Try to return the tile to the pool if (enableTilePooling) { returnedToPool = tilePool.ReturnObject(tileInstance); if (returnedToPool) { tileInstance.transform.parent = tilePoolRoot; tileInstance.TileDespawned(); TileInstanceDespawned?.Invoke(tileInstance); } } // If we can't return the tile to the pool, or pooling was disabled, destroy the tile instead if (!returnedToPool) { tileInstance.TileDespawned(); TileInstanceDespawned?.Invoke(tileInstance); GameObject.DestroyImmediate(tileInstance.gameObject); } } } }