UniRx-unirx中的对象池

对象池Unirxunity 对象池

一、对象池模式

《游戏设计模式-对象池模式》

1.概念

定义一个池对象,其包含了一组可重用对象。 其中每个可重用对象都支持查询“使用中”状态,说明它是不是“正在使用”。 池被初始化时,它就创建了整个对象集合(通常使用一次连续的分配),然后初始化所有对象到“不在使用中”状态。

当你需要新对象,向池子要一个。 它找到一个可用对象,初始化为“使用中”然后返回。 当对象不再被需要,它被设置回“不在使用中”。 通过这种方式,可以轻易地创建和销毁对象而不必分配内存或其他资源。

2.优点

游戏中会出现诸如子弹,粒子特效等大量的单一资源。频繁的创建和销毁会浪费很多性能并且产生大量的内存碎片。对象池模式放弃单独地分配和释放对象,从固定的池中重用对象,以提高性能和内存使用率。


enter description here

3.何时使用

  • 需要频繁创建和销毁对象。
  • 对象大小相仿。
  • 在堆上进行对象内存分配十分缓慢或者会导致内存碎片。
  • 每个对象都封装了像数据库或者网络连接这样很昂贵又可以重用的资源。

4.注意

  • 可以将创建对象和销毁对象平缓的放到多个帧处理
  • 动态的维护池子的大小

UniRx中的对象池使用

1.案例

1.1 prefab上脚本

using System;
using UniRx;
using UnityEngine;
using Random = UnityEngine.Random; public class PoolObject : MonoBehaviour
{
public IObservable<Unit> AsyncAction()
{
var colorstream = Observable.Timer(TimeSpan.FromSeconds(0.2f));
colorstream.Subscribe(_ =>
{
GetComponent<Renderer>().material.color = Color.Lerp(Color.red, Color.blue, Random.Range(0.0f,1.0f));
});
var positiontream = Observable.Timer(TimeSpan.FromSeconds(0.2f));
positiontream.Subscribe(_ =>
{
transform.localPosition=Vector3.one*Random.Range(-5f,5f);
}); var unit = Observable.ReturnUnit();
var allStream = Observable.WhenAll(unit);
allStream.Subscribe(_ =>
{
Debug.Log("QAQ");
}); return Observable.ReturnUnit();
}
}

2.自定义对象池

using System;
using UniRx;
using UniRx.Toolkit;
using UnityEngine;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random; public class UniRxObjectPool : ObjectPool<PoolObject>
{ private GameObject _prefab; public UniRxObjectPool(GameObject prefab)
{
this._prefab = prefab;
} protected override PoolObject CreateInstance()
{
var gameObj = Object.Instantiate(_prefab);
return gameObj.GetComponent<PoolObject>();
} protected override void OnBeforeRent(PoolObject instance)
{
base.OnBeforeRent(instance);
Debug.Log($"从池子中取出:{instance.name}");
} // 在对象返回到池子里面之前回调
protected override void OnBeforeReturn(PoolObject instance)
{
base.OnBeforeReturn(instance);
Debug.Log($" 返回 {instance} 到池子里面");
} // 在对象从池子里面移除回调
protected override void OnClear(PoolObject instance)
{
base.OnClear(instance);
Debug.Log($"从池子里面移除 {instance}");
}
}

3.对象池调用

using System;
using UniRx;
using UnityEngine;
using UnityEngine.UI; public class No11_ObjectPool : MonoBehaviour
{
[SerializeField]
private GameObject mPoolPrefab; [SerializeField]
private Button mBtnSpawn; [SerializeField]
private Button mBtnShrink; [SerializeField]
private Button mBtnClear; void Start()
{
UniRxObjectPool pool = new UniRxObjectPool(mPoolPrefab); mBtnSpawn.OnClickAsObservable().Subscribe(_ =>
{
for (int i = 0; i < 5; i++)
{
//从池中获取实例,出栈
var poolObj = pool.Rent();
poolObj.AsyncAction().Subscribe(next =>
{
Debug.Log("mult thread run");
});
//根据对象创建的频率来扩张池子大小
Observable.TimerFrame(300).Subscribe(f =>
{
//回收对象到池子
pool.Return(poolObj);
});
} }); mBtnShrink.OnClickAsObservable().Subscribe(_ =>
{
// 手动回收对象池子, 第一个参数是回收比例,第二个参数是保存最小数量
//pool.Shrink(0.6f, 1); // 自动回收对象池子,3秒检查一次进行回收,销毁实例,缩小池子大小,保持最小数
pool.StartShrinkTimer(TimeSpan.FromSeconds(3f), 0.6f, 2);
}); mBtnClear.OnClickAsObservable().Subscribe(_ =>
{
//清理对象池,destroy所有对象
pool.Clear();
});
}
}

2.源码

1.回收实例到对象池

将实例入栈,可以在OnBeforeReturn中对将要回收的实例进行以下操作,比如隐藏。

  1. /// <summary> 


  2. /// Return instance to pool. 


  3. /// </summary> 


  4. public void Return(T instance) 





  5. if (isDisposed) throw new ObjectDisposedException("ObjectPool was already disposed."); 


  6. if (instance == null) throw new ArgumentNullException("instance"); 



  7. if (q == null) q = new Queue<T>(); 



  8. if ((q.Count + 1) == MaxPoolCount) 





  9. throw new InvalidOperationException("Reached Max PoolSize"); 






  10. OnBeforeReturn(instance); 


  11. q.Enqueue(instance); 






2.缩减对象池

设置一个对象池保有对象实例的最小值,然后不断按照一定的比例系数出栈操作来缩减池子大小

  1. /// <summary> 


  2. /// Trim pool instances.  


  3. /// </summary> 


  4. /// <param name="instanceCountRatio">0.0f = clear all ~ 1.0f = live all.</param> 


  5. /// <param name="minSize">Min pool count.</param> 


  6. /// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param> 


  7. public void Shrink(float instanceCountRatio, int minSize, bool callOnBeforeRent = false) 





  8. if (q == null) return; 



  9. if (instanceCountRatio <= 0) instanceCountRatio = 0; 


  10. if (instanceCountRatio >= 1.0f) instanceCountRatio = 1.0f; 



  11. var size = (int)(q.Count * instanceCountRatio); 


  12. size = Math.Max(minSize, size); 



  13. while (q.Count > size) 





  14. var instance = q.Dequeue(); 


  15. if (callOnBeforeRent) 





  16. OnBeforeRent(instance); 





  17. OnClear(instance); 








  1. /// <summary> 


  2. /// If needs shrink pool frequently, start check timer. 


  3. /// </summary> 


  4. /// <param name="checkInterval">Interval of call Shrink.</param> 


  5. /// <param name="instanceCountRatio">0.0f = clearAll ~ 1.0f = live all.</param> 


  6. /// <param name="minSize">Min pool count.</param> 


  7. /// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param> 


  8. public IDisposable StartShrinkTimer(TimeSpan checkInterval, float instanceCountRatio, int minSize, bool callOnBeforeRent = false) 





  9. //UniRx Interval定间隔调用 


  10. return Observable.Interval(checkInterval) 


  11. .TakeWhile(_ => !isDisposed)//TakeWhile(condition) conditon为false时终止,且不包含临界的item 


  12. .Subscribe(_ => 





  13. Shrink(instanceCountRatio, minSize, callOnBeforeRent); 


  14. }); 





二、小结

对象池的好处就是提高性能和内存使用率,适合诸如子弹,弹幕,粒子效果(另一种优化技术GPU Intance),小怪群等。它和享元模式的区别类似于享元模式接头缝合怪,很多人用一个头,它则是可量产回收的机械克隆人。

UniRx-unirx中的对象池的更多相关文章

  1. Java中的对象池技术

    java中的对象池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复重复创建相等变量时节省了很多时间.对象池其实也就是一个内存 ...

  2. Unity编程标准导引-3.4 Unity中的对象池

    本文为博主原创文章,欢迎转载.请保留博主链接http://blog.csdn.net/andrewfan Unity编程标准导引-3.4 Unity中的对象池 本节通过一个简单的射击子弹的示例来介绍T ...

  3. Java 中的对象池实现

    点赞再看,动力无限.Hello world : ) 微信搜「程序猿阿朗 」. 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章. 最近在 ...

  4. 关于tomcat中Servlet对象池

    Servlet在不实现SingleThreadModel的情况下运行时是以单个实例模式,如下图,这种情况下,Wrapper容器只会通过反射实例化一个Servlet对象,对应此Servlet的所有客户端 ...

  5. .net core中的对象池

    asp.net core中通过扩展库的方式提供给了一个标准的对象池ObjectPool,定义在Microsoft.Extensions.ObjectPool.dll 程序集中.它本身是个纯虚的抽象类, ...

  6. GameObjectPool——Unity中的对象池

    这里介绍一种对象池的写法.它的优点在于无论取出还是插入游戏物体都是常数量时间. using UnityEngine; using System.Collections; using System.Co ...

  7. Egret中的对象池ObjectPool

    为了可以让对象复用,防止大量重复创建对象,导致资源浪费,使用对象池来管理. 对象池具体含义作用,自行百度. 一 对象池A 二 对象池B 三 字符串key和对象key的效率 一 对象池A /** * 对 ...

  8. Egret中的对象池Pool

    为了可以让对象复用,防止大量重复创建对象,导致资源浪费,使用对象池来管理. 一 对象池A 二 对象池B 一 对象池A 1. 支持传入构造函数 2. 支持预先创建对象 3. 支持统一执行函数 /** * ...

  9. Unity中的万能对象池

    本文为博主原创文章,欢迎转载.请保留博主链接http://blog.csdn.net/andrewfan Unity编程标准导引-3.4 Unity中的万能对象池 本节通过一个简单的射击子弹的示例来介 ...

  10. 对象池在 .NET (Core)中的应用[2]: 设计篇

    <编程篇>已经涉及到了对象池模型的大部分核心接口和类型.对象池模型其实是很简单的,不过其中有一些为了提升性能而刻意为之的实现细节倒是值得我们关注.总的来说,对象池模型由三个核心对象构成,它 ...

随机推荐

  1. 通用的 AI prompt 实操技巧

    1. 提供清晰.具体的目标在 Prompt 中明确指出你希望 AI 辅助完成的具体任务,包括要实现的功能.遵循的标准.适用的技术栈等. 2. 提供足够的上下文提供与任务相关的背景信息.现有代码片段.接 ...

  2. [GPT] export default {} 和 export {} 的区别

    在JavaScript ES6模块系统中,export default {} 和 export {} 有明显的区别: 1. export default {}: 这个语法用于导出一个默认的模块成员,它 ...

  3. [FAQ] 设置 npm 镜像源

    查看 npm 源: $ npm config get registry> http://registry.npmjs.org/ 修改 npm 源: $ npm config set regist ...

  4. 2019-3-15-uwp-ScrollViewer-content-out-of-panel-when-set-the-long-width

    title author date CreateTime categories uwp ScrollViewer content out of panel when set the long widt ...

  5. aliyun全站DCDN刷新--Django

    1.编写原因: 由于登录到阿里云DCDN,需要登录加打开各种页面,导致推送一次感觉非常麻烦,所以编写(网上以有很多可以借鉴) 2.基础环境 # 所需模块 pip install aliyun-pyth ...

  6. Go-Zero从0到1实现微服务项目开发(二)

    前言 书接上回,继续更新GoZero微服务实战系列文章. 上一篇被GoZero作者万总点赞了,更文动力倍增,也建议大家先看巧一篇,欢迎粉丝股东们三连支持一波:Go-zero微服务快速入门和最佳实践(一 ...

  7. SAP Adobe Form 教程三 日期,时间,floating field

    前文: SAP Adobe Form 教程一 简单示例 SAP Adobe Form 教程二 表 原文标题:SAP Adobe Interactive Form Tutorial. Part III. ...

  8. 05.Java 方法详解

    1.方法的定义及调用 设计方法的原则:一个方法只完成一个功能,有利于后期的扩展 方法的定义: 修饰符(可选) 返回值类型 方法名(参数类型 参数名(可选)){ 方法体 return 返回值; } 2. ...

  9. ECMAScript 语言规范每年都会进行一次更新,而备受期待的 ECMAScript 2024 将于 2024 年 6 月正式亮相。目前,ECMAScript 2024 的候选版本已经发布,为我们带来了一系列实用的新功能。

    Promise.withResolvers 使用 Promise.withResolvers() 关键的区别在于解决和拒绝函数现在与 Promise 本身处于同一作用域,而不是在执行器中被创建和一次性 ...

  10. C语言(较深入原理):%s通过字符串首元素地址输出,用指针数组来作示例

    首先,我们输出一个字符串都知道是用%s来输出,但是我们并没有多想是通过什么方式来输出的. 今天我在看指针数组的时候发现了一个问题,按就是定义一个字符类型的指针数组, /*字符串的输出本身就需要他的地址 ...