对象池这个名字听起来好像不明觉厉,其实就是将一系列需要反复创建和销毁的对象存储在一个看不到的地方,下次用同样的东西时往这里取,类似于一个存放备用物质的仓库。

它的好处就是避免了反复实例化个体的运算,能减少大量内存碎片,当然你需要更多的空间来存这些备用对象,相信使用这些空间是非常值得的。

最常见的应用就是子弹的创建和销毁。

一般对象池都是一个全局性的通用脚本,可以采用单例模式来设计。

https://www.cnblogs.com/koshio0219/p/11203631.html

对象池至少包含以下两个基本功能:

1.从池中取出指定类型的对象

2.回收各式各样的对象到池中

先定义对象池和池子的容量:

     private const int maxCount = ;
private Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>();

容量是一个常量,最好取二的幂值,这样的话可以刚好占用所有内存位的资源,避免浪费。

这里池子用字典标识,key为对象的名字,这样比较好记,你用InstanceID也没问题。

每个同样的对象一般在池子中可以有很多,用一个List来存。

下面先定义回收对象的方法:

     public void RecycleObj(GameObject obj)
{
var par = Camera.main;
obj.transform.SetParentSafe(par.transform);
obj.SetActive(false); if (pool.ContainsKey(obj.name))
{
if (pool[obj.name].Count < maxCount)
{
pool[obj.name].Add(obj);
}
}
else
{
pool.Add(obj.name, new List<GameObject>() { obj });
}
}

这里将回收的对象统一放在了场景主摄像机下,你也可以选择放在自己喜欢的位置。

回收对象就是先把对象隐藏,然后看池子中有没有这一类对象,有的话没有超过容量上限就直接扔进去。

如果没有这类对象,那就创建这一类型对象的Key值(名字:比如说螃蟹),顺便添加第一只螃蟹。

如果回收对象时改变了父物体,最好在设置父物体前后记录下对象的本地位置,旋转和缩放,可以写了一个扩展方法用于安全设置父物体:

     public static void SetParentSafe(this Transform transform,Transform parent)
{
var lp = transform.localPosition;
var lr = transform.localRotation;
var ls = transform.localScale;
transform.SetParent(parent);
transform.localPosition = lp;
transform.localRotation = lr;
transform.localScale = ls;
}

经常会遇到要批量回收进池子的情况:

     public void RecycleAllChildren(GameObject parent)
{
for (; parent.transform.childCount > ;)
{
var tar = parent.transform.GetChild().gameObject;
RecycleObj(tar);
}
}

对象可以回收了,那怎么取呢,自然也是能从池子里取就从池子里取,实在不行才去实例化:

     public GameObject GetObj(GameObject perfab)
{
//池子中有
GameObject result = null;
if (pool.ContainsKey(perfab.name))
{
if (pool[perfab.name].Count > )
{
result = pool[perfab.name][];
result.SetActive(true);
pool[perfab.name].Remove(result);
return result;
}
}
//池子中缺少
result = Object.Instantiate(perfab);
result.name = perfab.name;
RecycleObj(result);
GetObj(result);
return result;
}

如果池子中有对象,取出来之后记得要把这个对象从该类对象的列表中移除,不然下次可能又会取到这家伙,而这家伙已经要派去做别的了。

如果池子中缺少对象,那就只能实例化了,要注意把实例化后的对应改为大家都一样的名字,这样方便下一次取能找到它。

没有对象的情况下,我这里又重新回收了一下再取一次,你也可以直接返回该对象,相当于在取的时候不存在这类对象的话我提前做了标记。

和Instantiate方法一样,加一个可以设置父对象的重载方法:

     public GameObject GetObj(GameObject perfab, Transform parent)
{
var result = GetObj(perfab);
result.transform.SetParentSafe(parent);
return result;
}

下面是完整脚本:

 using System.Collections.Generic;
using UnityEngine; public class ObjectPool : Singleton<ObjectPool>
{
private const int maxCount = ;
private Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>(); public GameObject GetObj(GameObject perfab)
{
//池子中有
GameObject result = null;
if (pool.ContainsKey(perfab.name))
{
if (pool[perfab.name].Count > )
{
result = pool[perfab.name][];
if (result != null)
{
result.SetActive(true);
pool[perfab.name].Remove(result);
return result;
}
else
{
pool.Remove(perfab.name);
}
}
}
//池子中缺少
result = Object.Instantiate(perfab);
result.name = perfab.name;
RecycleObj(result);
GetObj(result);
return result;
} public GameObject GetObj(GameObject perfab, Transform parent)
{
var result = GetObj(perfab);
result.transform.SetParentSafe(parent);
return result;
} public void RecycleObj(GameObject obj)
{
var par = Camera.main;
obj.transform.SetParentSafe(par.transform);
obj.SetActive(false); if (pool.ContainsKey(obj.name))
{
if (pool[obj.name].Count < maxCount)
{
pool[obj.name].Add(obj);
}
}
else
{
pool.Add(obj.name, new List<GameObject>() { obj });
}
} public void RecycleAllChildren(GameObject parent)
{
for (; parent.transform.childCount > ;)
{
var tar = parent.transform.GetChild().gameObject;
RecycleObj(tar);
}
} public void Clear()
{
pool.Clear();
}
}

因为是用名字作为存储的Key值,所以不同类的物体命名不能相同,不然可能会取错对象。

另外由于上面的脚本有更改父物体的情况,在取出物体之后根据需要也可以对transform进行归位:

     public static void ResetLocal(this Transform transform)
{
transform.localPosition = Vector3.zero;
transform.localRotation = Quaternion.identity;
transform.localScale = Vector3.one;
}

上面是对Transform类的一个扩展方法,例如:

  var ins = ObjectPool.Instance.GetObj(bulletPrefab, parent.transform);
ins.transform.ResetLocal();

游戏设计模式——Unity对象池的更多相关文章

  1. Unity——对象池管理

    Unity对象池管理 一.Demo展示 二.逻辑 在游戏中会出现大量重复的物体需要频繁的创建和销毁:比如子弹,敌人,成就列表的格子等: 频繁的创建删除物体会造成很大的开销,像这种大量创建重复且非持续性 ...

  2. Unity 对象池 生产 保存

    Unity对象池主要是保存那些常用的物体,避免他们在不断销毁和创造中损坏性能. 主要思路为:创造物体时,判断是否存在,如果存在则调用并使其显示.如果不存在则创造一个新的. 当销毁时,调用协程延时隐藏物 ...

  3. 游戏开发设计模式之对象池模式(unity3d 示例实现)

    前篇:游戏开发设计模式之命令模式(unity3d 示例实现) 博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正. 原理:从一个固定 ...

  4. unity游戏开发_对象池

    现在假设游戏中我们需要实现一个这样功能,按下鼠标左键,发射一颗子弹,3秒之后消失.在这个功能中,我们发射了上百上千发子弹,就需要实例化生成上百上千次.这时候,我们就需要使用对象池这个概念,每次实例化生 ...

  5. Unity ----- 对象池GameObjectPool

    孙广东 2014.6.28 非常早之前看到的外国文章,认为不错,分享一下. 对象池在AssetStore中也是有非常多插件的,可是有些重了.自己写一个轻量的岂不是非常好. 当你须要创建大量某种类型对象 ...

  6. Unity 对象池的使用

    在游戏开发过程中,我们经常会遇到游戏发布后,测试时玩着玩着明显的感觉到有卡顿现象.出现这种现象的有两个原因:一是游戏优化的不够好或者游戏逻辑本身设计的就有问题,二是手机硬件不行.好吧,对于作为程序员的 ...

  7. Unity对象池的实现

    对象池是一个单例类: using System.Collections; using System.Collections.Generic; using UnityEngine; public cla ...

  8. GoLang设计模式06 - 对象池模式

    这次介绍最后一个创建型模式--对象池模式.顾名思义,对象池模式就是预先初始化创建好多个对象,并将之保存在一个池子里.当需要的时候,客户端就可以从池子里申请一个对象使用,使用完以后再将之放回到池子里.池 ...

  9. 游戏设计模式——Unity事件队列(纪念京阿尼事件)

    “对消息或事件的发送与受理进行时间上的解耦.” 在游戏开发过程中,经常会出现不同板块之间的信息交流,或是存在“当...,就...”的情况,事件队列编程模式可以有效解决消息传递中产生的脚本耦合问题,让同 ...

随机推荐

  1. Linux x86和x64的区别

    0x01:寄存器分配的不同 (1)64位有16个寄存器,32位只有8个.但是32位前8个都有不同的命名,分别是e _ ,而64位前8个使用了r代替e,也就是r _.e开头的寄存器命名依然可以直接运用于 ...

  2. 谈谈JVM垃圾回收

    概述 Java运行时区域中,程序计数器,虚拟机栈,本地方法栈三个区域随着线程的而生,随线程而死,这几个区域的内存分配和回收都具备确定性,不需要过多考虑回收问题.而Java堆和方法区则不一样,一个接口的 ...

  3. GIT和SVN教程

    各种版本控制工具的简单比较 特性 CVS SVN GIT 并发修改 支持 支持 支持 并发提交 不支持 支持 支持 历史轨迹 不支持更名 支持更名 支持更名 分布式 不支持 不支持 支持 SVN SV ...

  4. Codeforces 220C

    题意略. 思路: 我们可以把 bi[ i ] 在 ai[ ] 中的位置记录下来,然后算出 i - mp[ bi[i] ] ,再将它压入一个multiset.每次我们就二分地来寻找离0最近的数字来作为答 ...

  5. Leetcode之深度优先搜索(DFS)专题-690. 员工的重要性(Employee Importance)

    Leetcode之深度优先搜索(DFS)专题-690. 员工的重要性(Employee Importance) 深度优先搜索的解题详细介绍,点击 给定一个保存员工信息的数据结构,它包含了员工唯一的id ...

  6. PHP CURL根据详细地址获取腾讯地图经纬度

    <?php $address = "广东省广州市天河区"; $point = getPoint($address); var_dump($point);//输出经纬度 /** ...

  7. 【StyleCop】StyleCop规则汇总

    所有规则的翻译(基于版本4.7.44.0): 文档规则 1.SA1600:ElementsMustBeDocumented元素必须添加注释 2.SA1601: PartialElementsMustB ...

  8. Jedis操作Redis--List类型

    /** * List(列表) * BLPOP,BRPOP,BRPOPLPUSH,LINDEX,LINSERT,LLEN,LPOP,LPUSH,LPUSHX,LRANGE,LREM,LSET,LTRIM ...

  9. JavaScript get set方法 ES5/ES6写法

    网上鲜有get和set的方法的实例,在这边再mark一下. get和set我个人理解本身只是一个语法糖,它定义的属性相当于“存储器属性” 为内部属性提供了一个方便习惯的读/写方式 ES5写法 func ...

  10. Badboy - 导出脚本,用于JMeter性能测试

    参考: http://leafwf.blog.51cto.com/872759/1131119 http://www.51testing.com/html/00/130600-1367743.html ...