Unity3D

Unity PoolManager插件中文教程|Chinar

chinar · 11月23日 · 2018年 1556次已读


Chinar —— 心分享、心创新!

我们的初衷是将一种简单的生活方式,带给世人

使有限时间 具备无限可能


Chinar —— 心分享、心创新!

助力 Unity 插件 PoolManager 具体用法

为新手节省宝贵的时间,避免采坑!

Chinar 教程效果

Chinar
教程效果

全文高清图片,点击即可放大观看 (很多人竟然不知道)

风雨思年华

科技改变世界,编程颠覆科技!

1 Intro —— 简介

Intro —— 简介

我们在Unity开发项目中,经常大量的使用同一游戏对象,从而满足我们的游戏设计需求。

那么考虑优化方案时,不可避免的就是对象池技术,今天我们就来说下 Unity 插件 PoolManager的具体用法。

该插件发布的较早,但好在作者在不断维护来支持Unity的版本迭代,所以最新版本Unity也都能很好的支持,可以满足我们开发的多种需求。

网络上多数大神们也都只把组件的基本介绍给说了下,关于具体用法并没有做过多且全面的介绍,对于初学者来说并不友好。

那么今天,就跟着 Chinar 来具体了解下 PoolManager 插件的具体用法。我会通过几个案例,来详细说明该插件应用到项目中时具体实现方式。

2 PoolManager Intro —— 插件简介

PoolManager Intro —— 对象池插件介绍

首先我们先来了解下该插件的3个主脚本:PoolManager、SpawnPool、PreRuntimePoolItem

 (其他的是几个编辑器和示例脚本,对于初学者并不友好,有兴趣可以看看)

3 SpawnPool —— 生成池

SpawnPool —— 生成池

该脚本主要用于管理生成的对象池、对象池中元素,例如生成元素、回收元素、限制个数、自动回收等等….

将脚本挂载到游戏对象,即可看到组件参数,我们来逐一的了解下:

  1. PoolName:对象池名称
  2. MatchPoolScale:实例化预设物后比例为1,否则默认预设物比例
  3. MachPool Layer:元素Layer层级与当前对象池保持一致,否则默认预设物Layer
  4. Don’t Reparent:实例化预设物没有父物体,官方建议不要勾选
  5. Don’t Destroy On Load:切换场景,不删除对象池
  6. Log Message:控制台输出对象池信息

Pre-Prefab Pool Options :预加载对象池元素列表设置,通过+号,添加对象池预加载元素

  1. prefab:预设物拖进来
  2. preload Amount:对象池中预加载该元素总数
  3. preload Time:元素可异步加载(预加载数量庞大时,用来分摊压力)
  4. preload Frames:通过在几帧的过程中预加载大量元素,以减少每帧中的负荷
  5. preload Delay:延迟多久开始预加载
  6. limit Instance:开启/关闭实例化预设物限制功能
  7. limit Amount:当达到“限制数”时,将不再创建实例。如同时开启preload Amount则以limit Amout为准
  8. limit FIFO:开启先进先出,通过最先使用过的旧元素,生成新实例。(如:限制5个元素,当实例化第6个时,返回的是第一个元素,不勾选时取不到第6个,所以返回Null
  9. cull Despawend:实例剔除,也就是自动清理功能
  10. cull Above:执行自动清理时,至少保留多少个不清理
  11. cull Delay:剔除延时,也就是多久检测一次是否该清理元素了
  12. cull Max Per Pass:每次自动清理多少个元素
  13. 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
本博客为非营利性个人原创,除部分有明确署名的作品外,所刊登的所有作品的著作权均为本人所拥有,本人保留所有法定权利。违者必究!

0 条回应