zoukankan      html  css  js  c++  java
  • Unity日记—对象缓存池

    最近都在忙别的事了,今天忙里偷闲了解了一下对象池是啥玩意,简单记录一下。

    还是个正在学习的萌新,如果写的不好请见谅。

    对象池是啥

    在了解对象池之后,我才意识到以前写的代码有多么蠢,当场景中有一些重复的需要生成和销毁的物体时(比如地上可拾取的金币),我们常常调用Instantiate和Destroy函数去实现,这造成了大量的性能开销,尤其是当游戏物体挂载脚本时,脚本中的Awake()、OnEnable()、OnDestroy()等方法不断被调用,对性能造成了很大的负担,所以在复用性强的游戏中缓存池是十分重要的。

    对象池的原理就是我们把需要复用的物体存在池子里,需要“销毁物体”时,通过SetActive方法暂时让物体隐藏而不是真的销毁,需要“生成物体”时,先看看池子里有没有之前隐藏的物体,如果有就直接将其激活,没有的话再去生成物体,这样我们真正需要去生成物体的次数永远是场景中同时存在该物体的最大值,而不是每次使用都去生成和销毁。

    对象池实现

    我在unity中简单实现了一个对象池,用字典的形式存储每种物体对应的池子(也就是List),取出物体时使用一个字符串代表该物体,然后将该物体的预制体放在Resources目录下,注意预制体的名字要和传入的参数相同。

    需要注意的是,我们只是隐藏了物体,而物体的位置、旋转等信息并没有改变,所以当我们下次取出它时,它的数据还和原来一样,这种数据通常称为“脏数据”,所以我们可以让复用的物体都继承一个父类,提供两个抽象方法,让子物体去重写,当把物体取出和放回池子时调用这两个方法去处理数据。

    public abstract class RecyclableObject:MonoBehaviour
    {
        public abstract void OnSpawn();
    
        public abstract void OnUnspawn();
    }

    取出和放回物体时,可以使用SendMessage来调用子类身上的方法处理脏数据,比如调整位置之类的。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class ObjectPool
    {
        //单例
        private static ObjectPool _instance;
        public static ObjectPool Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new ObjectPool();
                }
                return _instance;
            }
        }
    
        public Dictionary<string, List<GameObject>> poolsDict = new Dictionary<string, List<GameObject>>();
    
        //取出物体
        public void Spawn(string name,Transform parent)
        {
            //如果没有这个类型的池子就创建一个
            if(!poolsDict.ContainsKey(name))
            {
                poolsDict.Add(name, new List<GameObject>());
            }
    
            //得到池子
            List<GameObject> ObjList;
            poolsDict.TryGetValue(name, out ObjList);
    
            //在池子里寻找被隐藏的游戏物体
            GameObject go = null;
            foreach(var obj in ObjList)
            {
                if(!obj.activeSelf)
                {
                    go = obj;
                }
            }
    
            if(go==null)//不存在隐藏的游戏物体
            {
                go = Object.Instantiate(Resources.Load<GameObject>(name));
                ObjList.Add(go);
            }
            else//存在隐藏的游戏物体
            {
                go.SetActive(true);
            }
    
            go.transform.parent = parent;
            go.SendMessage("OnSpawn", SendMessageOptions.DontRequireReceiver);
        }
    
        //回收物体
        public void Unspawn(GameObject go)
        {
            foreach(List<GameObject> list in poolsDict.Values)
            {
                if(list.Contains(go)&&go.activeSelf)
                {
                    go.SendMessage("OnUnspawn", SendMessageOptions.DontRequireReceiver);
                    go.SetActive(false);
                }
            }
        }
    
        //销毁池子
        public void ClearPool(string name)
        {
            if(poolsDict.ContainsKey(name))
            {
                foreach(GameObject go in poolsDict[name])
                {
                    Object.Destroy(go);
                }
                poolsDict.Remove(name);
            }
        }
    
    }

    马上要参加计算机设计大赛的省赛和蓝桥杯的国赛,所以暂时没时间学游戏开发了,希望比赛能有个好结果,加油吧。

  • 相关阅读:
    无线路由器的工作模式
    php 利用root 权限执行shell脚本
    shell 终端常用插件
    linux space/mark设置
    推送唯一标识符
    微信支付跨平台软件架构
    celery 动态配置定时任务
    两个报文是如何进行 TCP 分组传输
    接口 Interfaces
    How does Circus stack compare to a classical stack?
  • 原文地址:https://www.cnblogs.com/LiveForGame/p/10612007.html
Copyright © 2011-2022 走看看