Chinar —— 心分享、心创新!
我们的初衷是将一种简单的生活方式,带给世人
使有限时间 具备无限可能
Chinar —— 心分享、心创新!
助力 Unity 插件 PoolManager 具体用法
为新手节省宝贵的时间,避免采坑!
Chinar 教程效果
全文高清图片,点击即可放大观看 (很多人竟然不知道)
风雨思年华
科技改变世界,编程颠覆科技!
1 Intro —— 简介
Intro —— 简介
我们在Unity开发项目中,经常大量的使用同一游戏对象,从而满足我们的游戏设计需求。
那么考虑优化方案时,不可避免的就是对象池技术,今天我们就来说下 Unity 插件 PoolManager的具体用法。
该插件发布的较早,但好在作者在不断维护来支持Unity的版本迭代,所以最新版本Unity也都能很好的支持,可以满足我们开发的多种需求。
网络上多数大神们也都只把组件的基本介绍给说了下,关于具体用法并没有做过多且全面的介绍,对于初学者来说并不友好。
那么今天,就跟着 Chinar 来具体了解下 PoolManager 插件的具体用法。我会通过几个案例,来详细说明该插件应用到项目中时具体实现方式。
2 PoolManager Intro —— 插件简介
PoolManager Intro —— 对象池插件介绍
首先我们先来了解下该插件的3个主脚本:PoolManager、SpawnPool、PreRuntimePoolItem
(其他的是几个编辑器和示例脚本,对于初学者并不友好,有兴趣可以看看)
3 SpawnPool —— 生成池
SpawnPool —— 生成池
该脚本主要用于管理生成的对象池、对象池中元素,例如生成元素、回收元素、限制个数、自动回收等等….
将脚本挂载到游戏对象,即可看到组件参数,我们来逐一的了解下:

- PoolName:对象池名称
- MatchPoolScale:实例化预设物后比例为1,否则默认预设物比例
- MachPool Layer:元素Layer层级与当前对象池保持一致,否则默认预设物Layer
- Don’t Reparent:实例化预设物没有父物体,官方建议不要勾选
- Don’t Destroy On Load:切换场景,不删除对象池
- Log Message:控制台输出对象池信息
Pre-Prefab Pool Options :预加载对象池元素列表设置,通过+号,添加对象池预加载元素
- prefab:预设物拖进来
- preload Amount:对象池中预加载该元素总数
- preload Time:元素可异步加载(预加载数量庞大时,用来分摊压力)
- preload Frames:通过在几帧的过程中预加载大量元素,以减少每帧中的负荷
- preload Delay:延迟多久开始预加载
- limit Instance:开启/关闭实例化预设物限制功能
- limit Amount:当达到“限制数”时,将不再创建实例。如同时开启preload Amount则以limit Amout为准
- limit FIFO:开启先进先出,通过最先使用过的旧元素,生成新实例。(如:限制5个元素,当实例化第6个时,返回的是第一个元素,不勾选时取不到第6个,所以返回Null
- cull Despawend:实例剔除,也就是自动清理功能
- cull Above:执行自动清理时,至少保留多少个不清理
- cull Delay:剔除延时,也就是多久检测一次是否该清理元素了
- cull Max Per Pass:每次自动清理多少个元素
- log Message:输出日志
以上配置属性无需全部理解,先知道一个大概,我们会在后续实现时,根据需求来逐渐熟悉,无需刻意去记4
4 Try to spawn —— 尝试生产
Try to spawn —— 尝试生产
说不如做,下面我们来尝试通过简单拖拽、配置属性的方式,来完成一个简单的对象池的生产。
我们让命名为”Chinar”的对象池,生成10个Cube预设物,并开启自动清理功能,每隔1秒清理2个元素,最终保留5个不进行清理。

现在,你已经学会利用 SpawnPool 创建对象池,并完成对池中元素进行预加载、配置属性。但说实话,这满足不了我们的需求。因为项目开发中,我们大多需要利用代码来动态的控制池中元素,如池中元素的创建和回收,所以通过该方式创建的对象池,需要我们在项目开始时,就把我们所需要实例化的元素加入进来,这在使用上有一定局限性。那么4例子也有存在的必要性,因为我们通过这种方式,可以很方便的观察到不同属性的设定后,对象池是如何对池中元素进行处理的,这对于初学者来说很有必要。
5 Dynamic Spawn —— 动态生成
Dynamic Spawn —— 动态生成
下面我们通过 Chinar 写的一个形象直观的例子,理解通过代码获取对象池中预加载元素,并进行生产和回收。
我们就用 4 例子来做说明。

// ========================================================
// 描述:Chinar —— Demo 1:动态生成/回收
// 挂在层次面板的空物体上
// 作者:Chinar
// 创建时间:2018-11-26 16:07:17
// 版 本:1.0
// ========================================================
using PathologicalGames;
using UnityEngine;
public class ChinarPoolTest : MonoBehaviour
{
private Transform Precube; //对象池预设
void Start()
{
//获取对象池《Chinar》 中的预加载元素 “Cube”
Precube = PoolManager.Pools["Chinar"].prefabs["Cube"]; // (就相当于我们在 SpawnPool中挂载的预设物)
Transform spawnCube = PoolManager.Pools["Chinar"].Spawn(Precube); //然后实例化 Precube 类型的 元素
spawnCube.name = "Cube 已被使用"; //改名 (Chinar为了例子效果)
PoolManager.Pools["Chinar"].Despawn(spawnCube, 3); //3秒后清除
}
private void OnGUI()
{
if (GUI.Button(new Rect(111, 111, 200, 100), "发射 Cube")) //绘制按钮
{
Transform spawnCube = PoolManager.Pools["Chinar"].Spawn(Precube); //生成对象池元素
spawnCube.name = "Cube 已被使用"; //改名
PoolManager.Pools["Chinar"].Despawn(spawnCube, 3); //3秒后,回收
}
}
}
6 Extension—— 扩展射击示例
Dynamic Spawn Extension —— 动态生成扩展
Chinar 写了一个简单的射击示例,来帮助理解如何利用代码,完成对象池的动态创建、元素的生产、回收、自动清理等功能。



// ========================================================
// 描述:Chinar —— Demo 2:射击子弹
// 练习 PoolManager 对象池的创建,以及 池中元素的生成与回收
//
// 作者:Chinar
// 创建时间:2018-11-22 17:07:01
// 版 本:1.0
// ========================================================
using System.Collections.Generic;
using PathologicalGames;
using UnityEngine;
public class ChinarShootingBullets : MonoBehaviour
{
private SpawnPool pool; // 池对象
private Transform[] resourcesBullets; //资源中所有子弹
private Dictionary<string, Transform> dicBullet = new Dictionary<string, Transform>(); //字典
/// <summary>
/// 初始化函数
/// </summary>
void Start()
{
//重载2:在指定对象上,动态生成对象池,命名中如果有 Pool 会自动剔除
var pool2 = PoolManager.Pools.Create("Chinar888", GameObject.Find("Has been named Pool"));
/*
* 开始
*/
pool = PoolManager.Pools.Create("Chinar"); //动态生成一个对象池
pool.group.parent = transform; //给这个池子设置父物体
pool.group.localPosition = new Vector3(0, 0, 0); //设置池子的自身坐标
pool.group.localRotation = Quaternion.identity; //自身角度
resourcesBullets = Resources.LoadAll<Transform>("Bullet"); //加载Resources中所有子弹预设
foreach (var prefab in resourcesBullets)
{
dicBullet.Add(prefab.name, prefab); //做记录
PrefabPool prefabPool = new PrefabPool(prefab) //创建池中元素 Prefab 实例化,设定各个参数
{
preloadAmount = 0, //预加载数量
preloadTime = true, //预加载时间
preloadFrames = 6, //预加载所需帧数
limitInstances = true, //开启限制数量
limitFIFO = true, //池中元素达到最大时,取到为空。开启该选项,会取最不常用的颗粒
limitAmount = 100, //限制100个
cullDespawned = true, //开启自动回收
cullAbove = 5, //最低保留个数
cullDelay = 3, //回收检测间隔3秒
cullMaxPerPass = 20 //每次最大回收5个
};
pool.CreatePrefabPool(prefabPool); //开始创建指定PrefabPool元素
}
}
/// <summary>
/// 每帧刷新
/// </summary>
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Transform prefab = resourcesBullets[Random.Range(0, resourcesBullets.Length)]; //随机发射预设
Vector3 vector3 = Camera.main.ScreenToWorldPoint(Input.mousePosition); //初始位置
Quaternion quaternion = Quaternion.Euler(new Vector3(0, Random.Range(-28, 28), 0)); //初始角度
Transform t = pool.Spawn(prefab, vector3, quaternion); //生成
pool.Despawn(t, 3); //3秒后,回收
}
if (Input.GetMouseButton(1))
{
Transform t = pool.Spawn(resourcesBullets[Random.Range(0, resourcesBullets.Length)],
Camera.main.ScreenToWorldPoint(Input.mousePosition), Quaternion.Euler(new Vector3(0, Random.Range(-28, 28), 0)));
pool.Despawn(t, 3);
}
}
}
// ========================================================
// 描述:子弹脚本 —— 挂在子弹预设物上,对子弹本身做处理
// 作者:Chinar
// 创建时间:2018-11-22 17:49:19
// 版 本:1.0
// ========================================================
using System.Collections;
using PathologicalGames;
using UnityEngine;
public class ChinarBullet : MonoBehaviour
{
private Material deMaterial; //默认材质
public float Speed = 60f; //渐变速度
void Start()
{
deMaterial = GetComponent<MeshRenderer>().material;
InvokeRepeating("ChangeColor", 0, 1);
}
void Update()
{
transform.Translate(Vector3.forward * Time.deltaTime * Speed);
}
/// <summary>
/// 随机颜色
/// </summary>
/// <returns> Color </returns>
private Color RandomColor()
{
float r = Random.Range(0f, 1f);
float g = Random.Range(0f, 1f);
float b = Random.Range(0f, 1f);
Color color = new Color(r, g, b);
return color;
}
/// <summary>
/// 改变颜色
/// </summary>
private void ChangeColor()
{
if (gameObject.activeSelf)
{
StartCoroutine(ColorEnumerator(RandomColor()));
}
else
{
StopAllCoroutines();
}
}
/// <summary>
/// 开启协程 —— 循环颜色
/// </summary>
IEnumerator ColorEnumerator(Color temColor)
{
while (true)
{
deMaterial.color = Color.Lerp(deMaterial.color, temColor, Speed * Time.deltaTime); //插值
yield return 0; //等一帧
}
}
/// <summary>
/// 回调函数 —— 元素被创建时自动调用
/// </summary>
private void OnSpawned(SpawnPool pool)
{
}
/// <summary>
/// 回调函数 —— 对象池元素被回收时自动调用
/// </summary>
private void OnDespawned(SpawnPool pool)
{
GetComponent<Rigidbody>().Sleep();
name = "已回收";
}
}
7 Practice—— 练习
Practice—— 练习
Chinar 已经说得够细致了,每行都有中文注释。
但想要更快的掌握,还是需要大家跟着教程,自己做一遍练习,去实实在在的理解该插件该如何使用。
另外附上 PoolManager 官方文档,英语好的可以看看。
END
欢迎分享、点赞、转载、和链接博客站点或文章,使用时请务必注明文章或内容出处并注明网址。
其它事宜,请与本博主进行联系,留言,Email: ichinar@icloud.com
本博客为非营利性个人原创,除部分有明确署名的作品外,所刊登的所有作品的著作权均为本人所拥有,本人保留所有法定权利。违者必究!