What is a Singleton?
A Singleton is a design pattern that restricts the instantiation of a class to one single instance. This is useful when exactly one object is needed to coordinate actions across the system. The Singleton pattern ensures that a class has only one instance and provides a global point of access to it.
Characteristics of Singleton:
- Single Instance: Ensures that only one instance of the class is created.
- Global Access Point: Provides a global point of access to the instance.
- Lazy Initialization: Often used to create the instance when it is first needed.
Implementing a Singleton in C#
Steps to Implement a Singleton:
- Private Static Instance: A private static variable to hold the single instance of the class.
- Public Static Property: A public static property to provide global access to the instance.
- Private Constructor: A private constructor to prevent direct instantiation from outside the class.
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool : MonoBehaviour
{
// Step 1: Private static instance
private static ObjectPool instance;
// Step 2: Public static property
public static ObjectPool Instance
{
get
{
// Lazy initialization
if (instance == null)
{
// Attempt to find an existing instance in the scene
instance = FindObjectOfType<ObjectPool>();
// If no instance exists, create a new GameObject and attach ObjectPool component
if (instance == null)
{
GameObject obj = new GameObject(“ObjectPool”);
instance = obj.AddComponent<ObjectPool>();
}
// Make sure the instance persists across scene loads
DontDestroyOnLoad(instance.gameObject);
}
return instance;
}
}
// Private dictionary to hold multiple queues of pooled objects, one for each prefab
private Dictionary<GameObject, Queue<GameObject>> poolDictionary = new Dictionary<GameObject, Queue<GameObject>>();
// Step 3: Private constructor (in this case, the MonoBehaviour constructor is private by default)
private void Awake()
{
// Ensure only one instance of ObjectPool exists
if (instance == null)
{
instance = this;
DontDestroyOnLoad(gameObject);
}
else if (instance != this)
{
Destroy(gameObject);
}
}
public void InitializePool(GameObject prefab, int initialSize)
{
if (!poolDictionary.ContainsKey(prefab))
{
poolDictionary[prefab] = new Queue<GameObject>();
for (int i = 0; i < initialSize; i++)
{
GameObject obj = Instantiate(prefab);
obj.SetActive(false);
poolDictionary[prefab].Enqueue(obj);
}
}
}
public GameObject GetObject(GameObject prefab, Vector3 position, Quaternion rotation)
{
if (poolDictionary.ContainsKey(prefab) && poolDictionary[prefaf].Count > 0)
{
GameObject obj = poolDictionary[prefab].Dequeue();
obj.transform.position = position;
obj.transform.rotation = rotation;
obj.SetActive(true);
return obj;
}
else
{
GameObject obj = Instantiate(prefab, position, rotation);
return obj;
}
}
public void ReturnObject(GameObject prefab, GameObject obj)
{
obj.SetActive(false);
if (!poolDictionary.ContainsKey(prefab))
{
poolDictionary[prefab] = new Queue<GameObject>();
}
poolDictionary[prefab].Enqueue(obj);
}
}
Summary
- Singleton Pattern: Ensures that only one instance of the
ObjectPool
exists and provides a global access point. - Centralized Object Pool: All
EnemySpawner
instances use the sharedObjectPool
to manage enemy objects, promoting efficient resource management. - Initialization: The
Initialize
method inObjectPool
ensures the pool is only set up once per prefab type. - Object Reuse: The pooling mechanism reuses enemy objects, reducing the overhead of frequent instantiation and destruction.
Lazy Initialization in Singleton Pattern
In the context of a Singleton pattern, lazy initialization ensures that the single instance of the class is not created until it is first accessed. This is particularly useful if the creation of the instance is expensive or if the instance might not be needed during the entire lifetime of the application.