How to use "enum" with object Pooling - c#

I created an object pooling function, and when I call a Get() function in a player script, I don't want to pass it a number like Get(0), but instead I want to call it like Get((int)ObjPrefabs.PlayerBullet).
So I tried to use "enum", but I don't know how to assign the prefab object to enum.
public class ObjectManagerFirst : MonoBehaviour
{
public GameObject[] prefabs;
public enum ObjPrefabs {PlayerBullet};
List<GameObject>[] pools;
void Awake()
{
pools = new List<GameObject>[prefabs.Length];
for (int index = 0; index < pools.Length; index++)
{
pools[index] = new List<GameObject>();
}
}
public GameObject Get(int index)
{
GameObject select = null;
foreach(GameObject item in pools[index])
{
if(!item.activeSelf)
{
select = item;
select.SetActive(true);
break;
}
}
if(!select)
{
select = Instantiate(prefabs[index], transform);
pools[index].Add(select);
}
return select;
}
}
This is current way I call it in the Player Script:
GameObject bullet = objectManager.Get(0);
But I want:
GameObject bullet = objectManager.Get((int)ObjPrefabs.PlayerBullet);
I tried already tried the following, but it didn't work:
List<int> ObjList = new List<int>();
int value = ObjList[(int)ObjPrefabs.PlayerBullet]

You can assign int values to enum and then pass it to function here is an example
enum ObjPrefabs
{
PlayerBullet = 0,
EnemyBullet = 1
}
and then call your Get function like this
GameObject bullet = objectManager.Get((int)ObjPrefabs.PlayerBullet);
and it'll work.

Instead of using int for the index why not rather stick to the enum and use a
private Dictionary<ObjPrefabs, XXX> pools;
and rather use
public GameObject Get(ObjPrefabs type)
{
if(!pools.TryGetValue(type, out var pool)
{
Debug.LogError($"Pool for {type} has not been initialized!");
return null;
}
// otherwise get object from "pool"
}
For the XXX see below
Now to the pool itself .. an array is fine but even better would be to e.g. simply use a Queue.
I would recommend a more complex but easier to handle pooling approach:
[Serializable]
public class Pool
{
public class PoolObject : MonoBehaviour
{
private Pool _pool;
public void Initialize (Pool pool)
{
_pool = pool;
}
// Use this component and method to release instances instead of calling Destroy!
public void Release()
{
_pool.Release(this);
}
private void OnDestroy ()
{
Debug.LogWarning("Destroyed a pooled object! Rather use Release!", this);
_pool.OnDestroyed(this);
}
}
// In theory you could even get rid of this enum and use the prefab itself as key ;)
[SerializeField] private ObjPrefab _type;
[SerializeField] private GameObject _prefab;
[SerializeField] [Min(1)] private int _initialAmount;
private readonly Queue<PoolObject> _availableInstances = new();
private readonly List<PoolObject> _hotInstaces = new();
public ObjPrefabs Type => _type;
public void Initialize (Transform inactiveParent)
{
for(var i = 0; i < _initialAmount; i++)
{
var instance = CreateInstance();
instance.gameObject.SetActive(false);
_availableInstances.Enqueue(instance);
}
}
private PoolObject CreateInstance ()
{
var instance = Instantiate (_prefab, inactiveParent);
if(! instance.TryGetComponent<PoolObject>(out var obj))
{
obj = instance.AddComponent<PoolObject>();
}
obj.Initialize(this);
}
private void OnDestroyed (PoolObject obj)
{
hotInstaces.Remove(obj);
}
public PoolObject GetInstance()
{
var instance = _avaialableInstances.Count != 0 ? _availableInstances.Dequeue() : CreateInstance();
instance.gameObject.SetActive(true);
hotInstances.Add(instance);
return instance;
}
public void Release(PoolObject instance)
{
if(_hotInstances.Remove(instance))
{
instance.gameObject.SetActive(false);
_availableInstances.Enqueue(instance);
}
else
{
Debug.LogError($"{instance} isn't an active object!");
}
}
}
And then finally you would use
[SerializeField] private Pool[] _pools;
private readonly Dictionary<ObjPrefabs, Pool> _activePools = new();
private void Awake ()
{
foreach(var pool in _pools)
{
if(_activePools.ContainsKey(pool.Type))
{
Debug.LogError($"Duplicate pools for type {Type}!");
continue;
}
pool.Initialize(transform);
_activePools[pool.Type] = pool;
}
}
public Pool.PoolObject Get(ObjPrefabs type)
{
if(!pools.TryGetValue(type, out var pool)
{
Debug.LogError($"Pool for {type} has not been initialized!");
return null;
}
return pool.GetInstance();
}

Related

cannot convert from 'System.Collections.Generic.Queue<System.Collections.Generic.KeyValuePair<PotionCraft.PotionRecipe.Ingredient, uint>>'

I have this error when i try to complile this C# code on my Unity:
Assets\Scripts\Cauldron.cs(50,57): error CS1503: Argument 1: cannot convert from 'System.Collections.Generic.Queue<System.Collections.Generic.KeyValuePair<PotionCraft.PotionRecipe.Ingredient, uint>>' to 'System.Collections.Generic.Queue<System.Collections.Generic.KeyValuePair<Ingredient, uint>>'
This is the code
using System.Collections.Generic;
using UnityEngine;
namespace PotionCraft.Components
{
public interface IBrewingCauldron
{
public void AddIngredient(PotionRecipe.Ingredient ingredient);
public GameObject BrewPotion();
}
[RequireComponent(typeof(SphereCollider))]
[RequireComponent(typeof(Rigidbody))]
public class Cauldron : MonoBehaviour, IBrewingCauldron
{
public Dictionary<PotionRecipe.Ingredient, uint> Ingredients { get; private set; } = new();
[SerializeField] private SphereCollider cauldronCollider;
private readonly PotionBrewer _potionBrewer = new();
private uint _numberOfIngredients;
private void Awake()
{
cauldronCollider ??= GetComponent<SphereCollider>();
// Set the collider as trigger to interact with ingredients GameObject
cauldronCollider.isTrigger = true;
}
public void AddIngredient(PotionRecipe.Ingredient ingredient)
{
// Keep track of the number of ingredients added
_numberOfIngredients++;
if (!Ingredients.ContainsKey(ingredient))
{
Ingredients[ingredient] = 1;
}
else
{
Ingredients[ingredient]++;
}
}
public GameObject BrewPotion()
{
var ingredientQueue = new Queue<KeyValuePair<PotionRecipe.Ingredient, uint>>(Ingredients);
var potionObject = _potionBrewer.MakePotion(ingredientQueue, _numberOfIngredients);
if (potionObject is not null)
{
Debug.Log($"We made a {potionObject.name} !");
potionObject.transform.position = transform.position;
}
else
{
Debug.Log("We failed to make any potion !!!");
}
Ingredients = new Dictionary<PotionRecipe.Ingredient, uint>();
_numberOfIngredients = 0;
return potionObject;
}
}
}
Edit: This is another code might be related to the problem
using UnityEngine;
namespace PotionCraft.Components
{
public class Ingredients : MonoBehaviour
{
[SerializeField] private GameObject cauldronGameObject;
[SerializeField] private PotionRecipe.Ingredient ingredient;
private SphereCollider _cauldronCollider;
private IBrewingCauldron _cauldron;
private void Awake()
{
if (cauldronGameObject is not null)
{
_cauldron = cauldronGameObject.GetComponent<IBrewingCauldron>();
if (_cauldron is not null) return;
}
var ingredientObject = gameObject;
ingredientObject.name += " [IN ERROR]";
ingredientObject.SetActive(false);
throw new MissingComponentException($"{ingredientObject.name} is missing the cauldron gameobject");
}
private void Start()
{
_cauldronCollider = cauldronGameObject.GetComponent<SphereCollider>();
gameObject.name = ingredient.ToString();
}
private void OnTriggerEnter(Collider other)
{
if (other != _cauldronCollider) return;
_cauldron.AddIngredient(ingredient);
Destroy(gameObject);
}
}
}
So what am I doing wrong here?
The PotionBrewer.MakePotion method accepts an argument of type Queue<KeyValuePair<Ingredient, uint>>.
You are trying to pass it an argument of type Queue<KeyValuePair<PotionCraft.PotionRecipe.Ingredient, uint>> instead.
If PotionBrewer.MakePotion is supposed to accept an argument of the latter type, you might need to add this using alias to your PotionBrewer class:
using Ingredient = PotionCraft.PotionRecipe.Ingredient;

Unity GameObject.SetActive(true) is being funky

So i want to make an object pooling system for my game (and possibly future games) and well i pretty much got it figured out, except for a tiny detail which is frustrating the hell out of me.
In my SpawnFromPool method i Dequeue an object and SetActive(true);.
I then call Debug.Log(objToSpawn.activeSelf);
This logs True, but the object is not active in the scene.
Below you'' find the ObjectPooler class and the Spawner class i'm using. I'll also inclue a screenshot of my console.
Here's the entire ObjectPooler class
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Library.ExtensionMethods; //Just use this to convert from enum to int like once i think...
public class ObjectPooler : MonoBehaviour
{
#region Singleton
private static ObjectPooler _instance;
public static ObjectPooler Instance { get { return _instance; } }
private void OnEnable()
{
if (_instance != null && _instance != this)
Destroy(gameObject);
_instance = this;
}
#endregion
[System.Serializable]
private class Pool
{
public GameObject Prefab;
public int AmountToStartWith;
}
[SerializeField]
private List<Pool> _pools = new List<Pool>();
private Dictionary<PoolTypes, Queue<GameObject>> _poolDictionary = new Dictionary<PoolTypes, Queue<GameObject>>();
private void Awake()
{
for (int i = 0; i < _pools.Count; i++)
{
RequestObjectsOnStart(
i,
_pools[i].Prefab,
_pools[i].AmountToStartWith
);
}
}
public GameObject SpawnFromPool(PoolTypes type, Vector3 position, Quaternion rotation)
{
if (!_poolDictionary.ContainsKey(type))
{
Debug.LogWarning("Pool with tag " + type + " doesn't exist!");
return null;
}
if (_poolDictionary[type].Count == 0)
AddToPool(type.ToInt(), _pools[System.Convert.ToInt32(type)].Prefab, 1);
GameObject objToSpawn = _poolDictionary[type].Dequeue();
objToSpawn.SetActive(true);
objToSpawn.transform.position = position;
objToSpawn.transform.rotation = rotation;
Debug.Log(objToSpawn.activeSelf);
Debug.Log(_poolDictionary[PoolTypes.Cube].Count);
return objToSpawn;
}
public void RequestObjectsPerFrame(int type, GameObject obj, int amount)
{
StartCoroutine(AddToPoolPerFrame(type, obj, amount));
}
private void RequestObjectsOnStart(int type, GameObject obj, int amount)
{
AddToPool(type, obj, amount);
}
private void AddToPool(int type, GameObject obj, int amount)
{
Queue<GameObject> objectPool = new Queue<GameObject>();
for (int i = 0; i < amount; i++)
{
GameObject newObj = Instantiate(obj);
newObj.SetActive(false);
objectPool.Enqueue(obj);
}
if (_poolDictionary.ContainsKey((PoolTypes)type))
{
foreach (GameObject pooledObj in objectPool)
{
_poolDictionary[(PoolTypes)type].Enqueue(pooledObj);
}
return;
}
_poolDictionary.Add((PoolTypes)type, objectPool);
}
private IEnumerator AddToPoolPerFrame(int type, GameObject obj, int amount)
{
for (int i = 0; i < amount; i++)
{
AddToPool(type, obj, 1);
yield return null;
}
}
}
Here's the entire Spawner class
using UnityEngine;
public class Spawner : MonoBehaviour
{
ObjectPooler _op;
private void Start()
{
_op = ObjectPooler.Instance;
}
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
GameObject obj;
obj = _op.SpawnFromPool(PoolTypes.Sphere, transform.position, Quaternion.identity);
Debug.Log(obj);
}
}
}
The Extension Method i used in case someone was curious, or if that's where the problem is...
public static int ToInt<T>(this T e) where T : struct, System.IComparable
{
if (!(typeof(T).IsEnum))
throw new System.ArgumentException("Value must be an enum");
return (int)(object)e;
}
This is what the looks like after i left click once. The important one is the one that says true baing called after the objectToSpawn.SetActive(true); in the SpawnFromPool method.
here is the major problem:
objectPool.Enqueue(obj);
It should be
objectPool.Enqueue(newObj);
Your "obj" is just the prefab, and newObj is the instantiated object, which you should add into the pool.
GameObject newObj = Instantiate(obj);
newObj.SetActive(false);
objectPool.Enqueue(obj);

How do I store the same types of classes that have a different generic inside of a list?

I've been tinkering with this and I have a 'RespawnManager' that I want to use to manage my multiple 'SpawnPoint' classes with different generics but it ended up forcing me to use generics for my 'RespawnManager' which I don't want.
Let's say I had a SpawnPoint<T> class and I made a SpawnPoint<Enemy1>, SpawnPoint<Enemy2>, and SpawnPoint<Enemy3>. Is there any way I can make a list that can just manage multiple 'SpawnPoint's of any generic?
Base class:
public abstract class SpawnPoint<T> : MonoBehaviour
{
//how big the range of the spawn protection is
public int spawnProtectionRadius = 20;
public bool Occupied { get; set; }
public bool IsInSpawn(Transform target)
{
Debug.Log((target.position - transform.position).magnitude);
if ((target.position - transform.position).magnitude <= spawnProtectionRadius)
{
return true;
}
return false;
}
public abstract T Get();
}
Class that Inherits this
public class SeaMineSpawnPoint : SpawnPoint<Seamine>
{
public override Seamine Get()
{
return SeaMineObjectPool.PoolInstance.Get();
}
private void Start()
{
RespawnManager<Seamine>.respawnManager.AddSpawn(this);
}
}
Respawn manager:
public class RespawnManager<T> : MonoBehaviour where T : Component
{
public static RespawnManager<T> respawnManager;
[SerializeField]
private List<Transform> playerList;
[SerializeField]
private List<SpawnPoint<T>> spawnpoints;
private float respawnCounter;
private void Awake()
{
respawnManager = this;
}
private void Start()
{
foreach (SpawnPoint<T> sp in spawnpoints)
{
Debug.Log(sp.transform.position);
}
}
public void AddSpawn(SpawnPoint<T> spawnPoint)
{
spawnpoints.Add(spawnPoint);
}
public void RespawnSeaMines()
{
if (respawnCounter > 5)
{
respawnCounter = 0;
foreach (SpawnPoint<T> sp in spawnpoints)
{
foreach (Transform playerT in playerList)
{
if (sp.Occupied == false && !sp.IsInSpawn(playerT))
{
Component ourGameObj = sp.Get();
ourGameObj.transform.position = sp.transform.position;
ourGameObj.gameObject.SetActive(true);
sp.Occupied = true;
return;
}
}
}
}
}
private void Update()
{
respawnCounter += Time.deltaTime;
Debug.Log(respawnCounter);
RespawnSeaMines();
}
}
ObjectPool
//Class that's used for object pooling of different types.
//'T' must be a Unity component or it will error.
public abstract class ObjectPool<T> : MonoBehaviour where T : Component
{
//An object with this specific component that we use to copy.
[SerializeField]
private T prefab;
//Makes sure that only 1 coroutine runs at a time
private bool coroutineIsRunning;
//The singleton instance to our object pool.
public static ObjectPool<T> PoolInstance { get; private set; }
//A queue is used to organize plus activate and deactivate objects which
//have this component.
protected Queue<T> objects = new Queue<T>();
private void Awake()
{
//Set the instance of this pool to this class instance. Only one of these can be set.
if (PoolInstance != null)
{
throw new System.Exception("Singleton already exists. Cannot make another copy of this");
}
PoolInstance = this;
}
public T Get()
{
//If the queue happens to be empty, then add a brand new component.
if (objects.Count == 0) AddObjects(1);
//Returns the generic component and removes it from the queue.
return objects.Dequeue();
}
public void ReturnToPool(T objectToReturn)
{
//Disables the game object that the T component is attached to.
objectToReturn.gameObject.SetActive(false);
//Stores the T component in the queue.
objects.Enqueue(objectToReturn);
}
public void AddObjects(int count)
{
for (int i = 0; i < count; i++)
{
//Create a new copy of the prefab.
//The prefab is a game object with the T component attached to it.
T newObject = Instantiate(prefab);
//Disable the game object.
newObject.gameObject.SetActive(false);
//Add the T component to the queue.
//The T component is attached to the game object we created earlier.
objects.Enqueue(newObject);
}
}
public T GetWithDelay(int time)
{
T genericToReturn = null;
if (!coroutineIsRunning)
{
coroutineIsRunning = true;
StartCoroutine(GetCoroutine(time, genericToReturn));
}
return genericToReturn;
}
private IEnumerator GetCoroutine(int time, T generic)
{
float counter = 0;
while (counter < time)
{
counter += Time.deltaTime;
yield return null;
}
generic = Get();
generic.gameObject.SetActive(true);
coroutineIsRunning = false;
}
}
You should be able to declare your spawnpoints property in RespawnManager as a List<SpawnPoint<Component>> instead of List<SpawnPoint<T>>. That will allow you to get rid of the <T> type parameter entirely from RespawnManager and make it non-generic.

List array unity

I have a ObjectController script that looking for game objects and adding them to an array and another script that draws outline around those game objects. There're few tasks that I'm trying to achieve:
From ObjectController script List array I want to check what object is currently selected (clicked) so i won't be able to select (click) on other objects.
onMouseButtonDown(1) i want to clear selections (outline) from those objects.
Can you please guide me in the right direction?
I'm new to coding so please, go easy on me :D
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class ObjectController : MonoBehaviour
{
public List<SpriteOutline> objects;
private void Start()
{
List<SpriteOutline> objectList = FindObjectsOfType<SpriteOutline>().ToList<SpriteOutline>();
for (int i = 0; i < objectList.Count; i++)
{
objects.Add(objectList[i]);
}
}
public List<SpriteOutline> GetList()
{
return objects;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class SpriteOutline : MonoBehaviour {
public Color OutlineColor = Color.white;
private Color _currentColor = Color.clear;
[Range(0, 16)]
public int outlineSize = 1;
private bool clicked = false;
private SpriteRenderer spriteRenderer;
ObjectController objController;
IEnumerator Start()
{
objController = GameObject.Find("ObjectController").GetComponent<ObjectController>();
yield return new WaitForEndOfFrame();
for (int i = 0; i < objController.GetList().Count; i++)
{
Debug.Log(i);
}
}
void OnEnable() {
spriteRenderer = GetComponent<SpriteRenderer>();
UpdateOutline(true);
}
void OnDisable() {
UpdateOutline(false);
}
void Update() {
UpdateOutline(true);
onMouseDown();
}
private void onMouseDown()
{
Vector3 mousePos;
mousePos = Input.mousePosition;
mousePos = Camera.main.ScreenToWorldPoint(mousePos);
CapsuleCollider2D coll = GetComponent<CapsuleCollider2D>();
if (Input.GetMouseButtonDown(0))
{
if (coll.OverlapPoint(mousePos))
{
_currentColor = Color.clear;
if (!clicked)
{
_currentColor = OutlineColor;
clicked = true;
}
else
{
clicked = false;
}
}
}
if (Input.GetMouseButtonDown(1))
{
_currentColor = Color.clear;
}
}
void UpdateOutline(bool outline) {
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
spriteRenderer.GetPropertyBlock(mpb);
mpb.SetFloat("_Outline", outline ? 1f : 0);
mpb.SetColor("_OutlineColor", _currentColor);
mpb.SetFloat("_OutlineSize", outlineSize);
spriteRenderer.SetPropertyBlock(mpb);
}
}
hatebin
hatebin
A bit strange for me that you go through a controller class just to again find all instances of the actual class ;)
First you could simplify your code a lot:
public class ObjectController : MonoBehaviour
{
// The list is already public so no need to have a getter method ...
public List<SpriteOutline> objects;
private void Start()
{
// FindObjectsOfType returns an SpriteOutline[]
// so the easiest way of converting it to a list is
objects = FindObjectsOfType<SpriteOutline>().ToList();
}
}
Then you would later simply do
FindObjectOfType<ObjectController>().objects
However this isn't even necessary!
Instead of this manager/Singleton pattern here I would rather use a static and let your object instances register themselves and let your class handle it completely itself:
public class SpriteOutline : MonoBehaviour
{
[SerializeField] private Color OutlineColor = Color.white;
[SerializeField] [Range(0, 16)] private int outlineSize = 1;
[SerializeField] private SpriteRenderer _spriteRenderer;
// Here you actually register and unregisters instances
private static readonly List<SpriteOutline> _instances = new List<SpriteOutline>();
// If even needed public read-only access
public ReadOnlyCollection<SpriteOutline> Instances => _instances.AsReadOnly();
// The current selection
private static readonly HashSet<SpriteOutline> _currentSelection = new HashSet<SpriteOutline>();
// Again if needed a public read-only access
public static HashSet<SpriteOutline> CurrentSelection => new HashSet<SpriteOutline>(_currentSelection);
// backing field for storing the actual value of selected
private bool _isSelected;
// Property which additionally updates the outline when changed
public bool IsSelected
{
get => _isSelected;
set
{
if(value == _isSelected) return;
_isSelected = value;
if(value)
{
_currentSelection.Add(this);
}
else
{
_currentSelection.Remove(this);
}
UpdateOutline(enabled, value);
}
}
private void Awake()
{
if(!_spriteRenderer) _spriteRenderer = GetComponent<SpriteRenderer>();
// Register yourself
_instances.Add(this);
}
private void OnEnable()
{
UpdateOutline (true, _isSelected);
}
private void OnDisable()
{
UpdateOutline (false, _isSelected);
}
private void OnDestroy ()
{
// Unregister yourself
_instances.Remove(this);
if(_currentSelection.Contains(this)) _currentSelection.Remove(this);
}
// This simplifies your query a lot since this is anyway only called
// If the mouse is hovering the collider of this object
private void OnMouseDown()
{
// Toggle selection of this
if (Input.GetMouseButtonDown(0))
{
IsSelected = !IsSelected;
}
}
private void Update()
{
// Deselect this
if (Input.GetMouseButtonDown(1))
{
IsSelected = false;
}
}
void UpdateOutline(bool outline, bool selected)
{
var mpb = new MaterialPropertyBlock();
_spriteRenderer.GetPropertyBlock(mpb);
mpb.SetFloat("_Outline", outline ? 1f : 0);
mpb.SetColor("_OutlineColor", selected ? OutlineColor : Color.clear);
mpb.SetFloat("_OutlineSize", outlineSize);
spriteRenderer.SetPropertyBlock(mpb);
}
}
If you rather want to select only exactly one object at a time then I would exchange the HashSet<SpriteOutline> _currentSelection by something like
// The current selection
private static SpriteOutline _currentSelection;
// Again if needed a public read-only access
public static SpriteOutline CurrentSelection => _currentSelection;
accordingly change
public bool IsSelected
{
get => _isSelected;
set
{
if(value == _isSelected) return;
// assuming that if you set this via property it should
// overwrite the current selection
// otherwise you would here do
//if(_currentSelection && _currentSelection != this) return;
if(_currentSelection && _currentSelection != this) _currentSelection.IsSelected = false;
_isSelected = value;
_currentSelection = value ? this : null;
UpdateOutline(enabled, value);
}
}
and finally you wanted to block the input on objects that are not the selection:
private void OnMouseDown()
{
// Toggle selection of this
if (Input.GetMouseButtonDown(0))
{
if(_currentSelection && _currentSelection != this) return;
IsSelected = !IsSelected;
}
}
Note: Typed on smartphone but I hope the idea gets clear

Trouble getting a game object from object pool in Unity

I was having trouble converting an object pool script from UnityScript to C#, which I got a lot of good help with here. Now I'm having an issue trying to actually get a game object from the pool. I have three scripts all interacting with one another, so I'm not quite sure where it's going wrong. Here are the two scripts for the object pool, which I believe are all squared away and they're not giving any errors:
public class EasyObjectPool : MonoBehaviour {
[System.Serializable]
public class PoolInfo{
[SerializeField]
public string poolName;
public GameObject prefab;
public int poolSize;
public bool canGrowPoolSize = true;
}
[System.Serializable]
public class Pool{
public List<PoolObject> list = new List<PoolObject>();
public bool canGrowPoolSize;
public void Add (PoolObject poolObject){
list.Add(poolObject);
}
public int Count (){
return list.Count;
}
public PoolObject ObjectAt ( int index ){
PoolObject result = null;
if(index < list.Count) {
result = list[index];
}
return result;
}
}
static public EasyObjectPool instance ;
[SerializeField]
PoolInfo[] poolInfo = null;
private Dictionary<string, Pool> poolDictionary = new Dictionary<string, Pool>();
void Start () {
instance = this;
CheckForDuplicatePoolNames();
CreatePools();
}
private void CheckForDuplicatePoolNames() {
for (int index = 0; index < poolInfo.Length; index++) {
string poolName= poolInfo[index].poolName;
if(poolName.Length == 0) {
Debug.LogError(string.Format("Pool {0} does not have a name!",index));
}
for (int internalIndex = index + 1; internalIndex < poolInfo.Length; internalIndex++) {
if(poolName == poolInfo[internalIndex].poolName) {
Debug.LogError(string.Format("Pool {0} & {1} have the same name. Assign different names.", index, internalIndex));
}
}
}
}
private void CreatePools() {
foreach(PoolInfo currentPoolInfo in poolInfo){
Pool pool = new Pool();
pool.canGrowPoolSize = currentPoolInfo.canGrowPoolSize;
for(int index = 0; index < currentPoolInfo.poolSize; index++) {
//instantiate
GameObject go = Instantiate(currentPoolInfo.prefab) as GameObject;
PoolObject poolObject = go.GetComponent<PoolObject>();
if(poolObject == null) {
Debug.LogError("Prefab must have PoolObject script attached!: " + currentPoolInfo.poolName);
} else {
//set state
poolObject.ReturnToPool();
//add to pool
pool.Add(poolObject);
}
}
Debug.Log("Adding pool for: " + currentPoolInfo.poolName);
poolDictionary[currentPoolInfo.poolName] = pool;
}
}
public PoolObject GetObjectFromPool ( string poolName ){
PoolObject poolObject = null;
if(poolDictionary.ContainsKey(poolName)) {
Pool pool = poolDictionary[poolName];
//get the available object
for (int index = 0; index < pool.Count(); index++) {
PoolObject currentObject = pool.ObjectAt(index);
if(currentObject.AvailableForReuse()) {
//found an available object in pool
poolObject = currentObject;
break;
}
}
if(poolObject == null) {
if(pool.canGrowPoolSize) {
Debug.Log("Increasing pool size by 1: " + poolName);
foreach (PoolInfo currentPoolInfo in poolInfo) {
if(poolName == currentPoolInfo.poolName) {
GameObject go = Instantiate(currentPoolInfo.prefab) as GameObject;
poolObject = go.GetComponent<PoolObject>();
//set state
poolObject.ReturnToPool();
//add to pool
pool.Add(poolObject);
break;
}
}
} else {
Debug.LogWarning("No object available in pool. Consider setting canGrowPoolSize to true.: " + poolName);
}
}
} else {
Debug.LogError("Invalid pool name specified: " + poolName);
}
return poolObject;
}
}
And:
public class PoolObject : MonoBehaviour {
[HideInInspector]
public bool availableForReuse = true;
void Activate () {
availableForReuse = false;
gameObject.SetActive(true);
}
public void ReturnToPool () {
gameObject.SetActive(false);
availableForReuse = true;
}
public bool AvailableForReuse () {
//true when isAvailableForReuse & inactive in hierarchy
return availableForReuse && (gameObject.activeInHierarchy == false);
}
}
The original UnityScript said to retrieve an object from the pool with this statement:
var poolObject : PoolObject = EasyObjectPool.instance.GetObjectFromPool(poolName);
This is how I tried to do that in my shooting script with it trying to fire a bullet prefab from the pool:
public class ShootScript : MonoBehaviour {
public PoolObject poolObject;
private Transform myTransform;
private Transform cameraTransform;
private Vector3 launchPosition = new Vector3();
public float fireRate = 0.2f;
public float nextFire = 0;
// Use this for initialization
void Start () {
myTransform = transform;
cameraTransform = myTransform.FindChild("BulletSpawn");
}
void Update () {
poolObject = EasyObjectPool.instance.GetObjectFromPool<poolName>();
if(Input.GetButton("Fire1") && Time.time > nextFire){
nextFire = Time.time + fireRate;
launchPosition = cameraTransform.TransformPoint(0, 0, 0.2f);
poolObject.Activate();
poolObject.transform.position = launchPosition;
poolObject.transform.rotation = Quaternion.Euler(cameraTransform.eulerAngles.x + 90, myTransform.eulerAngles.y, 0);
}
}
}
My shoot script is giving me two errors:
1. The type or namespace name 'poolName' could not be found. Are you missing a using directive or an assembly reference?
For the line:
poolObject = EasyObjectPool.instance.GetObjectFromPool<poolName>();
2. 'PoolObject.Activate()' is inaccessible due to its protection level
For the line:
poolObject.Activate();
Did I mis-translate the UnityScript or am I missing something else? Any input is greatly appreciated
I think your object pool is incorrectly programmed. When you have a method like CheckForDuplicatePoolNames() ask yourself if you need a different collection like dictionary.
Keep it simple.
I recently implemented one and I have tested. It works pretty well.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[System.Serializable]
public class PoolObject{
public GameObject prefab;
public int startNum;
public int allocateNum;
public Transform anchor;
}
public class ObjectPool<T> : MonoBehaviour where T:Component{
public PoolObject[] poolObjects;
public bool ______________;
public Dictionary<string,Queue<T>> pool = new Dictionary<string, Queue<T>> ();
public Dictionary<string, List<T>> spawned = new Dictionary<string, List<T>> ();
public Transform anchor;
public void Init(){
InitPool ();
StartAllocate ();
}
public T Spawn(string type){
if (pool [type].Count == 0) {
int i = FindPrefabPosition(type);
if(poolObjects[i].allocateNum == 0)
return null;
Allocate(poolObjects[i], poolObjects[i].allocateNum);
}
T t = pool [type].Dequeue ();
spawned[type].Add (t);
t.gameObject.SetActive (true);
return t;
}
public void Recycle(T t){
if (spawned [t.name].Remove (t)) {
pool[t.name].Enqueue(t);
t.gameObject.SetActive(false);
}
}
private void Allocate(PoolObject po, int number){
for (int i=0; i<number; i++) {
GameObject go = Instantiate (po.prefab) as GameObject;
go.name = po.prefab.name;
go.transform.parent = po.anchor != null? po.anchor : anchor;
T t = go.GetComponent<T>();
pool[t.name].Enqueue(t);
t.gameObject.SetActive(false);
}
}
private int FindPrefabPosition(string type){
int position = 0;
while (poolObjects[position].prefab.name != type) {
position++;
}
return position;
}
void InitPool(){
if(anchor == null)
anchor = new GameObject("AnchorPool").transform;
for (int i =0; i<poolObjects.Length; i++) {
T t = poolObjects[i].prefab.GetComponent<T>();
t.name = poolObjects[i].prefab.name;
pool[t.name] = new Queue<T>();
spawned[t.name] = new List<T>();
}
}
void StartAllocate(){
for (int i=0; i<poolObjects.Length; i++) {
Allocate(poolObjects[i], poolObjects[i].startNum );
}
}
}
Example:
public class CoinsPool : ObjectPool<CoinScript>{}
In the unity inspector configure the coin prefab, the startNum and the allocateNum.
Somewhere:
void Awake(){
coinsPool = GetComponent<CoinsPool>();
coinsPool.Init();
}
CoinScript cs = coinsPool.Spawn("Coin"); //Coin is the name of the coin prefab.
Later
coinsPool.Recycle(cs);
The thing you write within <> should be a class name like PoolObject if the function is generic, which it is not. So to solve this you simply need to change
poolObject = EasyObjectPool.instance.GetObjectFromPool<poolName>();
to
string poolName = "thePoolNameYouWant";
poolObject = EasyObjectPool.instance.GetObjectFromPool(poolName);
Function are private by default so to solve the "inaccessible due to its protection level" error you need to make the function public by changing this
void Activate () {
availableForReuse = false;
gameObject.SetActive(true);
}
to this
public void Activate () {
availableForReuse = false;
gameObject.SetActive(true);
}

Categories