看了文档后有几个关于Bundle的问题

  • Creator 版本:2.4.9
  1. 游戏启动的时候,是不是会将几个内置bundle都立刻载入内存?
    如果没使用初始场景分包的话,是不是internal, main, resources 都会在启动时载入内存?

  2. 假如从resource中load一个prefab asset,没有addRef的话,是不是这个prefab随时都有可能被gc回收?即使我们在类中有一个变量引用了这个load的prefab asset?如果我instantiate了一个这个prefab,是不是就相当于给这个prefab asset addRef了?

  3. 假如我有三个main scene(非初始场景)。那么我把几个场景都改成prefab,然后只用一个空scene,在运行时用resources.load来载入和切换这些场景prefab,这样相比原来用scene来管理,性能上有差别吗?

按自己的理解回答一下:1.启动时进入主场景,如果主场景没有依赖相关分包,代码里没有主动加载某个分包,不会加载也就不会载入内存。 2. 有可能被回收,如果你只是加载没有使用,cocos回收的逻辑不是根据变量引用的,即使用变量引用保存了,依旧不影响清除,被清除再看你这个变量只能看到一个对象里大量属性为空的prefab,这个时候无法使用了,所以要addref,instantiate 会不会相当于给prefab addref呢,目前在源码里是没看到的,文档有cocos 有详细的回收策略你可以看看。3. 我自己我感觉没有太大差别,你可以翻翻论坛里有没有类型的问题,是否是预制体还是场景看你业务需求如何,prefab我觉得更好控制,但是肯定也是一个场景来展示的。

我查了文档,没看到清晰的说明。但我觉得是启动时全部加载了,因为resources的优先级比main bundle高,按文档定义,当一个低优先bundle的script引用了高优先bundle内的资源,系统不会自动加载高优先bundle,而是要求开发者自己确保高优先bundle已经被加载。

所以,既然我们能正常地在脚本中访问resources的资源(比如实例化resources目录中的prefab),那应该说明resources bundle已经和main bundle一起预先被加载了。

所以,如果启动时系统已经把resource bundle也加入内存,那把scene做成prefab放入resources,就和保持为scene格式放在main bundle里没什么性能区别了吧。

我猜应该是addref了,不然的话,假设从prefab实例化了一个模型,结果运行了一会texture被gc,模型可能就缺胳膊少腿了。

能不能介绍一下用resources.load prefab相对于使用场景的优点?我看到resources.load是异步的,但是director和bundle好像也可以用preloadScene异步加载。那么用resources和prefab的主要优势在哪里?

  1. 如果没有小游戏初始分包,那么会的,如果分包了,也是加载了初始分包后(这是为了减少冷启动的时间,其中internal比start优先级更高,所以即使分包,也会先加载),也会自动加载这些分包(单指引擎内置的分包,游戏内分包,需要自行加载)。
    image
  2. 没有addRef,也不会gc,gc与引用计数不完全对等,而且此处所涉及的也仅仅只是资源的引用计数,完全无关gc,没有addRef,那么该动态资源就没有进行引用计数,除非主动调用release,否则该资源一直在内存中。引擎的引用计数,是针对资源而言的,你instantiate只是在实例化节点,与资源无关,因此也无关addRef。
  3. 需要自行控制资源销毁的情况,如果是scene,scene跳转可以勾选自动释放资源。

PS:初学者容易误导的点,我们一般讨论的引用计数都是说的资源,gc是v8的,所以gc与引用计数在这一层面,完全没有相关性,可以粗暴的理解为gc是控制代码的内存,引用计数是控制资源的内存。

假如有start-scene包,你的意思是说游戏启动时先加载internal和start-scene bundle, 然后显示初始场景,场景显示出来后,初始场景中就开始load main和resource bundle吗?

我刚才用的语言不准确,不应该说gc,而应该说释放asset。
你的意思是说,从resources.load一个prefab,只要我不调用release,它所引用的资源都是一直有效的?不会出现我之后某个时刻调用instantiate发现这个prefab引用的资源已经被释放了?

如果你能保证该资源仅被动态加载(因为挂载的话,引擎会进行引用计数),且也没有进行引用计数(addref),那么你不主动释放,它就一直在。

但是它文档里表达的内容感觉不太一样。
image

它建议对resources.load的资源addref以防止其被提前错误释放。由于releaseXxx方法是不进行释放检查的(不考虑引用计数直接释放),那这个addref显然不是用来防release,而大概是用来防引擎某种自动清理ref为0的资源的扫描机制。

我觉得你问的很好,那首先确定是否加载问题的验证,可以在进入场景之后去看看控制台的网络是否已经被请求了,来确定resources 好main加载顺序的问题。

drean93说了一点相关的好处,但是我平常搭建的时候更喜欢只留一个必须的scene,prefab相对来说更好控制,比如说 我可以把prefab存入对象性做缓存避免下次进入会重复加载,如果再根据业务场景去删除或做其他事情。

1赞

因为你不addRef的话,资源就没有进入引用计数机制,存在下面的问题

  1. 如我所说,你如果不小心又将该资源进行了静态引用(挂载),那么那个地方被销毁后,该资源就销毁了
  2. 如果没有静态挂载,则该资源除非手动释放,否则一直在内存中,则内存不好控制。

如果你能做到资源加载及其完美(或者你根本不关心内存调用),你可以不使用addRef。
否则,很容易碰到下面的问题

// 假设动态加载a后,不进行引用计数
let a1 = load('a');
// 而在某个地方,你又加载了一次a
 let a2 = load('a');
// 某个时段,你因为不需要a了(因为a1已经不用了,但是你忘记了a2还在使用)
// 因为你没有进行引用计数,你释放只能通过release
release(a);
// 此时a的完全被销毁了,那么a2也无法使用


// 如果你进行引用计数,则没有该问题
let b1 = load('b');
b.addRef();
let b2 = load('b');
b2.addRef();
// 因为b资源的计数为2,此时你释放b1,b资源的计数也还是1,并不会销毁
b1.decRef();

另外,文档说得很清楚,你把文档连贯来读,就明白了

1赞

清晰明了的

你的意思是把一个scene拆成多个prefab,然后切换时保留公用的部分,然后替换掉有差异的部分?

恩是,可以这么做

1赞