一:目的

内存管理是软件开发中重要的一项,在游戏中,有一些需要频繁创建和销毁的对象,例如射击游戏中的子弹,跑酷游戏中的障碍物等,通常的方法是使用Instantiate和Destroy,不断的开辟和释放内存,但是这样的操作是致命的,会产生内存碎片

什么是内存碎片:内存碎片意味着在堆中的空余空间被打碎成了很多小的内存碎片,而不是大的连续内存块,总共可用内存也许很大,但是最长的连续空间可能很小很小

例如下图所示,假设我们最后还有20个空余字节,但是被一块正在使用的内存O2分割成了两部分,这时我们尝试分配15字节的对象就会失败,最终导致游戏崩溃

U3D游戏开发框架(六)——对象池_游戏开发

所有我们可以定义一个池对象,其中包含了一组可重用的对象

当需要新对象时,向池子要一个,如果池子中没有此对象,就Instantiate一个,如果池子中存在此对象,则直接SetActive=true

当需要销毁时,不使用Destroy,直接SetActive=false并放回池子

通过这种方式可以轻易地创建和销毁对象而不必分配内存或其他资源

二:解决的问题及优点

——避免分配内存和释放内存产生的内存碎片导致的游戏崩溃

三:使用

——将所有预制体文件统一放在Resources文件夹下的Prefabs文件夹的子文件夹下
——每个对象池物体的脚本继承ReusableObj类,并实现OnSpawn和OnUnSpawn方法,重写Reset方法
——使用ObjectPoolMgr.Ins.XXX

ObjectPoolMgr.Ins.Allocate("Effect");
ObjectPoolMgr.Ins.Recycle(gameObject);

四:代码实现

using System.Collections.Generic;
using UnityEngine;
 
/// <summary>
/// 对象池管理器
/// </summary>
public class ObjectPoolMgr : Singleton<ObjectPoolMgr>
{
    public const string ResDir = "Prefabs/";//资源目录
 
    private Dictionary<string, SubPool> poolCache = new Dictionary<string, SubPool>();//所有的池子
 
    #region Main
 
    /// <summary>
    /// 分配资源
    /// </summary>
    public GameObject Allocate(string resName)
    {
        if (!poolCache.ContainsKey(resName))
        {
            if (!CreatePool(resName))
            {
                Debug.LogError("创建池子失败:" + resName);
                return null;
            }
        }
        return poolCache[resName].Allocate();
    }
 
    /// <summary>
    /// 回收资源
    /// </summary>
    public void Recycle(GameObject obj)
    {
        string poolName = obj.GetComponent<ReuseableObj>().HostPoolId;
        poolCache[poolName].Recycle(obj);
    }
 
    #endregion
 
    /// <summary>
    /// 创建池子
    /// </summary>
    private bool CreatePool(string resName)
    {
        string path = ResDir + resName;
        GameObject prefab = Resources.Load<GameObject>(path);
        if (prefab == null)
        {
            return false;
        }
        SubPool subPool = new SubPool(resName, prefab);
        poolCache.Add(resName, subPool);
        return true;
    }
}
 
/// <summary>
/// 子池子
/// </summary>
public class SubPool
{
    public string poolId;//池子名称
    public GameObject prefab;//预制体
 
    public Stack<GameObject> unUsedObjCache = new Stack<GameObject>();//池子中所有未使用的对象
 
    /// <summary>
    /// 初始化池子
    /// </summary>
    public SubPool(string poolId, GameObject prefab)
    {
        this.poolId = poolId;
        this.prefab = prefab;
    }
 
    /// <summary>
    /// 分配资源
    /// </summary>
    public GameObject Allocate()
    {
        GameObject obj = GetUnusedObj();
        if (obj == null)
        {
            obj = SpawnObj();
        }
        obj.GetComponent<ReuseableObj>().OnSpawn();
        obj.SetActive(true);
        return obj;
    }
 
    /// <summary>
    /// 回收资源
    /// </summary>
    public void Recycle(GameObject obj)
    {
        obj.SetActive(false);
        unUsedObjCache.Push(obj);
        obj.GetComponent<ReuseableObj>().OnUnSpawn();
        obj.GetComponent<ReuseableObj>().Reset();
    }
 
    /// <summary>
    /// 生成对象
    /// </summary>
    public GameObject SpawnObj()
    {
        GameObject obj = Object.Instantiate(prefab);
        obj.GetComponent<ReuseableObj>().HostPoolId = poolId;
        return obj;
    }
 
    /// <summary>
    /// 从池子中得到一个未使用的对象
    /// </summary>
    private GameObject GetUnusedObj()
    {
        GameObject obj = null;
        if (unUsedObjCache.Count <= 0) return null;
 
        obj = unUsedObjCache.Pop();
        return obj;
    }
}
using UnityEngine;
 
/// <summary>
/// 对象池复用的物体
/// </summary>
public abstract class ReuseableObj : MonoBehaviour
{
    //从属的池子
    private string hostPoolId;
    public string HostPoolId
    {
        get
        {
            return hostPoolId;
        }
        set
        {
            hostPoolId = value;
        }
    }
 
    /// <summary>
    /// 取出时
    /// </summary>
    public abstract void OnSpawn();
 
    /// <summary>
    /// 回收时
    /// </summary>
    public abstract void OnUnSpawn();
 
    /// <summary>
    /// 重置
    /// </summary>
    public abstract void Reset();
}

图片来源:页游