UnityVR--PoolManager--对象池1

  本篇中使用的API:gameObject.CompareTag("标签")、UnityEvent()事件管理、ObjectPool<GameObject>()对象池

  参照unity官方教程:Hi ObjectPool 


目录

1. 应用场景

   2. 对象池的原理

  3. 查看资源消耗情况

  4. 不使用对象池实现的情形

  5. 使用对象池

  6. 是否使用对象池的比较


1. 应用场景

  对象池管理的作用场景是当短时间出现大量对象时,合理管理对象的出现、消亡,从而优化内存、GPU性能管理,不至于出现卡顿等现象,这在基于移动平台的场景中尤为明显。比如说下面这个游戏结束时,大量奖品突然涌现的画面:

  另外,还有在游戏中经常看到的,使用到大量子弹、受到大量怪兽攻击等的画面。

   2. 对象池的原理

  在上面的场景中,如果在短时间内大量地创建和销毁重复的对象,就会消耗大量资源,并产生大量的垃圾。在垃圾回收的时候,会中断执行程序代码,直到回收完成。这个时间可能持续1ms~几百ms,在PC上可能直观感受不明显,但到移动端、VR一体机或者其他低端终端运行时就会看到游戏的卡顿。

  因此,现在的Unity使用了ObjectPool的思想,即把场景中需要大量出现的GameObject存入一个数据集(字典或数组),就像一个池子一样。需要在场景中呈现时就激活它——SetActive(true),而不是重新去创建,不用了就让它失活——SetActive(false),而不是销毁它。  

  3. 查看资源消耗情况

  以下比较使用和不使用对象池的情况,对于运行时资源占用的情况,可以在菜单Windows->Analysis->Profiler查看,可以选择Scripts(脚本)和GarbageCollector(垃圾回收),重点查看这两项:

  

  4. 不使用对象池实现的情形

  如果不使用对象池管理,那么在Update()中会不断地使用Instantiate实例化物体,并且用Destroy()销毁物体,会让系统不断地分配内存和回收内存。下面写一个不使用对象池管理的例子,包含Trophy.cs和TrophyManager.cs两段代码。

  (1)建立简单测试场景:在场景中建立一个空节点(Trophy),把需要大量显示的对象都放在它底下,这些对象每一个都要假设Rigidbody和Collider,等下需要它们触地消失。

  

   (2)Trophy代码:动态地挂在每个奖品上,其中:

    定义一个事件destroyEvent,用于触发TrophyManager中的销毁(Destroy)回调;

    使用一个OnCollisionEnter控制奖品接触其他碰撞器后, 激活destroyEvent事件。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;//使用事件时,需要加载这个命名空间
//对象:控制每个奖品,动态地挂载在每一个礼物上
//作用:让礼物碰到其他碰撞器就消失

public class Trophy : MonoBehaviour
{
    public UnityEvent destroyEvent=new UnityEvent();
    //公开定义一个用于销毁的事件,在TrophyManager中监听它并调用Destroy
    public  bool isDestroy = false;
    //判断是否已经销毁,以免重复触碰不同的Collider多次销毁

    public void OnEnable()
    {//Trophy有效时(不管是激活的还是新创建的),先把isDestroy标记置为false
        isDestroy = false;
    }

    private void OnCollisionEnter(Collision collision)
    {//碰撞事件
        if(collision.gameObject.CompareTag("Ground") &&!isDestroy)
        {//如果碰到了标签为"Ground"的碰撞器,且Trophy本身也是有效状态
            isDestroy = true;
            destroyEvent?.Invoke(); //触发销毁事件
        }
    }
}

   (3)TrophyManager代码:这里只要做两个步骤:第一步,创建新的Trophy;第二步:监听到销毁事件后,销毁Trophy。

public class TrophyManager : MonoBehaviour
{
    public GameObject[] trophies; //建立一个数组,用于存放所有的trophy对象
    public int number = 50; //定义在场上新建的trophy对象的数量

    void Update()
    {        
        for(int i=0;i<number;i++)
        {
            var trophy=Instantiate(trophies[Random.Range(0, trophies.Length)], transform);
            //实例化对象到父物体(挂载TrophyManager脚本的对象)下
            
            trophy.transform.localPosition= Random.insideUnitSphere;
            //Random.insideUnitSphere返回半径为1的球体内的一个随机点

            trophy.AddComponent<Trophy>().destroyEvent.AddListener(() =>
            {   //监听Trophy脚本中的销毁事件
                Destroy(Trophy);
            });

  这个脚本挂在所有奖品的父节点上,并将所有奖品对象拖入数组当中。这样就能执行礼物喷涌的效果。

  5. 使用对象池

  使用Unity定义的对象池,需要在开始使用一下命名空间:using UnityEngine.Pool;

  Trophy.cs代码不做更改,TrophyManager.cs代码修改如下

public class TrophyManager : MonoBehaviour
{
    public GameObject[] trophies;
    public int number = 50;
    private ObjectPool<GameObject> trophyPool; //定义一个对象池
    void Start()
    {
        trophyPool = new ObjectPool<GameObject>(createFunc: () =>
        {   //建立Trophy的对象池trophyPool,类型为<GameObject>类型
            //设置对象池的回调
            var trophy = Instantiate(trophies[Random.Range(0, trophies.Length)], transform);
            trophy.AddComponent<Trophy>().destroyEvent.AddListener(call: () =>
            {   //监听Trophy脚本的destroyEvent事件
                trophyPool.Release(trophy);  //不销毁,而是在对象池中回收
            });
            return trophy;
        },
            actionOnGet: (go) =>
            {  //调用时激活
                go.SetActive(true);
                go.transform.localPosition = Random.insideUnitSphere;
            },
            actionOnRelease: (go) =>
            {  //失活
                go.SetActive(false);
            },
            actionOnDestroy: (go) =>
            {  //销毁
                Destroy(go);
            });
    }

  这个λ表达式比较长,主要是因为对象池的声明就很长,包括了建立对象池、调用、释放、销毁、检查内容、初始容量、最大容量这些参数:

public ObjectPool(Func<T> createFunc, Action<T> actionOnGet = null, Action<T> actionOnRelease = null, Action<T> actionOnDestroy = null, bool collectionCheck = true, int defaultCapacity = 10, int maxSize = 10000)

  6. 是否使用对象池的比较

  如果在PC端运行,上面两段代码的区别并不能直观感受出来,可以打开Profiler查看一下资源占用的情况,下面左图是不使用对象池,右图使用了对象池,可以看到GC垃圾回收数量右边明显少于左边。

  

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Unity Mega-Fiers是一款强大的Unity插件,它为开发者提供了许多功能和工具,以帮助他们创建优秀的游戏体验。 首先,Unity Mega-Fiers具有强大的形变功能。开发者可以通过该插件实现各种形状的变换,包括弯曲、挤压、拉伸等。这为游戏中的人物、物体和环境提供了更多的自由度和个性化选项,使其更具动态和真实感。 其次,Unity Mega-Fiers还提供了高度的粒子系统控制能力。开发者可以使用该插件来创建更加逼真的粒子效果,并具有更准确的控制。这包括粒子的大小、颜色、速度等方面,使游戏中的特效更加出色。 另外,Unity Mega-Fiers还支持可编程网格。这意味着开发者可以通过脚本来控制网格的生成和变形,从而实现各种复杂的效果。无论是地形生成、水体模拟还是其他物体的变形,都可以通过该插件实现,并使游戏更加逼真和具有个性化。 除了以上功能,Unity Mega-Fiers还提供了其他诸多辅助工具,如动画控制、特效编辑等,为开发者提供了更丰富的开发选项和提升游戏质量的手段。 总结而言,Unity Mega-Fiers是一款功能强大的Unity插件,为开发者提供了丰富的形变、粒子系统控制和可编程网格等功能。它能够大幅提升游戏的真实感和品质,并为开发者提供更多的创作空间和个性化选项。无论是初学者还是有经验的开发者,都能够从中受益,并创造出出色的游戏作品。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值