背景

在研究Inventory Pro插件的时候,发现老外实现的一个泛型对象池,觉得设计的小巧实用,不敢私藏,特此共享出来。

以前也看过很多博友关于对象池的总结分享,但是世界这么大,这么复杂到底什么样的对象池才是好的呢,我们发现通用的对象池未必适应所有的环境,比如基于UI的局部(从某个Scene,到某个Dialog)对象池,范围不同,需要的对象池就有不同的要求。本文就是介绍一种基于预设(Prefab)的局部UI对象池。

通用信息提示窗口的实现http://www.manew.com/thread-94647-1-1.html

声明:本文首发于蛮牛,次发博客园,本人原创。

原文链接1http://www.manew.com/thread-94650-1-1.html,原文链接2

演示

左下角信息提示窗口NoticeUI中的信息提示比较多时,具有滚动条和超出自动隐藏的功能,就是通过对象池技术实现,从而提高性能和效率

下面代码是在NoticeUI中 设置对象池的相关代码

分析

老规矩先上类图,不过其实没有什么好分析的,就是对象池的标准接口,取Get和回收这里是Destroy。还有出现两个类一个是泛型的,一个是非泛型的,至于为什么是结构体而不是类,这块我也没有深究。

下面我们仔细分析下这个UI对象池的类,首先看下类的

声明和构造函数

1、类注释写的很清楚,告诉你这个对象池支持的对象是GameObjects,限制了必须是Unity3D的类型,该类的功能仅为了提高速度。

它并不完善,建议使用小规模的数据。明白了这点很重要,也就是我在背景里说的每种场景使用的对象池是不一样的,一定不能混用和滥用。

2、泛型结构体的声明,这里我们T限定了类型是UnityEngine.Component也就是Unity3D的组件基类,所以为什么注释里面说了只适合GameObjects了,剩下的就是new()关键字约束,确保类是可以new的。IPoolableObject是面向接口的一种实现约束(框架层考虑就是不一样),实际是强制要求在代码级别做好对象池类的管理。

3、构造函数是用了缺省值StatSize64很贴心,下面三句初始化代码,创建了一个_PoolParent GameObject,并挂到了全局的组件树上,我觉得的这点特别重要,这使得在Edit下可以看见池对象的动态创建和状态,特别是让组件树特别清晰,点个赞,第三句,也就是保存了泛型对象

poolParent = new GameObject("_PoolParent").transform;
poolParent.SetParent(InventoryManager.instance.collectionObjectsParent);
this.baseObject = baseObject;

泛型池对象的初始化

池对象初始化创建

泛型池对象的初始化其实是调用了GameObject.Instantiate<T>的泛型方法重载,也就是深克隆。这也就是解释了为什么可以完成对于预设(Prefab)的对象池了,然后设置其父对象,挂接到组件树上,最后调用gameObject.SetActivie(false)让UI对象不可见,其实是一个开关。

池对象的获取和销毁

池对象的获取

有了创建时候的开关,获取UI池对象就可以通过这个开关来判断对象是否已经使用了,然后如果active是false的,通过设置成true打开就完成了池对象的创建,这里createWhenNoneLeft参数默认是true,用来解决当构造函数预设池对象都被使用后如何扩展的问题,这里其实很简单了当所有预设都用完了直接再创建一个即可,多亏了c#的动态数组List<T>,不然还要像c++那样动态调整数组的大小。

池对象的销毁

对象的销毁其实是回收,这里对象最简单的一步就是SetActive(false)也就是把UI开关给关上,让对象不可见。其实重要的是调用IPoolObject接口的Reset函数实现,进行一些善后工作,让然也要通过 transform.SetParent()进行组件树的重置。所有对象的销毁和回收就很简单了只需要循环遍历销毁即可。

核心源码

using System;
using System.Collections.Generic;
using Devdog.InventorySystem;
using Devdog.InventorySystem.Models;
using UnityEngine; namespace Devdog.InventorySystem
{
/// <summary>
/// Only supports GameObjects and no unique types, just to speed some things up.
/// It's not ideal and I advice you to only use it on small collections.
/// </summary>
public struct InventoryPool<T> where T : UnityEngine.Component, IPoolableObject, new()
{
private List<T> itemPool;
private Transform poolParent;
private T baseObject; public InventoryPool(T baseObject, int startSize = )
{
poolParent = new GameObject("_PoolParent").transform;
poolParent.SetParent(InventoryManager.instance.collectionObjectsParent);
this.baseObject = baseObject; itemPool = new List<T>(startSize);
for (int i = ; i < startSize; i++)
{
Instantiate();
}
} /// <summary>
/// Create an object inside the pool
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
public T Instantiate()
{
var a = GameObject.Instantiate<T>(baseObject);
a.transform.SetParent(poolParent);
a.gameObject.SetActive(false); // Start disabled itemPool.Add(a);
return a;
} /// <summary>
/// Get an object from the pool
/// </summary>
public T Get(bool createWhenNoneLeft = true)
{
foreach (var item in itemPool)
{
if(item.gameObject.activeSelf == false)
{
item.gameObject.SetActive(true);
return item;
}
} if (createWhenNoneLeft)
{
Debug.Log("New object created, considering increasing the pool size if this is logged often");
return Instantiate();
} return null;
} /// <summary>
/// Mark an object as inactive so it can be recycled.
/// </summary>
/// <param name="item"></param>
public void Destroy(T item)
{
item.Reset(); // Resets the item state
item.transform.SetParent(poolParent);
item.gameObject.SetActive(false); // Up for reuse
} public void DestroyAll()
{
foreach (var item in itemPool)
Destroy(item);
}
} /// <summary>
/// InventoryPool only good for gameObjects
/// </summary>
public struct InventoryPool
{
private List<GameObject> itemPool;
private Transform poolParent;
private GameObject baseObject; public InventoryPool(GameObject baseObject, int startSize = )
{
poolParent = new GameObject("_PoolParent").transform;
poolParent.SetParent(InventoryManager.instance.collectionObjectsParent);
this.baseObject = baseObject; itemPool = new List<GameObject>(startSize);
for (int i = ; i < startSize; i++)
{
Instantiate();
}
} /// <summary>
/// Create an object inside the pool
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
public GameObject Instantiate()
{
GameObject a = null;
if (baseObject != null)
a = GameObject.Instantiate<GameObject>(baseObject);
else
a = new GameObject(); a.transform.SetParent(poolParent);
a.gameObject.SetActive(false); // Start disabled itemPool.Add(a);
return a;
} /// <summary>
/// Get an object from the pool
/// </summary>
public GameObject Get(bool createWhenNoneLeft = true)
{
foreach (var item in itemPool)
{
if (item.gameObject.activeInHierarchy == false)
{
item.gameObject.SetActive(true);
return item;
}
} if (createWhenNoneLeft)
{
Debug.Log("New object created, considering increasing the pool size if this is logged often");
return Instantiate();
} return null;
} /// <summary>
/// Mark an object as inactive so it can be recycled.
/// </summary>
/// <param name="item"></param>
public void Destroy(GameObject item)
{
item.transform.SetParent(poolParent);
item.gameObject.SetActive(false); // Up for reuse
} public void DestroyAll()
{
foreach (var item in itemPool)
Destroy(item);
}
}
}

Unity3D 基于预设(Prefab)的泛型对象池实现的更多相关文章

  1. 基于Apache组件,分析对象池原理

    池塘里养:Object: 一.设计与原理 1.基础案例 首先看一个基于common-pool2对象池组件的应用案例,主要有工厂类.对象池.对象三个核心角色,以及池化对象的使用流程: import or ...

  2. 屏幕坐标和世界坐标的转换+对象池技术(3D打地鼠小游戏)

    游戏中可能经常会遇到需要某个物体跟着鼠标移动,然后又需要把物体放在某个鼠标指定的位置 实现方式 Camera.main.WorldToScreenPoint Camera.main.ScreenToW ...

  3. 大数据技术之_27_电商平台数据分析项目_02_预备知识 + Scala + Spark Core + Spark SQL + Spark Streaming + Java 对象池

    第0章 预备知识0.1 Scala0.1.1 Scala 操作符0.1.2 拉链操作0.2 Spark Core0.2.1 Spark RDD 持久化0.2.2 Spark 共享变量0.3 Spark ...

  4. 游戏开发设计模式之对象池模式(unity3d 示例实现)

    前篇:游戏开发设计模式之命令模式(unity3d 示例实现) 博主才学尚浅,难免会有错误,尤其是设计模式这种极富禅意且需要大量经验的东西,如果哪里书写错误或有遗漏,还请各位前辈指正. 原理:从一个固定 ...

  5. delphi新语法之泛型实现的对象池模板

    现在的DELPHI因为支持泛型的语法,所以也能支持模板编程了.   // 标准模板 unit UntPools;   interface   uses   Classes, SysUtils, Unt ...

  6. Unity3D 游戏开发构架篇 —— 动态大场景生成 = 区域加载+对象池管理

    项目做一个类似无尽模式的场景,想了一想,其实方法很简单,做一个相关的总结. 主要先谈一谈构架,后期附上代码. 一.区域加载 其实无尽场景的实现很简单,因为屏幕限制,那么不论何时何地,我们只能看到自己的 ...

  7. Unity3d对象池

    Singleton.cs 12345678910111213 using UnityEngine;/// <summary>/// 单例模版类/// </summary>pub ...

  8. unity3d对象池的使用

    说对象池之前首先来看看单例类和单例脚本的区别.这里有介绍 http://blog.csdn.net/lzhq1982/article/details/12649281 使用对象池的好处是不用每次都创建 ...

  9. [译]Unity3D内存管理——对象池(Object Pool)

    原文地址:C# Memory Management for Unity Developers (part 3 of 3), 其实从原文标题可以看出,这是一系列文章中的第三篇,前两篇讲解了从C#语言本身 ...

随机推荐

  1. powershell 批量生成文件

    缘由 由于现在替省厅工作,年底了要向地市收很多文件,哪些地市已报,哪些没报,需要一目了然. 我的处理方式是收什么文件就针对该文件新建一个目录,然后在该目录下为所有地市建一个占位文件. 等收到项某市的材 ...

  2. IMAP和POP3有什么区别

    http://help.163.com/10/0203/13/5UJONJ4I00753VB8.html?servCode=6010237 IMAP和POP3有什么区别?   POP3协议允许电子邮件 ...

  3. 【转】MySQL USE NAMES 'UTF8'

    先说MySQL的字符集问题.Windows下可通过修改my.ini内的 # CLIENT SECTION [mysql] default-character-set=utf8 # SERVER SEC ...

  4. 15 款最好的 C/C++ 编译器和集成开发环境

    我们有很多编程语言来进行 web 开发,比如 Java,.Net,PHP,Ruby,Perl,Python 等等.今天我们主要讨论的是两大古老而又流行的语言: C 和 C++ ,它们有着许多卓越的特性 ...

  5. C/C++ 判断主机字节存储序列

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAA0oAAADFCAIAAADltpUqAAAgAElEQVR4nOyd65XyvA6FqYASKIEWqI

  6. JQuery源码之“对象的结构解析”

    吃完午饭,觉得有点发困,想起了以后我们的产品可能要做到各种浏览器的兼容于是乎不得不清醒起来!我们的web项目多数是依赖于Jquery的.据了解,在Jquery的2.0版本以后对IE的低端版本浏览器不再 ...

  7. Spark Programming--Actions II

    saveAsTextFile saveAsTextFile(path, compressionCodecClass=None) aveAsTextFile用于将RDD以文本文件的格式存储到文件系统中, ...

  8. 【转】深入理解TextView实现Rich Text--在同一个TextView设置不同字体风格

    深入理解TextView实现Rich Text--在同一个TextView设置不同字体风格 作者: 字体:[增加 减小] 类型:转载   本篇文章是对Android中在同一个TextView中设置不同 ...

  9. imx6 生成 spi设备节点

    开发板需要使用spi接口,但是spi接口被touch占用,使用event进行操作.所以需要更改配置,生成spi设备节点. 参考链接 https://community.nxp.com/thread/3 ...

  10. 树莓派连接wifi

    使用树莓派,通过无线网卡连接wifi,再通过远程桌面或者ssh的连接树莓派比较方便,本文记录树莓派wifi如何设置. 参考链接: http://www.jianshu.com/p/b42e8d3df4 ...