Unity3d单例模式与对象池

发表于2018-08-29
评论0 1.5k浏览
单例模式是所有设计模式之中运用最广泛的设计模式之一,而对象池在游戏中相当常用,通过对GameObject的反复利用,能节约宝贵的CPU资源。在本篇文章中将给大家分享下在项目中使用单例模式与对象池的技巧。


单例模式

单例模式,简单说就是类的实例在内存中只存在一份,其中单例模式有2种写法:

首先,是继承自MonoBehaviour的单例,需要使用U3D组件和功能可以用这种单例。理解U3D本身单例写法的机制就知道为啥要这么写了,第一个挂载脚本的对象,就是该单例,后面再怎么重复挂载是无效的。因为挂载的时候就实例化该类了。
    public static ObjectPool instance;   //单例
    //U3D的单例机制,是第一个挂载脚本的对象,就是该单例,后面再怎么重复挂载是无效的。因为挂载的时候就实例化instance了。
    void Awake()
    {
        instance = this;
    }

其次,不继承MonoBehaviour的单例,全局单例吧,什么场景都能用,不能挂载到U3D物体上。
    private static ObjectPool instance;   //单例
    public ObjectPool GetInstance()
    {
        if (instance == null) 
        {
            instance = new ObjectPool();
        }
        return instance;
    }  

对象池

思路:重复创建大量物理和销毁物体,会大量消耗资源,比如:子弹,金币等等,对象池的作用就是创建完了不销毁,只能把它隐藏存入对象池,用一个列表保存数据,需要用的时候再取出来,同时激活它,并且移除列表,这样列表中剩下的就是隐藏可以使用的对象。因为可能有很多物体需要用对象池,所以把个物体的LIST列表存入一个字典。如果对象池中有100个对象,创建90个都是在对象池中激活,只有创建110,才会再新生成10个。
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//U3D单例对象池
public  class ObjectPool : MonoBehaviour
{
    public static ObjectPool instance;   //单例
    public GameObject[] prefabObjects;   //prefab数组
    private Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>();  //对象池字典
    //U3D的单例机制,是第一个挂载脚本的对象,就是该单例,后面再怎么重复挂载是无效的。因为挂载的时候就实例化instance了。
    void Awake()
    {
        instance = this; 
    }
    //从池中获取      //正式项目最好用一个静态类来保存名字,防止出错
    public GameObject GetOut(string GameObjectName, Vector3 vector3)
    {
        GameObject gameObject;   //返回的gameObject
        //如果池中有
        if (pool.ContainsKey(GameObjectName) && pool[GameObjectName].Count > 0)
        {
            //取池里的用
            gameObject = pool[GameObjectName][0];
            gameObject.SetActive(true);
            gameObject.transform.position = vector3;
            //取完移除
            pool[GameObjectName].RemoveAt(0);
        }
       //如果没有
        else
        {
            GameObject prefabObject = null;
            //要生成的prefabObject   prefab数组中的物体名字要和传入的字符串一致  
            for (int i = 0; i < prefabObjects.Length; i++)
            {
                if (prefabObjects[i].name == GameObjectName)
                {
                    prefabObject = prefabObjects[i];
                }
            }
            //直接创建
            gameObject = (GameObject)GameObject.Instantiate(prefabObject, vector3, Quaternion.identity);
        }
        return gameObject;
    }
    //存入对象池
    public void SetIn(string GameObjectName,GameObject gameObject) 
    {
      //池中没有
      if (!pool.ContainsKey(GameObjectName))
      {   //新建池List
          pool.Add(GameObjectName, new List<GameObject>());
      }
      //存入池
      gameObject.SetActive(false);
      pool[GameObjectName].Add(gameObject);
      Debug.Log(pool[GameObjectName].Count);
    }
    //销毁对象池
    public void DestroyPool(string GameObjectName)
    {
        if (pool.ContainsKey(GameObjectName))
        {   //删除对象
            for (int i = 0; i< pool[GameObjectName].Count; i++) 
            {
                Destroy(pool[GameObjectName][i]);
            }
            //移除列表
            pool.Remove(GameObjectName);
        }
    }
} 
注意:使用的时候,从对象池获取,什么地方都可以用,存入对象池这个方法最好在物体本身上,比如子弹上挂着一个,自己就把自身存入对象池。实际使用就用PoolManager插件好了,据说很好用。来自:https://blog.csdn.net/u012322710/article/details/53224337

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引