unity对象池的使用

对象池的用法和意义

为什么要用对象池,它有什么用呢?它一般用在哪些方面呢?比如,你要制作一个射击类游戏,射击肯定要经常发射子弹吧,如果你不使用对象池,那么你每发射一颗子弹就要实例化一颗子弹,之后还要Destroy销毁掉,不断的实例化和销毁,是非常消耗性能的,小游戏还好,如果是比较大点的游戏,很容易造成卡顿现象,这样玩家肯定就不乐意玩了。又比如跑酷游戏,游戏中会反复的生成金币和障碍物和道具,如果也是反复的生成销毁的话,性能非常差。所以就引入了对象池。

对象池的实现

无非就是,游戏物体在用的时候就设为显示的,即SetActive(true),用不到的时候就SetActive(false),当要使用的false的游戏物体用完了,就再实例化出来,没用完的时候,将false的游戏物体设为true即可。

当游戏中有许多的物品需要重复的实例化和销毁时,如金币、小车、栅栏、磁铁等,这个时候我们可以先建一个子池子,不同类型的东西分别在不同的子池子里,最后由一个大池子统一管理。

子池子的实现

在实现子池子之前,先写个接口,C#中接口可以多继承,而类只能单继承,所以使用接口,接口名一般由大写的I开头。代码如下:

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

public interface IReusable
{
    //取出的时候调用
    void OnSpawn();

     //回收的时候调用
    void OnUnSpawn();
}

接口写完后,再写个类,让这个类同时继承MonoBehaviour和 IReusable。
之后在对象池中只需要继承这个新写的类就可以了,相当于同时继承了MonoBehaviour和 IReusable。新的类代码如下:

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

public abstract class ReusableObject : MonoBehaviour, IReusable
{
    public abstract void OnSpawn();

    public abstract void OnUnSpawn();
}

为什么要先写这个接口和这个类呢,因为下面的子池子中会用到。
就是go.SendMessage(“OnSpawn”,SendMessageOptions.DontRequireReceiver);
和go.SendMessage(“OnUnSpawn”,SendMessageOptions.DontRequireReceiver);
将游戏物体设为true时,发送消息让该游戏物体执行OnSpawn()的方法,可以在OnSpawn中设置生成的游戏物体的位置和大小之类的。

将游戏物体设为false时,发送消息让该游戏物体执行OnUnSpawn()的方法,可以在OnUnSpawn中将变换过的游戏物体恢复成原来的模样,如果没有变换过,那就在OnUnSpawn中留空就好了。
子池子的代码如下:

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

public class SubPool
{
	//这个是负责保存子池子中的游戏物体
    private List<GameObject> m_objects = new List<GameObject>();

    private GameObject m_prefab;

    public string Name { get { return m_prefab.name; } }//每个子池子都有一个名字标识

    private Transform m_parent;

    public SubPool(Transform parent,GameObject go)
    {
        m_parent = parent;
        m_prefab = go;
    }

    /// <summary>
    /// 取出物体
    /// </summary>
    /// <returns></returns>
    public GameObject Spawn()
    {
        GameObject go = null;
        foreach (var obj in m_objects)
        {
            if(!obj.activeSelf)		//!obj.activeSelf代表游戏物体是false的;
            {
                go = obj;
                break;
            }
        }
        if(go==null)
        {
            go = GameObject.Instantiate<GameObject>(m_prefab);
            go.transform.parent = m_parent;
            m_objects.Add(go);
        }
        go.SetActive(true);
        go.SendMessage("OnSpawn", SendMessageOptions.DontRequireReceiver);
        return go;
    }

    /// <summary>
    /// 回收一个游戏物体
    /// </summary>
    /// <param name="go"></param>
    public void UnSpawn(GameObject go)
    {
        if(Contain(go))
        {
            go.SendMessage("OnUnSpawn", SendMessageOptions.DontRequireReceiver);
            go.SetActive(false);
        }
    }

    /// <summary>
    /// 回收所有游戏物体
    /// </summary>
    public void UnSpawnAll()
    {
        foreach (var obj in m_objects)
        {
            if(obj.activeSelf)
            {
                UnSpawn(obj);
            }
        }
    }

    /// <summary>
    /// 判断是否属于list里面,,因为m_objects是私有的,所以在大池子中访问不到,无法判断小池子中是否包含有传进来的游戏物体,所以这里提供一个供外部访问的方法。
    /// </summary>
    /// <returns></returns>
    public bool Contain(GameObject go)
    {
        return m_objects.Contains(go);
    }
}

小池子建完了以后,就需要建一个大池子来统一管理这些小池子了。
大池子的代码如下:

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

public class ObjectPool : MonoSingleton<ObjectPool>
{
    /// <summary>
    /// 资源目录,就是需要实例化的那些游戏物体所在的路径
    /// </summary>
    public string ResourceDir = "";
	//游戏物体的名字对应相应的子池子
    private Dictionary<string, SubPool> m_pools = new Dictionary<string, SubPool>();
	
    public GameObject Spawn(string name,Transform trans)
    {
        SubPool pool = null;
        if(!m_pools.ContainsKey(name))
        {
            RegisteNew(name,trans);
        }
        pool = m_pools[name];
        return pool.Spawn();
    }

    /// <summary>
    /// 新建一个池子
    /// </summary>
    private void RegisteNew(string names,Transform trans)
    {
        string path = ResourceDir + "/" + names;
        GameObject go = Resources.Load<GameObject>(path);
        SubPool pool = new SubPool(trans, go);
        m_pools.Add(pool.Name, pool);
    }

    /// <summary>
    /// 清除所有
    /// </summary>
    public void Clear()
    {
        m_pools.Clear();
    }

    /// <summary>
    /// 回收物体
    /// </summary>
    /// <param name="go"></param>
    public void UnSpawn(GameObject go)
    {
        SubPool pool = null;
        foreach (var p in m_pools.Values)
        {
            if(p.Contain(go))
            {
                pool = p;
                break;
            }
        }
        pool.UnSpawn(go);
    }

    //回收所有物体
    public void UnSpawnAll()
    {
        foreach (var p in m_pools.Values)
        {
            p.UnSpawnAll();
        }
    }
}

大池子继承自 MonoSingleton< ObjectPool>,就是将自身变成一个单例,单例的内容请移步https://blog.csdn.net/weixin_43839583/article/details/103405296

因为大池子只有一个,并且需要频繁的使用,所以使用到单例。

对象池的使用就到这里了,性能非常的高效。

我对象池已经建好了,那么我的对象在哪啊,国家怎么还没给我分配对象。

发布了15 篇原创文章 · 获赞 0 · 访问量 258

猜你喜欢

转载自blog.csdn.net/weixin_43839583/article/details/103405831
今日推荐