❤️UNITY实战进阶-ObjectPool 对象池-4

  • 前言

        在制作播放器得时候我们需要加载网络图片,我们经常使用UnityWebRequest去加载

UnityWebRequest www = new UnityWebRequest(networkPath);
DownloadHandlerTexture download = new DownloadHandlerTexture(true);
www.downloadHandler = download;
yield return www.SendWebRequest();
while (!www.isDone || www.isNetworkError)
    yield return new WaitForEndOfFrame();
if (img != null)
{
    if (download.texture != null)
    {
        img.sprite = (Sprite.Create(download.texture, new Rect(0, 0, download.texture.width, download.texture.height), new Vector2(0.5f, 0.5f)));
    }
    else
    {
        img.sprite = null;
    }
}
www.Dispose();

很多同学经常只管使用,不管回收,加载出来得图片全部加载到内存中,这样内存占用会越来越大,导致游戏卡顿严重。我们就需要引出对象池概念。


  • 什么是对象池 

        对象池中包含若干提前准备好的若干实例,当需要时从对象池中提取,当不需要时,则重新放入对象池。
        使用对象池不需要频繁的产生和销毁实例对象,对象池中的实例如果不够程序调用才会继续产生实例,这大大节省了性能。
        举个栗子:
        I have a plane,I have a bullet, a~, plane fired bullets

        飞机发射子弹
        做法一,不停得实例化子弹,达到条件则销毁子弹。
        做法二,优先创建N个子弹放入容器中,使用得时候取出没有使用的子弹,使用完的子弹设置为未使用状态同时放入到容器中,如果容器中的都使用了,则实例化一个新得放入容器中使用。
        做法三,依据做法二的方式,增加一个对象过期时间和对象上限,如果对象没使用的时间超过了过期时间则销毁对象,以此保证内存大小。


  •  对象池的使用

        这里对加载网络的Sprite图片放入到对象池中使用,并保存到本地
        并设置池对象上限,池对象过期时间,池对象优先级。
        

 (1).获取本地存储路径中的图片

private string FolderPath
{
    get
    {
        return Path.Combine(Application.persistentDataPath, "YourFileName");
        //return string.Format("{0}/{1}", Application.persistentDataPath, "YourFileName");
    }
}

public FileInfo[] GetFolderChildsPath(string folderPath, string pngSuffix, string jpgSuffix)
{
    if (!Directory.Exists(folderPath))
    {
        Directory.CreateDirectory(folderPath);
    }
    DirectoryInfo di = new DirectoryInfo(folderPath);
    FileInfo[] files = di.GetFiles();
    string fileName;
    List<FileInfo> list = new List<FileInfo>();
    for (int i = 0; i < files.Length; i++)
    {
        fileName = files[i].Name.ToLower();
        if (fileName.EndsWith(pngSuffix) || fileName.EndsWith(jpgSuffix))
        {
            list.Add(files[i]);
        }
    }
    return list.ToArray();
}

//初始化获取
public void FileRecv()
{
    //存名字用于判断本地是否有这张图片
    m_PersistentAllName.Clear();
    Log.Info(FolderPath);
    FileInfo[] completePath = GetFolderChildsPath(FolderPath, PNGFileSuffix, JPGFileSuffix);
    for (int i = 0; i < completePath.Length; i++)
    {
        //Debug.LogWarning("图片完整路径: " + completePath[i].Name);
        m_PersistentAllName.Add(completePath[i].Name);
    }
}

m_PersistentAllName ; //存名字用于判断本地是否有这张图片

(2).创建GF的ObjectPool

1.池对象需继承于ObjectBase基类

public class SpriteItemObject : ObjectBase
{
    public static SpriteItemObject Create(string name, object target)
    {
        SpriteItemObject hpBarItemObject = new SpriteItemObject(name, target);
        return hpBarItemObject;
    }

    public SpriteItemObject(string name, object target) : base(name, target)
    {
    }

    protected override void Release(bool isShutdown)
    {
        Sprite sprite = (Sprite)Target;
        if (sprite == null)
        {
            return;
        }
        Object.Destroy(sprite);
    }
}

对象的基类请查看GameFramework.ObjectPool.ObjectBase

2.创建对象池

private IObjectPool<SpriteItemObject> m_SpriteObjectPool = null;

m_SpriteObjectPool = GameEntry.ObjectPool.CreateSingleSpawnObjectPool<SpriteItemObject>(
    "NetworkSprites", 
    m_InstancePoolCapacity,
    m_InstancePoolExpireTime, 
    m_InstancePoolPriority);

简简单单的一句话就吧对象池创建好了
详细代码请查看UnityGameFramework.Runtime.ObjectPoolComponent
创建池分2种:
        1).池类对象能多次被获取 CreateMultiSpawnObjectPool
        2).池类对象只能被单次获取 CreateSingleSpawnObjectPool
对象池Manager请查看GameFramework.ObjectPool.ObjectPoolManager

(3).对象池的销毁

记得在你的管理类的释放函数种销毁创建的对象池

 if (m_SpriteObjectPool != null)
 {
     GameEntry.ObjectPool.DestroyObjectPool(m_SpriteObjectPool);
     m_SpriteObjectPool = null;
 }

(4).获取对象池中的Sprite对象

private Sprite CreateSprite(string fullname, string filename, Texture2D download = null)
{
    //从对象池中取出一个对象
    SpriteItemObject spriteItemObject = m_SpriteObjectPool.Spawn(filename);
    Sprite sprite;
    //对象是否为空
    if (spriteItemObject != null)
    {
        //对象不为空直接获取
        sprite = (Sprite)spriteItemObject.Target;
    }
    else
    {
        //对象为空,并且没有下载的图片数据,直接从本地加载
        if (download == null)
            download = TextureUtility.LoadTexture(fullname);
        //创建sprite
        sprite = (Sprite.Create(download, new Rect(0, 0, download.width, download.height), new Vector2(0.5f, 0.5f)));
        //sprite放入到SpriteItemObject.Target中存入m_SpriteObjectPool,并设置该对象已被获取
        m_SpriteObjectPool.Register(SpriteItemObject.Create(filename, sprite), true);
    }
    return sprite;
}

(5).回收对象池内的Sprite对象

if (m_SpriteObjectPool == null) 
    return;
m_SpriteObjectPool.Unspawn(target);

(6).结合LoadNetworkImage使用与回收

public IEnumerator LoadSprites(string networkPath, Image img, string appointName)
{
    string fileName;
    //指定一个名字
    if (string.IsNullOrEmpty(appointName))
    {
        fileName = networkPath.Substring(networkPath.LastIndexOf('/') + 1);
    }
    else
    {
        fileName = ReplaceName(appointName, "/", "_"); ;
    }
    //本地存在这个名字 
    if (FindName(fileName))
    {
        Sprite sprite;
        //从本地加载图片,塞入到对象池中
        yield return sprite = CreateSprite(Path.Combine(FolderPath, fileName), fileName, null);
        if (img)
        {
            //回收之前img上引用的sprite对象
            UnspawnReference(img.sprite);
            //重新赋值
            img.sprite = sprite;
        }
    }
    //下载保存本地
    else
    {
        if (string.IsNullOrEmpty(networkPath))
        {
            if (img != null)
            {
                UnspawnReference(img.sprite);
                img.sprite = null;
            }
            yield break;
        }
        UnityWebRequest www = new UnityWebRequest(networkPath);
        DownloadHandlerTexture download = new DownloadHandlerTexture(true);
        www.downloadHandler = download;
        yield return www.SendWebRequest();
        while (!www.isDone || www.isNetworkError)
            yield return new WaitForEndOfFrame();
        if (img != null)
        {
            if (download.texture != null)
            {
                //回收之前img上引用的sprite对象
                UnspawnReference(img.sprite);
                string full_path = Path.Combine(FolderPath, fileName);
                //获取的网络图片塞到对象池中
                yield return img.sprite = CreateSprite(full_path, fileName, download.texture);
                //保存到本地
                CreatePNG(full_path, download.data, fileName);
            }
            else
            {
                img = null;
            }
        }
        www.Dispose();
    }
}

  • 效果

 注意左下角的动态加载的图片

 查看对象池中的所有sprite对象

1.名称 2.是否加锁 3.是否正在使用 4.优先级 5.该对象上上次使用的时间戳

第三个如果是false的话,代表该对象已经开始做释放倒计时, 如果在期间没有使用将会自动销毁

3个sprite在使用,1.用户头像 2.歌单头像 3.当前歌曲的头像
1个sprite可以释放

GF大于此版本的对象池的对象基类继承了引用池的对象基类,所以在使用上也有很大的改变

    public class SpriteItemObject : ObjectBase
    {
        public static SpriteItemObject Create(object target)
        {
            SpriteItemObject spriteObject = ReferencePool.Acquire<SpriteItemObject>();
            spriteObject.Initialize(target);
            return spriteObject;
        }

        protected override void Release(bool isShutdown)
        {
           Sprite sprite = (Sprite)Target;
            if (sprite == null)
            {
                return;
            }
            Object.Destroy(sprite);
        }
    }

 有兴趣的小伙伴可以关注一波

 o(* ̄▽ ̄*)ブ

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jerrt-J

希望我创作能给你带来有用的帮助

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值