博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用对象池优化性能
阅读量:1983 次
发布时间:2019-04-27

本文共 5528 字,大约阅读时间需要 18 分钟。

一:前言

在游戏的制作过程中,我们可能会碰到这样的情况,开枪的时候射出子弹,每个子弹即一个对象,正常情况,我们的处理方式可能会是每开一枪就Instantiate一个新的子弹,当子弹到达极限距离或者碰到物体后再Destroy销毁它。假设有射出1000发子弹,我们就会执行1000次这样的操作,反复创建销毁是一个内存反复分配与释放的过程,很容易产生内存碎片。在Unity中Instantiate和Destroy操作,不仅影响性能还容易产生内存碎片,在Unity中创建或是销毁对象需要付出昂贵的代价的。而用对象池可以解决性能开销问题

内存碎片:

内存碎片的意思是内存被分成一个一个的小块而不是整个大块,所有内存小块的大小可能很大但并不能使用,比如你想分配16byte的内存,此时如果有 20byte的空间就可以分配成功,但是如果这20byte是内存碎片,为两个10byte就会分配失败。所以,如果存在大量内存碎片,理论上有足够的可用内存,也会分配失败
很多游戏公司的游戏都会进行浸泡测试,让一个游戏跑好几天,查看是否崩溃来检测内存泄露等等,因为内存碎片产生毁灭性的结果是一个缓慢的过程


二:什么是对象池?

池可以理解为我们现实生活中的游泳池,里面装满了水,而在计算机世界里称为一定数量水的集合。所以池的概念和集合很相似。例如内存池就是一定数量的已经分配好的内存的集合。线程池就是一定数量的已经创建好的线程的集合,那么,对象池,顾名思义就是一定数量的已经创建好的对象(Object)的集合

对象池的原理就是预先分配一大块内存,生成满需要经常用的对象,然后直到不使用再全部释放


三:对象池的优点

对象池就是复用池中对象,没有分配内存和创建堆中对象的开销,进而减少垃圾收集器的负担, 避免内存抖动


四:对象池的应用场景

在制作过程中当经常有同一个Prefab要用到多次,需要反复实例化(Instantiate)和销毁(Desroy)。例如射击游戏中的子弹,跑酷游戏中的障碍物,路径等.....

 

五:对象池的使用

在游戏加载时把一批Prefab实例化好放在对象池中,游戏中用的时候拿出来(SetActive=true),不用的时候放回去(SetActive=false),避免反复实例化和销毁。可以理解为对象池就是一个租借处,需要的时候借出去,用完了再还回来


六:对象池的使用流程

——————————————————————ReusableObject脚本

using UnityEngine;/// /// 对象池中的每个物体都需要重写取出和放回的方法/// public abstract class ReusableObject : MonoBehaviour{    ///     /// 取出时    ///     public abstract void OnSpawn();    ///     /// 回收时    ///     public abstract void OnUnSpawn();}

——————————————————————SubPool脚本 

using UnityEngine;using System.Collections.Generic;/// /// 每个子池子/// public class SubPool{    //预制体    private GameObject prefab;    //父物体    private Transform parent;    //当前子池子中的所有物体    private List
goList = new List
(); ///
/// 初始化当前子池子 /// ///
预制体 ///
父物体 public SubPool(GameObject prefab, Transform parent) { this.prefab = prefab; this.parent = parent; } #region public方法 ///
/// 取出物体 /// public GameObject Spawn() { GameObject go = null; go = GetGoFromList(); if (go == null) { go = GameObject.Instantiate(prefab, parent); goList.Add(go); } go.GetComponent
().OnSpawn();//取出物体时执行的方法 go.SetActive(true); return go; } ///
/// 回收物体 /// ///
要回收的物体 public void UnSpawn(GameObject go) { go.GetComponent
().OnUnSpawn();//回收物体时执行的方法 go.SetActive(false); } ///
/// 回收所有物体 /// public void UnSpawnAll() { foreach (var temp in goList) { if (temp.activeSelf) { UnSpawn(temp); } } } ///
/// 当前子池子中是否包含此游戏物体 /// ///
判断的游戏物体 public bool Contains(GameObject go) { return goList.Contains(go); } #endregion #region private方法 ///
/// 从goList中得到一个物体 /// ///
private GameObject GetGoFromList() { GameObject go = null; foreach (var temp in goList) { if (temp.activeSelf == false) { go = temp; } } return go; } #endregion}

 

——————————————————————ObjectPool脚本 

using UnityEngine;using System.Collections.Generic;/// /// 对象池总管理器/// public class ObjectPool : MonoBehaviour{    //单例    public static ObjectPool Instance { get; set; }    //每个对象池物体信息的字典    private Dictionary
poolInfoDict = new Dictionary
(); //每个子池子的字典 private Dictionary
subPoolDict = new Dictionary
(); [Header("资源目录")] public string resDir; [Header("手动添加所有对象池物体")] [Space(25)] public ObjectInfo[] objects; private void Awake() { Instance = this; //初始化每个对象池物体信息的字典 foreach (var o in objects) { if (!poolInfoDict.ContainsKey(o.resName)) { poolInfoDict.Add(o.resName, o.parent); } } } #region main ///
/// 取出物体 /// ///
资源名称 public GameObject Spawn(string resName) { GameObject go = null; if (!subPoolDict.ContainsKey(resName)) { if (!CreatePool(resName)) { Debug.LogWarning("创建池子失败:" + resName); return go; } } SubPool pool = subPoolDict[resName]; go = pool.Spawn();//取出物体 return go; } ///
/// 回收物体 /// ///
要回收的物体 public void UnSpawn(GameObject go) { foreach (var pool in subPoolDict.Values) { if (pool.Contains(go)) { pool.UnSpawn(go); break; } } } ///
/// 回收所有物体 /// public void UnSpawnAll() { foreach (var pool in subPoolDict.Values) { pool.UnSpawnAll(); } } #endregion #region private方法 ///
/// 创建池子 /// ///
资源名称 private bool CreatePool(string resName) { if (!poolInfoDict.ContainsKey(resName)) { Debug.LogWarning("不存在此子池子:" + resName + ",请在面板中手动添加资源名称"); return false; } string path = resDir + resName; GameObject prefab = Resources.Load
(path); if (prefab == null) { Debug.LogWarning("路径不正确:" + path); return false; } SubPool pool = new SubPool(prefab, poolInfoDict[resName]); subPoolDict.Add(resName, pool); return true; } #endregion}///
/// 每个对象池中的物体信息/// [System.Serializable]public class ObjectInfo{ //资源名称 public string resName; //父物体 public Transform parent;}

转载地址:http://nvyvf.baihongyu.com/

你可能感兴趣的文章
golang实现大数据量文件的排序
查看>>
golang中的time包
查看>>
golang fmt包中的占位符
查看>>
Docker下使用Redis
查看>>
Redis的主从和集群设置
查看>>
对Redis Cluster的理解
查看>>
清华师哥丢了个在Github下载量50万+的项目给我,让(附源码下载地址)
查看>>
The MASM32 SDK version 10 发布了!
查看>>
发布软件:TreeInfo(分层信息管理器)
查看>>
c++二分图的最大匹配
查看>>
c++点的距离
查看>>
c++实现彩色炫酷(?)画面
查看>>
c++马拦过河卒
查看>>
2019NOIP D4题 加工领奖
查看>>
1997年世界黑客大赛获奖作品
查看>>
论DEV-C++怎样才能做窗口
查看>>
Failed to connect to github.com port 443: Operation timed out和弹出无法打开"GoogleSoftwareUpdate.bundle"
查看>>
2021.5.19 JS高级第二天
查看>>
2021.5.20 JS高级第三天
查看>>
2021.5.21 Jquery
查看>>