❤️UNITY Actual Combat Advanced-ObjectPool Object Pool-4

  • foreword

        When making a player, we need to load network images, we often use UnityWebRequest to load

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();

Many students often just use it, regardless of recycling, and load all the loaded pictures into the memory, so that the memory usage will become larger and larger, resulting in serious game lag. We need to introduce the concept of object pool.


  • What is object pool 

        The object pool contains several instances prepared in advance, which are extracted from the object pool when needed, and put back into the object pool when not needed.
        Using the object pool does not need to generate and destroy instance objects frequently. If there are not enough instances in the object pool, the program will continue to generate instances, which greatly saves performance.
        Take a chestnut:
        I have a plane, I have a bullet, a~, plane fired bullets

        The first method of launching bullets from an aircraft
        is to instantiate the bullets continuously, and destroy the bullets when the conditions are met.
        Method 2, firstly create N bullets and put them in the container, take out the unused bullets when used, set the used bullets as unused and put them into the container at the same time, if all the bullets in the container are used, instantiate one Put it in a new container and use it.
        Method 3, according to method 2, add an object expiration time and object upper limit, if the object is not used for more than the expiration time, destroy the object, so as to ensure the memory size.


  •  Use of object pools

        Here, put the Sprite image loaded on the network into the object pool for use, save it locally
        and set the upper limit of the pool object, the expiration time of the pool object, and the priority of the pool object.
        

 (1). Get the pictures in the local storage path

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 ; //Save the name to determine whether there is this picture locally

(2). Create GF's ObjectPool

1. The pool object needs to inherit from the ObjectBase base class

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);
    }
}

For the base class of the object, please refer to GameFramework.ObjectPool.ObjectBase

2. Create an object pool

private IObjectPool<SpriteItemObject> m_SpriteObjectPool = null;

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

Just a simple sentence. The object pool is created. For
detailed code, please refer to UnityGameFramework.Runtime.ObjectPoolComponent.
There are 2 types of pool creation:
        1). Pool objects can be acquired multiple times. CreateMultiSpawnObjectPool
        2). Pool objects can only be created single To get CreateSingleSpawnObjectPool
object pool Manager, please refer to GameFramework.ObjectPool.ObjectPoolManager

(3). Destruction of the object pool

Remember to destroy the created object pool in the release function of your management class

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

(4). Get the Sprite object in the object pool

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).Recycle the Sprite object in the object pool

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

(6). Combined use and recycling of 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();
    }
}

  • Effect

 Note the dynamically loaded picture in the lower left corner

 View all sprite objects in the object pool

1. Name 2. Whether it is locked or not 3. Whether it is in use 4. Priority 5. The timestamp of the last use on this object

If the third one is false, it means that the object has started to count down to release, and it will be automatically destroyed if it is not used during the period

3 sprites are in use, 1. User avatar 2. Playlist avatar 3. Current song avatar
1 sprite can be released

The object base class of the object pool of GF greater than this version inherits the object base class of the reference pool , so there are also great changes in use

    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);
        }
    }

 Interested friends can pay attention to a wave

 o(* ̄▽ ̄*)bu

Guess you like

Origin blog.csdn.net/flj135792468/article/details/120363721