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】深入浅出对象池
最近看到对象池这一块的东西,是频繁创建和删除类型游戏优化性能的一个解决方案. 简单来讲对象池就是个数组,把不用的对象放进去,因为数组还保存了对象的引用,所以对象不会被回收,等需要用的时候再从数组中取出 ...
随机推荐
- 在 java 中守护线程和本地线程区别?
java 中的线程分为两种:守护线程(Daemon)和用户线程(User). 任何线程都可以设置为守护线程和用户线程,通过方法 Thread.setDaemon(bool on):true 则把该线程 ...
- idea常见设置一
1.顶部执行栏调整 原: 设置:勾选view==>toolbar 后: 这个一看其实没有什么太大的作用,但是你看这个按钮 这个按钮就是File==>Settings,这样会稍微方便点,只要 ...
- python学习笔记(六)——程序调试
在我们平时编写程序时,常常会遇到各种错误,俗称BUG.而我们程序猿的工作常常需要对程序进行调试,也就是所谓的debug. 程序调试是将编制的程序投入实际运行前,用手工或编译程序等方法进行测试,修正语法 ...
- React+dva+webpack+antd-mobile 实战分享(二)
第一篇 https://segmentfault.com/a/11... 在上一篇文章中教给大家了怎么搭建项目的架子:那么今天我们就来说一下项目里的导航和列表的实现 导航 废话不说啦 下面直接给大家讲 ...
- 如何利用WebSocket实现网页版聊天室
花了将近一周的时间终于完成了利用WebSocket完成网页版聊天室这个小demo,期间还走过了一段"看似弯曲"的道路,但是我想其实也不算是弯路吧,因为你走过的路必将留下你的足迹.这 ...
- JS 用状态机的思想看Generator之基本语法篇
前言 最近学习了阮一峰老师的<ECMAScript 6 入门>里的Generator相关知识,以及<你不知道的JS>中卷的异步编程部分.同时在SegmentFault问答区看到 ...
- Android bluetoothAdapter.startDiscovery()无法搜索设备问题解决办法
Android6.0以上要定位权限,要手动把手机软件的定位权限打开,又被坑了好长时间
- 各种类型的Dialog
下面是几种对话框的效果 图一: 图二: 图三: 图四: 图五: 图六: 图七: 图1效果:该效果是当按返回按钮时弹出一个提示,来确保无误操作,采用常见的对话框样式. 代码: 创建对话框方法dialog ...
- Windows CMD常用命令集合
CMD命令: 开始->运行->键入cmd或command(在命令行里可以看到系统版本.文件系统版本) chcp 修改默认字符集chcp 936默认中文chcp 65001 1. appwi ...
- swig模板引擎和ejs模板引擎
swig模板引擎的基本用法: 1. 变量 {{ name }} //name名前后必须要加空格,不加就会报错 2. 属性 {{ student.name }} 3. 模板继承 swig使用exten ...