Unity learning notes -- how to use the object pool to generate game objects gracefully and easily

preface

Recently, I reviewed my FPS project and found that the object pool code I wrote before is almost hard to see...
Plus I recently learned the factory model. I optimized my object pool code.
After updating: the code is more concise, easier to understand, easy to operate, and the code coupling is reduced.

Prepare in advance

  • Note: this example uses bullet preforms. You can change other preforms.

1. We need to create a Resources folder under the Assets folder

2. We need to add our preforms in the Resources folder

Implementation steps

Step 1: create an object pool manager

Create an object pool and let it inherit from the singleton template class.
(how to write a singleton template class: On design pattern and its application in Unity: I. single case pattern)
PS: the code will be much shorter if there are no comments, but I still made comments for everyone's convenience.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PoolManager : Singleton<PoolManager>
{
    /// <summary>
    ///All object pools, each element corresponds to an object pool of a game object
    /// </summary>
    private Dictionary<string, Stack<GameObject>> pool = new Dictionary<string, Stack<GameObject>>()
    {
        {"AssaultRifleBullet", new Stack<GameObject>() },           //Rifle bullet object pool
        {"SniperBullet", new Stack<GameObject>() },                 //Sniper bullet object pool
        {"HandgunBullet", new Stack<GameObject>() },                //Pistol bullet object pool
        {"SMGBullet", new Stack<GameObject>() },                    //Submachine gun bullet object pool
        {"ShotGunBullet", new Stack<GameObject>() },                //Shotgun bullet object pool
    };
    /// <summary>
    ///Store all the prefabs that can be used for the object pool and put them in the Resources folder
    /// </summary>
    private Dictionary<string, GameObject> Prefabs = new Dictionary<string, GameObject>();
    [SerializeField]
    /// <summary>
    ///Stores the name of the preform that needs to be dynamically loaded
    /// </summary>
    private List<string> prefabsName;
    protected override void Awake()
    {
        base.Awake();
        DontDestroyOnLoad(this);
        InitDictionary();
    }
    /// <summary>
    ///Dynamically load preforms. Note that the names of all preforms in the Resources folder should be the same as the name of the object pool
    /// </summary>
    private void InitDictionary()
    {
        for (int i = 0; i < prefabsName.Count; i++)
        {
            Prefabs.Add(prefabsName[i], (GameObject)Resources.Load(prefabsName[i]));
        }
    }
    /// <summary>
    ///Recycle to object pool
    /// </summary>
    ///< param name = "gameobjectname" > name of game object in object pool < / param >
    ///< param name = "go" > recycled game objects < / param >
    public void CollectObject(string gameobjectName, GameObject go)
    {
        if(pool.TryGetValue(gameobjectName, out Stack<GameObject> stack))
        {
            //Push game objects onto the stack (add to the object pool)
            go.SetActive(false);
            stack.Push(go);
        }
    }
    /// <summary>
    ///Create object call
    /// </summary>
    ///< param name = "gameobjectname" > name of game object in object pool < / param >
    ///< returns > game objects taken from the object pool < / returns >
    public GameObject Creat(string gameobjectName)
    {
        GameObject res = null;
        //If there is an object pool corresponding to the game object
        if (pool.TryGetValue(gameobjectName, out Stack<GameObject> stack) && Prefabs.ContainsKey(gameobjectName))
        {
            //If there are objects in the object pool, take them out directly, or regenerate a preform.
            res = stack.Count >= 1 ? stack.Pop() : Instantiate(Prefabs[gameobjectName]);
            res?.gameObject.SetActive(true);
        }
        return res;
    }
    /// <summary>
    ///Create object call
    /// </summary>
    ///< param name = "gameobjectname" > name of game object in object pool < / param >
    ///< param name = "position" > coordinates of the game object < / param >
    ///< param name = "rotation" > rotation of game object < / param >
    ///< returns > game objects taken from the object pool < / returns >
    public GameObject Creat(string gameobjectName, Vector3 position, Quaternion rotation)
    {
        GameObject res = Creat(gameobjectName);
        res.transform.position = position;
        res.transform.rotation = rotation;
        return res;
    }
}
  • Note that the name of the preform in the Resources folder should be the same as that in the prefabsName list.

Step 2: create an abstract factory

I won't post the code if I'm limited to space here.
Abstract factory steps I wrote an article: On design pattern and its application in Unity: III. abstract factory pattern

Step 3: Bullet factory code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class BulletFactory : Singleton<BulletFactory>, IBaseFactory
{
    protected override void Awake()
    {
        base.Awake();
        DontDestroyOnLoad(this);
    }
    /// <summary>
    ///Called when a bullet is created
    /// </summary>
    ///< param name = "producename" > bullet Name: AssaultRifleBullet, SniperBullet, HandgunBullet, SMGBullet, shotgunbullet < / param >
    ///< returns > bullet < / returns >
    public GameObject Creat(string produceName)
    {
        return PoolManager.Ins.Creat(produceName);
    }
    /// <summary>
    ///Called when a bullet is created
    /// </summary>
    ///< param name = "producename" > bullet Name: AssaultRifleBullet, SniperBullet, HandgunBullet, SMGBullet, shotgunbullet < / param >
    ///< param name = "position" > bullet position < / param >
    ///< param name = "rotation" > bullet rotation < / param >
    ///< returns > bullet < / returns >
    public GameObject Creat(string produceName, Vector3 position, Quaternion rotation)
    {
        GameObject go = Creat(produceName);
        go.transform.position = position;
        go.transform.rotation = rotation;
        return go;
    }
}

Step 4: how to generate game objects

Suppose my gun script has a shooting function, then we can call it like this

public void HandleShot()
{
	GameObject bullet = FactoryProducer.GetFactory(FactoryType.BulletFactory).Creat(firearmBulletName);
}

Step 5: how to recycle game objects

For example, my bullet script has a timer that automatically recycles the game object after 5s, so we can write as follows:

private void RecoveryToPool()
{
    PoolManager.Ins.CollectObject(owner.firearmBulletName, gameObject);
}

Add new object pool

If we want to add an object pool later, we only need to add a line of code to the object pool of PoolManager
Then add the corresponding preform and preform name in the lower two figures (the same as the object pool name above)

matters needing attention




The names pointed by the arrows in these three pictures should be the same, otherwise they cannot be loaded correctly.

last

If you have any better way, I hope the boss can give guidance, learn together and make progress together!

Keywords: Unity Game Development

Added by Benjigga on Tue, 04 Jan 2022 19:17:43 +0200