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】深入浅出对象池
最近看到对象池这一块的东西,是频繁创建和删除类型游戏优化性能的一个解决方案. 简单来讲对象池就是个数组,把不用的对象放进去,因为数组还保存了对象的引用,所以对象不会被回收,等需要用的时候再从数组中取出 ...
随机推荐
- awk 详解?
awk '{pattern + action}' {filenames} #cat /etc/passwd |awk -F ':' '{print 1"\t"7}' //-F 的意 ...
- UML 中有哪些常用的图?
UML 定义了多种图形化的符号来描述软件系统部分或全部的静态结构和动态结 构,包括:用例图(use case diagram).类图(class diagram).时序图(sequence diagr ...
- My模板设计模式
模板模式 目标: 第一个设计模式:模板模式 步骤: 第一个设计模式:模板模式 讲解: 我们现在使用抽象类设计一个模板模式的应用, 例如在小学的时候,我们经常写作文,通常都是有模板可以套用的. 假如我现 ...
- Python中查看变量的类型,内存地址,所占字节的大小
查看变量的类型 #利用内置type()函数 >>> nfc=["Packers","49"] >>> afc=[" ...
- ADAS最全整理
为了实现「无人驾驶」的理想,各大整车厂和供应商目前正在开发「高级驾驶员辅助系统」(ADAS),随着 ADAS 慢慢被完善,车辆也在一步一步从 NHTSa 的 0 级过度到第 4 级,实现完全自动化.& ...
- es8(字符串,对象)
es8(字符串,对象) 字符串补白: let str = "abc"; let a = str.padEnd(5); let b = str.padStart(5); let c ...
- java中如何使用接口继承(Extending Interfaces)
5.接口继承(Extending Interfaces)和通话talk的功能.而Moto888更为高级,除了照相和通话功能以外,还有mp3的功能.接口继承到底有什么意义呢?马克-to-win:1)通过 ...
- tomcat启动报错:A child container failed during start
环境:maven3.3.9+jdk1.8+tomcat8.5 错误详细描述: 严重: A child container failed during start java.util.concurren ...
- MapReduce在集群执行任务时报错:Initialization of all the collectors failed. Error in last collector was:java.lang.ClassCastException
报错信息详细: Error: java.io.IOException: Initialization of all the collectors failed. Error in last colle ...
- 使用vue-cli构建工具构建vue项目时候组件的使用
<template> <div class="contains"> <!-- <div class="main"> & ...