首页 > 程序开发 > 软件开发 > 其他 >

Unity3D游戏架构设计之对象管理【三】

2017-03-22

Unity3D游戏架构设计之对象管理【三】:一种对象池实现。因为内部采用Queue存储对象。

Unity3D游戏架构设计之对象管理【三】:一种对象池实现。因为内部采用Queue存储对象。
/// 
/// 
/// 需要实现对象生成器和生成器工厂
/// QObjPool有两个重要的接口
/// T BorrowObj()        借对象
/// ReturnObj(T obj)     还对象
/// 之所以叫借和还,意思就是不能拿着T去做T的删除操作!!!
/// 
/// 对象生成器
/// public abstract class QObjCreator
/// 因为对象池不需要知道对象怎么创建出来的,所以需要外部提供。
/// 
/// 对象生成器工厂
/// public abstract class QObjCreatorFactory
/// 这个不是必须的,但有一种情况下需要提供。
/// 就是生成器生成时,有伴随动作,额外操作,比如生成个文件啥的。
/// 这时候如果仅仅持有对象生成器的指针,就会导致每次生成后的都放这个文件中。
/// 在极端情况下,文件就会爆掉。
/// 
/// 
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// 
/// 对象生成器
/// 
/// 
public abstract class QObjCreator
{
    public virtual T [] CreateObjects()
    {
        T [] objs = null;
        return objs;
    }

    /// 
    /// 隐藏对象
    /// 
    ///
    public virtual void HideObject(T obj)
    {

    }

    /// 
    /// 释放对象
    /// 
    ///
    public virtual void RealseObject(T obj)
    {

    }

    /// 
    /// 释放自己
    /// 
    public virtual void Realse()
    {

    }
}

/// 
/// 对象生成器工厂类,需要派生类实现
/// 
public abstract class QObjCreatorFactory
{
    public virtual QObjCreator CreatCreator()
    {
        QObjCreator creator = null;
        return creator;
    }

    public virtual void Realse()
    {

    }
}

public class QObjPool
{
    private Queue m_pool = new Queue();
    public int FreeCount { get; private set; }
    public int TotleCount { get; private set; }

    private QObjCreatorFactory m_creatorFactory;
    private QObjCreator m_creator;

    public QObjCreator BAK_CREATOR { get; private set; }
    public bool Init(QObjCreator creator, QObjCreatorFactory creatorFactory = null)
    {
        FreeCount        = 0;
        TotleCount       = 0;
        m_creator        = creator;
        m_creatorFactory = creatorFactory;
        return Resize();
    }

    public T BorrowObj()
    {
        bool retCode = false;
        T obj = default(T);
        //Debug.Log("FreeCount" + FreeCount);
        if (FreeCount > 0)
        {
            obj = m_pool.Dequeue();
            FreeCount--;
            return obj;
        }
        
        //if (obj == null)
        {
            retCode = Resize();
            if (!retCode)
            {
                return default(T);
            }
            obj = m_pool.Dequeue();
        }
        FreeCount--;
        
        if (FreeCount <= 0)
        {
            Debug.LogError("本pool出现严重逻辑问题,请联系作者");
        }

        return obj;
    }

    public void ReturnObj(T obj)
    {
        m_pool.Enqueue(obj);
        FreeCount++;
    }
    /// 
    /// 按指定对象个数释放,当leftCout == 0 时表示整个pool的对象都释放
    /// 
    ///
    ///
    public void Realse(int count, out int leftCout)
    {
        if (BAK_CREATOR == null)
        {
            Debug.LogWarning("QObjPool 的备份对象生成器为null");
            leftCout = FreeCount;
            return;
        }

        int realseCount = Mathf.Min(count, FreeCount);
        for (int i = 0; i < realseCount; i++)
        {
            T obj = m_pool.Dequeue();
            BAK_CREATOR.RealseObject(obj);
            FreeCount--;
        }

        leftCout = FreeCount;

        if (FreeCount <= 0)
        {
            m_pool           = null;

            BAK_CREATOR.Realse();
            BAK_CREATOR      = null;

            if (m_creator != null)
            {
                m_creator.Realse();
                m_creator = null;
            }

            if (m_creatorFactory != null)
            {
                m_creatorFactory.Realse();
                m_creatorFactory = null;
            }
        }
    }

    private bool Resize()
    {
        bool result = false;

        //必须没有空闲的才执行
        if (FreeCount > 0)
        {
            return result;
        }

        T [] objs = null;
        objs = CreatObjects(m_creator, m_creatorFactory);
        if (objs == null)
        {
            return result;
        }

        int countForAdd  = objs.Length;
        if ((int.MaxValue - countForAdd <= TotleCount)
            || (int.MaxValue - countForAdd <= FreeCount)
        )
        {
            Debug.LogError("本pool已碎,无力支撑,请检查上层逻辑是否合理");
            return result;
        }

        FreeCount       += countForAdd;
        TotleCount      += countForAdd;
        for (int i = 0; i < countForAdd; i++)
        {
            m_pool.Enqueue(objs[i]);
        }
        
        result = true;
        return result;
    }

    private T [] CreatObjects(QObjCreator creator, QObjCreatorFactory creatorFactory)
    {
        T [] objs = null;

        if (creator != null)
        {
            m_creator   = creator;
            BAK_CREATOR = creator;
            objs        = m_creator.CreateObjects();
        }
        else if (creatorFactory != null)
        {
            QObjCreator _creator = creatorFactory.CreatCreator();
            BAK_CREATOR             = _creator;
            objs                    = _creator.CreateObjects();
        }
        else
        {
            objs = null;
        }

        return objs;
    }
}

1.2 通用GameObject的对象池实现

GameObject的对象池只需要把模板对应到具体的GameObject,并且只需要实现构造器即可。
/// 
/// QObjPool的通用GameObject版本实现
/// 
/// 
/// 
using UnityEngine;
using System.Collections;

public class QObjCreatorForGameObject : QObjCreator
{
    public string m_path;

    public int m_count;

    public static readonly Vector3 INIT_POS = new Vector3(0f, -10f, 0f);

    protected GameObject m_seed;

    private bool isInit = false;

    public QObjCreatorForGameObject(string path, int count)
    {
        m_seed = ResourcesManagerMediator.GetGameObjectFromResourcesManager(path);
        if (m_seed == null)
        {
            Debug.LogWarning("读取种子资源出错 " + path);
            return;
        }

        m_seed.transform.position = INIT_POS;

        m_path  = path;
        m_count = count;
        isInit  = true;
    }

    private QObjCreatorForGameObject() { }

    public override void Realse()
    {
        m_seed = null;
    }

    public override GameObject[] CreateObjects()
    {
        if (!isInit)
        {
            return null;
        }

        GameObject[] objs = new GameObject[m_count];
        for (int i = 0; i < m_count; i++)
        {
            GameObject go = GameObject.Instantiate(m_seed);
            //HideObject(go);
            go.transform.position = INIT_POS;
            objs[i] = go;
        }

        return objs;
    }

    public override void HideObject(GameObject obj)
    {
        obj.transform.position = INIT_POS;
    }

    public override void RealseObject(GameObject obj)
    {

    }

}

1.3 粒子Particle的对象池实现

粒子本身就是GameObject,因此它的实现就更简单了,代码越来越少了。

/// 
/// QObjPool的粒子特效版本
/// 
/// 
/// 
using UnityEngine;
using System.Collections;

public class QObjPoolForParticle : QObjCreatorForGameObject
{
    public QObjPoolForParticle(string path, int count)
        :base(path, count){}

    public override void Realse()
    {
        m_seed = null;
    }

    public override void HideObject(GameObject obj)
    {
        ParticleSystem [] particleSystems = obj.GetComponentsInChildren();
        int length = particleSystems.Length;
        for (int i = 0; i < length; i++)
        {
            particleSystems[i].Stop();
        }
    }

    public override void RealseObject(GameObject obj)
    {

    }

}

2 其他对象的管理

上面是给出了GameObject和粒子的对象管理。在实际的项目中,需要根据实际需求做相应的设计。核心的设计是选择适合的数据结构。
以UI为例,UI很少出现重复的,可能需要的是把之前打开过的UI做个缓存,减少从磁盘读取的次数。这个缓存数量可以根据项目情况自己设定。比如我用一个队列把打开的UI都存起来,最多8个,当出现第9个时,把最早打开那个UI从缓存中移除。最早那个UI就是队列的头。队列的头尾操作的效率你懂的,效率很不错的。
以地图为例,MMO的地图是分块加载的,我们完全可以对每个块编一个索引。对象池的数据结构已map为主,key就是这个索引。角色走到哪里,需要加载的地块的索引是已知的(很多算法可以实现,比如9宫),直接从对象池中按索引取出来就好了。map按key做查询的项目你懂得,效率也是很好的。
好了,前后一共分别写了三篇来讲对象池的管理,希望大家能看懂,并且对大家有帮助。如有疑问或者更好的建议可以给我发邮件或者在下面留言。至于对象管理的其他部分的代码我就不打算给出了。主要原因是在于对象管理的核心是对象池的实现,其他相对简单。再就是其他部分和具体项目结合比较紧密。

当然也可以做的很抽象还实现,但我个人认为没有太大的必要,有过度设计的嫌疑。对了,上面给出的代码,如果你看起来比较困惑,可以适当复习下C#的模板,以及设计模式的“工厂”模式。
相关文章
最新文章
热点推荐