Unity开发(六) Prefab加载自动化管理引用计数管理器

本文介绍Unity中Prefab的自动化管理,包括Prefab实例化、自动销毁核心ObjInfo、Prefab加载管理器的实现,以及同步异步加载、回调逻辑和引用计数管理,旨在实现GameObject内存最小化和代码健壮性。
摘要由CSDN通过智能技术生成

前言

这段时间经历离职,十一,入职,搬家,一个月没有鼓捣博客了,现在继续我们的博客之旅。

这篇文章是关于Prefab加载管理器,实现GameObject创建销毁的自动化管理,保证内存的最小化和代码健壮性。
另外,这篇文章依赖于上一篇文章,读者可以先参阅。

Unity资源加载

Unity资源类型,按加载流程顺序,有三种

  1. AssetBundle 资源以压缩包文件存在(Resources目录下资源打成包体后也是以ab格式存在)
  2. Asset 资源在内存中的存在格式
  3. GameObject 针对Prefab导出的Asset,可实例化
加载
Prefab实例化
AssetBundle
Asset
GameObject

针对AssetBundle 的加载,读者可以参阅AssetBundle同步异步引用计数资源加载管理器
针对Asset 的加载,读者可以参阅Asset同步异步引用计数资源加载管理器
针对GameObject的加载,本文会作讲解,并提供整套方案和代码

Prefab实例化

我们都知道Prefab是一种存储格式,加载后是一个asset格式文件,需要实例化后才是一个可用的GameObject。
而如果每次我们都去加载asset,然后实例化,然后代码管理每一个GameObject的内存,这当然不是我们想要的。那我们想要的是什么?
我想要的是——自动化管理

  1. 提供一个接口,加载到指定GameObject
  2. 不用关心销毁,内存管理,实例克隆等操作
  3. 可以实现同步加载,异步加载和手动销毁

Unity自身肯定不包含上述功能,需要我们自己实现框架

自动销毁核心——ObjInfo

我们先不关心管理器内部逻辑,先关心如何实现自动销毁

我们都知道AwakeOnDestroy函数,顾名思义,创建和销毁的时候调用,那么我们很简单地构造一个MonoBehaviour,实现如下

using UnityEngine;

public class ObjInfo : MonoBehaviour
{
   
    void OnDestroy()
    {
   
        //被动销毁,保证引用计数正确
        PrefabLoadMgr.I.Destroy(this.gameObject);
    }
}

我们可以对每个通过Prefab加载管理器创建的GameObject添加一个ObjInfo,这个GameObject在销毁的时候,会调用ObjInfoOnDestroy方法,会通知管理器,销毁自身,,这样就实现了自动销毁

如果GameObject被克隆了呢,就会造成引用计数出错,所以还要修正引用计数的正确性,如下

    public int InstanceId = -1;
    public string AssetName = string.Empty;

    void Awake()
    {
   
        if (string.IsNullOrEmpty(AssetName)) return;
        //非空,说明通过克隆实例化,添加引用计数

        InstanceId = gameObject.GetInstanceID();
        PrefabLoadMgr.I.AddAssetRef(AssetName, this.gameObject);
    }

我们很开心地以为可以了,但Unity并没有按我们理想化的方式运行。

实例化——必须的active

Unity提供的AwakeOnDestroy函数,必须在GameObject节点被active的情况下,才会触发运行,也就是说新加载的节点GameObject, 必须挂在启用的GameObject节点下。
那这样我们就先将节点创建在一个通用节点_assetParent.transform下,然后再把它移到目标节点上,我们就得到了如下代码:

private GameObject InstanceAsset(PrefabObject _prefabObj, Transform _parent)
{
   
	GameObject go = GameObject.Instantiate(_prefabObj._asset, _assetParent.transform;) as GameObject;
	go.name = go.name.Replace("(Clone)", "");
	ObjInfo obgInfo = go.AddComponent<ObjInfo>();
	
    if(!go.activeSelf)
    {
   //保证GameObject active一次,ObjInfo才能触发Awake,未Awake的脚本不能触发OnDestroy
        go.SetActive(true);
        go.SetActive(false);
    }

	if (obgInfo != null)
	{
   
		obgInfo.InstanceId = go.GetInstanceID();
		obgInfo.AssetName = _prefabObj._assetName;
	}

	if (_parent != null)
		go.transform.SetParent(_parent);

	return go;
}

这样终于实现了自动销毁,不过笔者测试发现,go.transform.SetParent是一个非常耗时的操作,而我们大部分情况下,挂载的父节点都是active的,所以我们优化成可以挂载在目标节点下,直接挂载,不能再挂载在公用节点上,具体代码见下文。

Prefab加载管理器

加载管理器,在前面几篇描述了很多了,关键词是——加载单元,队列,外部接口。这一部分也不例外,只不过简单了很多。

外部接口
异步
同步
卸载
移除回调
增加引用
刷新
每帧调用
依赖
LoadAsync
LoadSync
Destroy
RemoveCallBack
AddAssetRef
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值