Unity实现简单的对象池
一、简介
先说说为什么要使用对象池
在Unity游戏运行时,经常需要生成一些物体,例如子弹、敌人等。虽然Unity中有Instantiate()方法可以使用,但是在某些情况下并不高效。特别是对于那些需要大量生成又需要大量销毁的物体来说,多次重复调用Instantiate()方法和Destory()方法会造成大量的性能消耗。
这时使用对象池是一个更好的选择。
那么什么是对象池呢?
简单来说,就是在一开始创建一些物体(或对象),将它们隐藏(休眠)起来,对象池就是这些物体的集合,当需要使用的时候,就将需要的对象激活然后使用,而不是实例化生成。如果对象池中的对象消耗完了可以扩大对象池或者重新再次使用对象池中的对象。
一般情况下,一个对象池中存放的都是一类物体,我们一般希望创建多个对象池来存储不同类型的物体。
例如我们需要两个对象池来分别存储球体和立方体。
那么可以选择使用Dictionary来创建对象池,这样不仅可以创建对象池,还能指定每个对象池存储对象的类型。这样就能通过Tag来访问对象池。
至于对象池中可以使用Queue(队列)来存储具体的对象,队列不仅可以快速获取到第一个对象,能够按顺序获取对象。如果出队的对象在使用完成之后再次入队,那么这样就可以一直循环来重用对象。
二、Unity中的具体实现
新建一个Unity项目,在场景中添加一个空物体,命名为ObjectPool
同时制作一个黑色的地面便于显示和观察

新建脚本ObjectPooler添加到ObjectPool上
public class ObjectPooler : MonoBehaviour
{
[System.Serializable]
public class Pool //对象池类
{
public string tag; //对象池的Tag(名称)
public GameObject prefab; //对象池所保存的物体类型
public int size; //对象池的大小
}
public List<Pool> pools;
Dictionary<string, Queue<GameObject>> poolDictionary; //声明字典
void Start()
{
//实例化字典 对象池的Tag 对象池保存的物体
poolDictionary = new Dictionary<string, Queue<GameObject>>();
}
}
在Inspector中添加对应的数据,这里简单创建了立方体和球体并设为了预制体

然后继续修改ObjectPooler
public class ObjectPooler : MonoBehaviour
{
[System.Serializable]
public class Pool
{
public string tag;
public GameObject prefab;
public int size;
}
public List<Pool> pools;
Dictionary<string, Queue<GameObject>> poolDictionary;
public static ObjectPooler Instance; //单例模式,便于访问对象池
private void Awake()
{
Instance = this;
}
void Start()
{
poolDictionary = new Dictionary<string, Queue<GameObject>>();
foreach (Pool pool in pools)
{
Queue<GameObject> objectPool = new Queue<GameObject>(); //为每个对象池创建队列
for (int i = 0; i < pool.size; i++)
{
GameObject obj = Instantiate(pool.prefab);
obj.SetActive(false); //隐藏对象池中的对象
objectPool.Enqueue(obj);//将对象入队
}
poolDictionary.Add(pool.tag, objectPool); //添加到字典后可以通过tag来快速访问对象池
}
}
public GameObject SpawnFromPool(string tag, Vector3 positon, Quaternion rotation) //从对象池中获取对象的方法
{
if (!poolDictionary.ContainsKey(tag)) //如果对象池字典中不包含所需的对象池
{
Debug.Log("Pool: " + tag + " does not exist");
return null;
}
GameObject objectToSpawn = poolDictionary[tag].Dequeue(); //出队,从对象池中获取所需的对象
objectToSpawn.transform.position = positon; //设置获取到的对象的位置
objectToSpawn.transform.rotation = rotation; //设置对象的旋转
objectToSpawn.SetActive(true); //将对象从隐藏设为激活
poolDictionary[tag].Enqueue(objectToSpawn); //再次入队,可以重复使用,如果需要的对象数量超过对象池内对象的数量,在考虑扩大对象池
//这样重复使用就不必一直生成和消耗对象,节约了大量性能
return objectToSpawn; //返回对象
}
}
新建脚本CubeSpanwer,来使用对象池生成物体
public class CubeSpanwer : MonoBehaviour
{
ObjectPooler objectPooler;
private void Start()
{
objectPooler = ObjectPooler.Instance;
}
private void FixedUpdate()
{
//这样会高效一点,比ObjectPooler.Instance
objectPooler.SpawnFromPool("Cube", transform.position, Quaternion.identity);
}
}
新建脚本Cube,添加到Cube预制体上,让其在生成时添加一个力便于观察
注意:为了方便观察这里移除了Cube上的BoxCollider
public class Cube : MonoBehaviour
{
void Start()
{
GetComponent<Rigidbody>().AddForce(new Vector3(Random.Range(0f, 0.2f), 1f, Random.Range(0f, 0.2f)));
}
}
我们发现Cube并没有向上飞起而是堆叠在一起

这时因为Cube只在生成时在Start中添加了力,只调用了一次,但马上就被隐藏放入对象池了,等到再次取出时,并没有任何方法的调用,只是单纯设置位置
我们需要让cube对象知道自己被重用了,再次调用添加力的方法
新建接口 IPooledObject
public interface IPooledObject
{
void OnObjectSpawn();
}
然后让Cube继承该接口
public class Cube : MonoBehaviour, IPooledObject
{
private Rigidbody rig;
public void OnObjectSpawn()
{
rig = gameObject.GetComponent<Rigidbody>();
rig.velocity = Vector3.zero; //将速度重置为0,物体在被隐藏时仍然具有速度,不然重用时仍然具有向下的速度
rig.AddForce(new Vector3(Random.Range(0, 0.2f), 10, Random.Range(0, 0.2f)), ForceMode.Impulse);
}
}
然后修改ObjectPooler,让Cube在被重用时调用重用的方法
public GameObject SpawnFromPool(string tag, Vector3 positon, Quaternion rotation) //从对象池中获取对象的方法
{
......
IPooledObject pooledObj = objectToSpawn.GetComponent<IPooledObject>();
if (pooledObj != null) //判断,并不是所有对象都继承了该接口,例如Cube我想让它向上飞,Sphere则让它直接生成,Sphere就不必继承IPoolObject接口
{
pooledObj.OnObjectSpawn(); //调用重用时的方法
}
poolDictionary[tag].Enqueue(objectToSpawn);
return objectToSpawn;
}
运行结果:
Cube从CubeSpawner不断生成,可以自行设置计时器来限制生成的速度

Unity实现简单的对象池的更多相关文章
- 在C#中实现简单的对象池
当我们频繁创建删除大量对象的时候,对象的创建删除所造成的开销就不容小觑了.为了提高性能,我们往往需要实现一个对象池作为Cache:使用对象时,它从池中提取.用完对象时,它放回池中.从而减少创建对象的开 ...
- Java网络与多线程系列之1:实现一个简单的对象池
前言 为什么要从对象池开始呢,先从一个网络IO操作的demo说起 比如下面这段代码,显而易见已经在代码中使用了一个固定大小的线程池,所以现在的重点在实现Runnble接口的匿名对象上,这个对象每次创建 ...
- Unity中的万能对象池
本文为博主原创文章,欢迎转载.请保留博主链接http://blog.csdn.net/andrewfan Unity编程标准导引-3.4 Unity中的万能对象池 本节通过一个简单的射击子弹的示例来介 ...
- unity游戏开发_对象池
现在假设游戏中我们需要实现一个这样功能,按下鼠标左键,发射一颗子弹,3秒之后消失.在这个功能中,我们发射了上百上千发子弹,就需要实例化生成上百上千次.这时候,我们就需要使用对象池这个概念,每次实例化生 ...
- Unity3D|-使用ScriptableObject脚本化对象来制作一个简单的对象池
ScriptableObject是一个用于生成单独Asset的结构.同时,它也能被称为是Unity中用于处理序列化的结构. 可以作为我们存储资源数据的有效方案.同时此资源可以作为我们AB包的有效资源! ...
- Unity 对象池的使用
在游戏开发过程中,我们经常会遇到游戏发布后,测试时玩着玩着明显的感觉到有卡顿现象.出现这种现象的有两个原因:一是游戏优化的不够好或者游戏逻辑本身设计的就有问题,二是手机硬件不行.好吧,对于作为程序员的 ...
- [译]Unity3D内存管理——对象池(Object Pool)
原文地址:C# Memory Management for Unity Developers (part 3 of 3), 其实从原文标题可以看出,这是一系列文章中的第三篇,前两篇讲解了从C#语言本身 ...
- common-pool2对象池(连接池)的介绍及使用
我们在服务器开发的过程中,往往会有一些对象,它的创建和初始化需要的时间比较长,比如数据库连接,网络IO,大数据对象等.在大量使用这些对象时,如果不采用一些技术优化,就会造成一些不可忽略的性能影响.一种 ...
- 【h5-egret】深入浅出对象池
最近看到对象池这一块的东西,是频繁创建和删除类型游戏优化性能的一个解决方案. 简单来讲对象池就是个数组,把不用的对象放进去,因为数组还保存了对象的引用,所以对象不会被回收,等需要用的时候再从数组中取出 ...
随机推荐
- jQuery对象的创建
1.js类库 JavaScript封装了很多的预定义的对象和实用函数,能帮助使用者建立有高难度交互 客户端页面,并且兼容各大浏览器.跑在浏览器,请求服务器 当前比较流行的js库: jquery EXT ...
- Oracle入门基础(七)一一集合运算
SQL> /* SQL> 查询10和20号部门的员工 SQL> 1. select * from emp where deptno=10 or deptno=20; SQL> ...
- java的jsr303校验
因为是菜鸡,所以就还没有具体了解jsr303具体是什么 JSR是Java Specification Requests的缩写,意思是Java 规范提案.是指向JCP(Java Community Pr ...
- 哪个类包含 clone 方法?是 Cloneable 还是 Object?
java.lang.Cloneable 是一个标示性接口,不包含任何方法,clone 方法在 object 类中定义.并且需要知道 clone() 方法是一个本地方法,这意味着它是由 c 或 c++ ...
- C语言之开发流程(知识点7)
一.C程序的运行步骤: 1.编辑:进入C语言编译系统,建立源程序文件,扩展名为"c"或"cpp",编辑并修改.保存源程序. 2.编译:计算机不能识别和执行高级语 ...
- simulink模块使用方式
逻辑模块 1.小于等于系列模块 Applies the selected relational operator to the inputs and outputs the result. The t ...
- 微信小程序版博客——开发汇总总结(附源码)
花了点时间陆陆续续,拼拼凑凑将我的小程序版博客搭建完了,这里做个简单的分享和总结. 整体效果 对于博客来说功能页面不是很多,且有些限制于后端服务(基于ghost博客提供的服务),相关样式可以参考截图或 ...
- 我的python学习记_02
流程控制 算术运算符: + 加(在字符串中拼接作用) - 减 * 乘 / 除 // 商 % 取余 ** 次幂 比较运算符: > 是否大于 >= 是否大于等于 < 是否小于 != 是否 ...
- 世界各国 MCC 和 MNC 列表
http://www.cnblogs.com/inteliot/archive/2012/08/22/2651666.html常见MCC:代码(MCC) ISO 3166-1 国家202 ...
- js原生的Ajax
js原生的Ajax其实就是围绕浏览器内内置的Ajax引擎对象进行学习的,要使用js原 生的Ajax完成异步操作,有如下几个步骤: 1)创建Ajax引擎对象 2)为Ajax引擎对象绑定监听(监听服务器已 ...