Unity开发——对象池的简单使用与示例

一、对象池的简介

Unity 对象池是一种优化技术,用于管理和重复使用游戏中的对象,以减少资源的创建和销毁开销,从而提高性能。在游戏开发中,经常需要创建和销毁大量的对象,比如子弹、敌人、特效等。使用对象池可以避免频繁地创建和销毁这些对象,而是将它们事先创建好并存放在一个池中,在需要的时候直接从池中取出并重复使用。

对象池的实现通常包括以下几个步骤:

  1. 创建对象池:在游戏启动时,创建一个对象池,并初始化一定数量的对象放入池中。
  2. 从对象池中获取对象:当游戏需要使用某个对象时,可以从对象池中获取一个可用的对象。
  3. 使用对象:使用获取到的对象进行相应的操作,比如设置位置、旋转、播放动画等。
  4. 对象回收:当不再需要使用该对象时,将其重新放回对象池中以供后续使用。

通过使用对象池,可以减少频繁的内存分配和垃圾回收,提高游戏的性能和流畅度。同时,对象池也可以控制对象的数量,避免过多的对象同时存在导致内存占用过高。在实际开发中,可以根据游戏的需求和性能优化的要求合理地使用对象池技术 。

二、对象池的基本构造

首先我们先用一种简单的写法实现一个对象池功能:

using UnityEngine;
using System.Collections.Generic;

public class ObjectPool : MonoBehaviour
{
    public GameObject prefab;
    public int poolSize;
    
    private List<GameObject> objectPool;

    private void Start()
    {
        // 创建对象池
        objectPool = new List<GameObject>();
        for (int i = 0; i < poolSize; i++)
        {
            GameObject obj = Instantiate(prefab);
            obj.SetActive(false);
            objectPool.Add(obj);
        }
    }

    public GameObject GetObject()
    {
        // 从对象池中获取可用对象
        foreach (GameObject obj in objectPool)
        {
            if (!obj.activeInHierarchy)
            {
                obj.SetActive(true);
                return obj;
            }
        }

        // 如果池中没有可用对象,则动态创建一个新对象
        GameObject newObj = Instantiate(prefab);
        objectPool.Add(newObj);
        return newObj;
    }

    public void ReleaseObject(GameObject obj)
    {
        // 重置对象状态并返回到对象池
        obj.SetActive(false);
        obj.transform.position = Vector3.zero;
    }
}

上述我们用了List实现了对象池的操作,但是我们可以发现在我们使用对象池中的元素的时候我们需要进行一个遍历,判断对象池中的元素是否在被使用,当发现有没有被使用的元素时候就进行启用并返回。

但如果当我们的对象池存储的数据量比较大的时候,我们又需要频繁遍历这时候我们的效率非常的底,比如:发射子弹,生成敌人等等...

那么我们接下来再看这段代码:

using UnityEngine;
using System.Collections.Generic;

public class ObjectPool : MonoBehaviour
{
    public GameObject prefab;
    public int poolSize;

    private Queue<GameObject> objectPool;

    private void Start()
    {
        // 创建对象池
        objectPool = new Queue<GameObject>();
        for (int i = 0; i < poolSize; i++)
        {
            GameObject obj = Instantiate(prefab);
            obj.SetActive(false);
            objectPool.Enqueue(obj);
        }
    }

    public GameObject GetObject()
    {
        // 从对象池中查询是否存在未引用对象
        if(objectPool.Count == 0)
        {
            // 如果池中没有可用对象,则动态创建一个新对象
            GameObject newObj = Instantiate(prefab);
            objectPool.Enqueue(newObj);
            return newObj;
        }

        // 有直接出队进行返回 
        return objectPool.Dequeue();
    }

    public void ReleaseObject(GameObject obj)
    {
        // 重置对象状态并返回到对象池
        obj.SetActive(false);
        obj.transform.position = Vector3.zero;
        objectPool.Enqueue(obj);
    }
}

这段代码是将List换成了Queue,我们可以不再需要每次进行调用元素的时候都需要遍历查找是否存在未引用对象了。

不光是Queue,Stack也是一样的,只是将上面代码中的出队和入队的函数换成我们的入栈和出栈即可,道理都是一样的。

三、运用对象池实现的简单示例

接下来我们实现一个简单的示例:

我们实现一下飙血的效果,而飙血用到的Text我们就会从对象池中进行存取

首先我们先实现点击屏幕让Cube进行位置的变换

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ControlCubePos : MonoBehaviour
{
    private GameObject cube_Prefab;//Cube
    private Camera camera_Main;//相机
    private Vector3 cur_PointPos;//点击的位置

    void Start()
    {
        cube_Prefab = GameObject.Find("Cube");
        camera_Main = Camera.main;
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //点击刷新位置
            cur_PointPos = camera_Main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10));
            cube_Prefab.transform.position = cur_PointPos;
        }
    }
}

现在我们实现了鼠标点击跟换Cube的位置

鼠标点击跟换Cube的位置

这个功能只是一个小插曲,接下来要用对象池实现飘血效果,接下来使用的对象池是将对象池的基本构造中所用的示例进行了一点点的改动,首先将它继承了一个单例,之后将我们的队列改成了栈的形式,又将GameObject的类型改成了Hp的类型(Hp是自定义的脚本)。

 //飘血功能
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Hp text_Hp = ObjectPool.Instance.GetObject();
            text_Hp.gameObject.SetActive(true);
            text_Hp.InitPos(camera_Main.WorldToScreenPoint(cube_Prefab.transform.position));
        }

这是一段在我们ControlCubePos 类中的Update函数新填的一段功能,它先从对象池中拿取一个未被引用的元素并进行启用,之后调用了Hp自定义类中的InitPos的函数,它是进行位置数据初始化的。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Hp : MonoBehaviour
{
    private Vector2 m_StartPos;//初始位置
    private Vector2 m_TargetPos;//目标位置
    private bool action_IsOn;//开始运动 ---- 飘血

    public RectTransform rect;

    /// <summary>
    /// 位置初始化
    /// </summary>
    /// <param name="_pos">Cube的位置</param>
    public void InitPos(Vector2 _pos)
    {
        rect = transform as RectTransform;
        //设置锚点位置
        rect.anchorMin = Vector2.zero;
        rect.anchorMax = Vector2.zero;
        //赋值初始化位置
        rect.anchoredPosition = _pos;
        m_TargetPos = _pos + new Vector2(50, 100);
        action_IsOn = true;
    }

    // Update is called once per frame
    void Update()
    {
        if (action_IsOn)
        {
            //text移动
            rect.anchoredPosition = Vector2.Lerp(rect.anchoredPosition, m_TargetPos, Time.deltaTime * 3);
            if (Vector2.Distance(rect.anchoredPosition, m_TargetPos) <= 10)
            {
                ObjectPool.Instance.ReleaseObject(this);
                action_IsOn = false;
            }
        }
    }
}

在InitPos函数中改变了它的当前状态,使他可以进行往上飘的动作,当到达位置之后,调用ReleaseObject方法再放回我们的对象池当中。

接下来我们可以看一下整体的效果

飘血整体效果

这样一个非常简单的对象池小示例就完成了!!!

  对于对象池的时候,在平时开发的过程当中运用还是非常广泛的,在这里我们只是简单的展示了一下对象池的基本原理,如果还想更加了解对象池的运用,可以在网上下载一些具体的案例,以便更加具体的了解对象池。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Unity中,可以使用对象池(Object Pool)来缓存一些需要频繁创建和销毁的对象,以便在需要时直接从对象池中获取,避免频繁创建和销毁对象所带来的性能开销。下面我们来讲解如何使用对象池。 ### 手动创建对象池 手动创建对象池的步骤如下: 1. 创建一个GameObject对象作为对象池的容器,将需要缓存的对象挂在该容器下。 2. 编写一个对象池类来实现对象的缓存和取出。以下是一个简单对象池示例: ```csharp using System.Collections.Generic; using UnityEngine; public class ObjectPool : MonoBehaviour { public GameObject prefab; // 缓存的对象的预制体 public int poolSize; // 对象池大小 private List<GameObject> objectPool = new List<GameObject>(); // 对象池列表 private void Start() { // 创建对象池中的对象 for (int i = 0; i < poolSize; i++) { GameObject obj = Instantiate(prefab); obj.SetActive(false); objectPool.Add(obj); } } // 从对象池中取出对象 public GameObject GetObject() { foreach (GameObject obj in objectPool) { if (!obj.activeInHierarchy) { obj.SetActive(true); return obj; } } // 如果对象池中没有可用对象,则新建一个对象 GameObject newObj = Instantiate(prefab); objectPool.Add(newObj); return newObj; } // 将对象放回对象池 public void ReleaseObject(GameObject obj) { obj.SetActive(false); } } ``` 3. 在需要使用对象的地方,通过对象池类的GetObject方法获取一个对象,使用完后通过ReleaseObject方法将对象放回对象池即可。 ### 使用Unity提供的对象池Unity提供了ObjectPool类来支持对象池的创建和使用使用Unity提供的对象池类的步骤如下: 1. 创建一个GameObject对象作为对象池的容器,将需要缓存的对象挂在该容器下。 2. 编写脚本,在Start方法中初始化对象池。以下是一个简单示例: ```csharp using UnityEngine; using UnityEngine.Pool; public class ObjectPoolExample : MonoBehaviour { public GameObject prefab; // 缓存的对象的预制体 public int poolSize; // 对象池大小 private ObjectPool<GameObject> objectPool; // 对象池 private void Start() { objectPool = new ObjectPool<GameObject>( () => Instantiate(prefab), null, obj => obj.SetActive(true), obj => obj.SetActive(false), null, poolSize); } // 在需要使用对象的地方,通过GetObject方法获取一个对象,使用完后通过Release方法将对象放回对象池即可。 private void Update() { GameObject obj = objectPool.Get(); objectPool.Release(obj); } } ``` 以上是手动创建对象池使用Unity提供的对象池类的两种方式。两种方式都可以有效地减少对象创建和销毁的次数,提高性能并减少内存占用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值