栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > Unity3D

Unity性能优化之内存管理的优化(内存管理性能增强之预制池)四

Unity3D 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity性能优化之内存管理的优化(内存管理性能增强之预制池)四

预制池系统
首先需要知道预制池系统需要做什么:

  1. 如果已经存在已回收的版本,应该重新生成第一个可用的对象
  2. 如果不存在已回收的版本,应该从预制体中实例化新的GameObject
  3. 在上述两种情况下, 都应该在附加到GameObject上的所有IPoolableComponent接口上调用Spawned方法
  4. 必须接受请求,以回收固定GameObject
  5. 如果对象由对象池系统管理,他应该禁用,并在附加到GameObject上的所有IPoolableComponent接口类上调用Despawned方法
  6. 如果对象没有由对象池系统管理,应该报错
    以上的需求简单明了,但是如果希望使解决方案的性能更好,则需要进行一些改进
    首先,对于主要的入口来说,典型的单例是一种非常好的选择,因为单例可以做到在任何地方,全局访问;
    生成的对象的主要任务包括接受一个预制引用,指出是否有回收的GameObject从相同的引用中实例化,为此,对象池系统需要为任何给定的预制体跟踪两种不同类型的列表,一个是已经激活的(实例化的),一个使未激活的(回收的)共两个对象列表,这种数据信息,最好抽象到一个类去管理,将其命名为PrefabPool;
    另一方面,由于生成的Gameobject需要给定一个预制体,我们将通过一个数据结构能够快速的将预制体映射到管理他们的PrefabPool,同时,由于回收对象需要给定一个Game Object,我们将通过另一个数据结构把已经生成的Gameobject映射到最初生成他们的PrefabPool,这样看来我们需要一对字典,分别用来管理生成与回收


接下来需要定义在生成对象发生了什么

接下来需要定义回收对象的时候需要做什么

现在已经有了可以自动处理多个预制池的系统,剩下的工作就是如何定义这个池子的行为,如前所述,Prefab类应该维护两个数据结构,一个用于在给定的Prefab中实例化活动(派生)对象,另一个用于非活动(回收的)对象;
从技术上来讲PrefabPoolingSystem1类已经维护了一个由PrefabPool管理的prefab的映射,所以实际上可以节省一点内存,因此这两个数据结构是PrefabPool需要跟踪的成员变量,但是由于每个GameObejct还必须要维护所有IPoolableComponent引用的列表,以便对他们调用Sapwned和Despawned方法,获取这些引用可能是在运行时执行的一个昂贵操作,所以最好将数据缓存在一个简单的数据结构中;如下

现在可以定义PrefabPool类的成员数据了

生成对象
接下来定义在池系统容器中生成GameObject意味着什么,在某个时刻,PrefabPool会收到一个请求,从给定的预制体中利用给定位置旋转,生成GameObject,首先应该要检查是否有该预制体的非激活实例,如果有,就可以将下一个可用对象移出队列,并重新生成它,如果没有,就需要使用GameObject.Instantiate从预制体中实例化新的GameObject,此时应该创建PoolablePrefabData对象,来保存GameObject引用,并获取附加在它上面,所有实现了IPoolableComponent的MonoBehaviour列表
下面的Spawn定义了这样的行为;

对象的回收

预制池测试

预先生成实例

预制池和场景加载
该系统还有一个重要的警告,由于PrefabPoolingSystem是静态类,它比场景的生命周期更长,这意味着当加载新场景时,池系统的字典尝试维护之前场景中已经池化的实例的引用,但unity在切换场景时强制销毁这些对象,而不管我们依然保有对他们的引用,因此字典将充满空引用,这将为下一个场景带来很多严重的问题,因此,应该在PrefabPoolingSystem中创建一个方法,类似于重置系统 ,并且在新的场景加载前调用,为下一个场景做好准备;

全部代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

public static class PrefabPoolingSystem1
{
    static Dictionary _prefabTopoolMap = new Dictionary();
    static Dictionary _goToPoolMap = new Dictionary();


    public static GameObject Spawn(GameObject prefab,Vector3 position,Quaternion rotation)
    {
        if(!_prefabTopoolMap.ContainsKey(prefab)) //如果缓存中没有包含要生成的对象
        {
            _prefabTopoolMap.Add(prefab, new PrefabPool());
        }
        PrefabPool pool = _prefabTopoolMap[prefab];//获得对象所在的预制池
        GameObject go = pool.Spawn(prefab, position, rotation);//使用预制池生成预制
        _goToPoolMap.Add(go, pool);//将生成的预制体与预制池保存
        return go;
    }

    public  static GameObject Spawn(GameObject prefab) 
    {
        return Spawn(prefab, Vector3.zero, Quaternion.identity);
    }

    public static bool Despawn(GameObject prefab)
    {
        if(!_goToPoolMap.ContainsKey(prefab))
        {
            Debug.LogError(string.Format("Object {0} not managed by pool system!", prefab.name));
            return false;
        }
        PrefabPool pool = _goToPoolMap[prefab];
        if(pool.Despawn(prefab))
        {
            _goToPoolMap.Remove(prefab);
            return true;
        }
        return false;
    }
    public static void Prespawn(GameObject prefab, int numToSpawn)
    {
        List spawnedObjects = new List();

        for (int i = 0; i < numToSpawn; i++)
        {
            spawnedObjects.Add(Spawn(prefab));
        }

        for (int i = 0; i < numToSpawn; i++)
        {
            Despawn(spawnedObjects[i]);
        }

        spawnedObjects.Clear();
    }


    public static void Reset()
    {
        _prefabTopoolMap.Clear();
        _goToPoolMap.Clear();
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

public struct PoolablePrefabData1
{
    public GameObject go;
    public IPoolableComponent[] poolableComponents;
}
class PrefabPool1
{
    Dictionary _activeList = new Dictionary();
    Queue _inactiveList = new Queue();

    public GameObject Spawn(GameObject prefab,Vector3 position,Quaternion rotation)
    {
        PoolablePrefabData1 data;
        if(_inactiveList.Count > 0)//如果存在已经回收的对象
        {
            data = _inactiveList.Dequeue();
        }
        else //如果不存在已经回收的对象
        {
            //那么就需要新实例化一个对象
            GameObject newGo = GameObject.Instantiate(prefab, position, rotation);//实例化
            data = new PoolablePrefabData1();
            data.go = newGo;
            data.poolableComponents = newGo.GetComponents();//需要获取所有IPoolableComponent引用的列表,以便调用Spawned和Despawned
        }
        //设置实例化物体的位置
        data.go.SetActive(true);
        data.go.transform.position = position;
        data.go.transform.rotation = rotation;

        for(int i = 0;i
            data.poolableComponents[i].Spawned();//所有的实现了IPoolableComponent接口的都需要调用Spawned
        }
        _activeList.Add(prefab, data);

        return data.go;
    }

    public bool Despawn(GameObject objToDespawn)
    {
        if(!_activeList.ContainsKey(objToDespawn))//如果将要回收的对象没有在显示列表中,代表出错啦
        {
            Debug.LogError("This Object is not managed by this object pool!");
            return false;
        }

        PoolablePrefabData1 data = _activeList[objToDespawn];
        for(int i = 0;i 
            data.poolableComponents[i].Despawned();
        }

        data.go.SetActive(false);
        _activeList.Remove(objToDespawn); //移除
        _inactiveList.Enqueue(data);//添加
        return true;
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PrefabPoolingTestInput : MonoBehaviour {
    [SerializeField] GameObject _orcPrefab;
    [SerializeField] GameObject _trollPrefab;
    [SerializeField] GameObject _ogrePrefab;
    [SerializeField] GameObject _dragonPrefab;

    List _orcs = new List();
    List _trolls = new List();
    List _ogres = new List();
    List _dragons = new List();

    void Start() {
        PrefabPoolingSystem1.Prespawn(_orcPrefab, 11);
        PrefabPoolingSystem1.Prespawn(_trollPrefab, 8);
        PrefabPoolingSystem1.Prespawn(_ogrePrefab, 5);
        PrefabPoolingSystem1.Prespawn(_dragonPrefab, 1);
    }

    void Update() {
        if (Input.GetKeyDown(KeyCode.Alpha1)) { SpawnObject(_orcPrefab, _orcs); }
        if (Input.GetKeyDown(KeyCode.Alpha2)) { SpawnObject(_trollPrefab, _trolls); }
        if (Input.GetKeyDown(KeyCode.Alpha3)) { SpawnObject(_ogrePrefab, _ogres); }
        if (Input.GetKeyDown(KeyCode.Alpha4)) { SpawnObject(_dragonPrefab, _dragons); }
        if (Input.GetKeyDown(KeyCode.Q)) { DespawnRandomObject(_orcs); }
        if (Input.GetKeyDown(KeyCode.W)) { DespawnRandomObject(_trolls); }
        if (Input.GetKeyDown(KeyCode.E)) { DespawnRandomObject(_ogres); }
        if (Input.GetKeyDown(KeyCode.R)) { DespawnRandomObject(_dragons); }
    }

    void SpawnObject(GameObject prefab, List list) {
        GameObject obj = PrefabPoolingSystem1.Spawn(prefab,5.0f * Random.insideUnitSphere,Quaternion.identity);
        list.Add(obj);
    }

    void DespawnRandomObject(List list) {
        if (list.Count == 0) {
            // Nothing to despawn
            return;
        }

        int i = Random.Range(0, list.Count);
        PrefabPoolingSystem1.Despawn(list[i]);
        list.RemoveAt(i);
    }
}
public interface IPoolableComponent {
    void Spawned();
    void Despawned();
}

预制池系统总结:
这个池系统为GameObject和预制的运行时内存分配问题提供了一个很好的解决方案,但是需要注意以下事项:

  1. 需要小心再重新生成的物体中正确的重置重要数据(刚体速度等)
  2. 必须确保不会预先生成太少或者太多的实例
  3. 应该小心IPoolableComponent中的Spawned和Despawned方法的执行顺序,不能假定他们以特定的顺序执行
  4. 在加载新场景的时候,必须要调用PrefabPoolingSystem1的Reset,以清除可能不再存爱的空引用对象

如果希望扩展这个系统,可以从下面的几个方面来做:

  1. 在GameObject初始化之后添加到GameObject上的IPoolableComponent不会触发他们的Spawned()或Despawned方法,因为值在GameObject首次初始化收集该列表,为了修复此问题,可以PrefabPool,在每次调用Spawned和Despawned时,获取IPoolableComponent引用,但是代价时生成和回收期间有额外的开销;
  2. 添加到预制根下的子节点的任何IPoolableComponent不会被同继,为了修复此问题,可以修改prefabPool来使用GetComponentsInchildren,代价是有额外的开销;
  3. 可以实现一种方法,让IPoolableComponent在获取期间设置优先级,并直接控制他们的Spawn和Despawned的方法的执行顺序;
  4. 这个系统不会将自己设置为DontDestroyOnLoad的预制实例友好的交互,明智的做法可能时在每个Spawn调用中添加一个布尔值,以判断加载新场景的时候,是否应该持久化;
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/900416.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号