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

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

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

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

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. DRF (Django REST framework) 中的视图扩展类

    2. 五个扩展类 1)ListModelMixin 列表视图扩展类,提供list(request, *args, **kwargs)方法快速实现列表视图,返回200状态码. 该Mixin的list方法 ...

  2. .net测试篇之测试神器Autofixture在几个复杂场景下的使用示例以及与Moq结合

    系列目录 为String指定一个值. 在第三节里我们讲了如何使用自定义配置加上一个自定义算法生成一个自定义字符串,然而有些时候我们仅仅是需要某个字段是有意义的,这个时候随便生成的字符串也满足不了我们的 ...

  3. SSH开发模式——Struts2(第二小节)

    上一小节已经学会了如何去搭建Struts2的开发环境,该篇博客我们继续深入Struts2,了解Struts2框架的拦截器. 首先对我们在web.xml文件配置的过滤器进行一个源码的分析. 在Strut ...

  4. Keras实例教程(2)

    https://blog.csdn.net/baimafujinji/article/details/78385745

  5. 快应用 吸顶 bug

    官网说, 加载页面时,所有元素的appear事件都会被触发一次.因此,需要过滤第一次的appear事件:  但是,即使设置了过滤,也无效 于是,我把show属性改成了if属性,问题就解决 如下图:  

  6. MSIL实用指南-方法的调用

    方法调用指令主要有Call和Callvirt. 调用static或sealed修饰的方法,用Call指令. 调用virtual或abstract修饰的方法,用Callvirt指令. 代码实例: ilG ...

  7. 一文看尽Java-Thread

    一.前言      主要分成两部说起:Thread源码解读和常见面试题解答,废话不多说开始: 二.源码解读 首先看下构造函数,构造函数都是通过调用init方法对属性进行初始化,主要是对线程组.线程名字 ...

  8. Reactive(2) 响应式流与制奶厂业务

    目录 再谈响应式 为什么Web后端开发的,对 Reactive 没有感觉 Java 9 支持的 Reactive Stream 范例 小结 扩展阅读 再谈响应式 在前一篇文章从Reactive编程到& ...

  9. ubuntu安装elasticsearch及head插件

    1.安装elasticsearch,参考http://www.cnblogs.com/hanyinglong/p/5409003.html就可以了 简单描述下: mkdir -p /usr/local ...

  10. lightoj 1158 - Anagram Division(记忆化搜索+状压)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1158 题解:这题看起来就像是记忆搜索,由于s很少最多就10位所以可以考虑用状压 ...