动态加载龙骨资源
你会注意到,对应纹理的dynamicRef会变很多,是因为骨骼多导致的。
dragonBones的动态引用实现思路如下:
动态加载时,
tex/ske的static正常都是0,在Armature.dragonAsset和Armature.dragonAtlasAset赋值时,对assets进行引用管理,目前这样的实现逻辑dynamicRef符合预期。
注意到texture.staticRef=1,是因为tex引用导致的,staticRef=1在gc时会排除(因为gc只关注staticRef=0的情况),问题不大
当龙骨所在的节点销毁后,目前ske和tex能够恢复dynamicRef,真正的texture的动态引用没有恢复。
不过这个问题不大,因为在释放db.tex时,会把真正的texture也释放掉,因为会查找db.tex的依赖
ske这类非纹理资源的渲染帧问题
ske的renderTime始终为0,gc在遇到0的时候就认为一直没有使用,事实上目前renderTime只能检索到纹理,非纹理类的资源是没有效的
但是因为dynamicRef是正常的,所以他并不会被释放,当dynamicRef真正为0时,会被立刻释放,可能存在和tex释放不同步的问题
解决办法也有,取了个巧:
this.dragonAsset.getLatestRenderFrame = () => {
const dragonAtlasAsset = this.dragonAtlasAsset;
if (dragonAtlasAsset === null) {
// prefab模式加载龙骨,销毁龙骨时,dragonAtlasAsset=null
return 0;
} else {
// 重写函数:使用dragonAtlasAsset的renderTime
return dragonAtlasAsset.getLatestRenderFrame();
}
};
通过prefab加载龙骨,销毁后ske是索引不到tex的,所以导致这里为0
对于非纹理类的资源,这种借用的做法很明显存在缺陷,一旦依赖的纹理和自身失去了关联,就导致无法检索到renderTime,所以还是得assets增加一个renderTime属性,后续开发过程中也证明这样做很有必要。
在update中,纹理和自身同步即可
Armature.update(){
const total = cc.director.getTotalFrames();
this.dragonAsset.setLatestRenderFrame(total);
}
db需要加载2个资源带来的问题
因为它是需要动态加载2个资源,需要2次load的过程
function createDB(asset_tex, asset_ske){
// 异步创建dragonBones
setTimeout(()=>{
const db = new cc.Node("db");
const comp = db.addComponent(dragonBones.ArmatureDisplay);
comp.dragonAsset = asset_ske;
comp.dragonAtlasAsset = asset_tex;
// 一定要记得取消gc的保护,否则gc将无法自动释放相关资源
gc.protectAsset(asset_tex, false);
gc.protectAsset(asset_ske, false);
}, 1000);
}
cc.loader.loadRes(tex,(error, asset_tex)=>{
// 此时asset_tex并不会被释放,调用这个api的原因就是为了防止gc释放掉
gc.protectAsset(asset_tex, true);
cc.loader.loadRes(ske, (error, asset_ske)=>{
// 如果createDB中创建dragonBones没有异步逻辑,可以取消对asset_ske的保护
gc.protectAsset(asset_ske, true);
// 因为load ske需要时间,如果这段时间刚好触发gc,就会将之前的asset_tex释放掉
createDB(asset_tex, asset_ske);
})
})
prefab方式加载龙骨
prefab静态引用
prefab在释放的时候会连带所依赖资源也一起释放
var depends = dependUtil.getDeps(asset._uuid);
for (let i = 0, l = depends.length; i < l; i++) {
var dependAsset = assets.get(depends[i]);
if (dependAsset) {
dependAsset.decRef(false);
releaseManager._free(dependAsset, false);
}
}
prefab是能够自己感知到所依赖的资源的,staticRef这些都是静态引用。
实现prefab的动态引用管理
一般在使用dragonBones的时候,都是搞到一个prefab中,这样的话prefab就会自动统计所依赖的静态资源。
我也考虑到了你可能在prefab里面添加龙骨,其实本质上还是对prefab的管理
测试发现:加载前,释放后,ref并没有任何变化
实现思路应该从prefab.dynamicRef下手,调试下顶层的加载逻辑
const ins = cc.instantiate(prefab);// 实例化prefab
追踪代码,发现最终都走到了base-node,所以可以在base-node里面添加对prefab资源的引用操作。
node._instantiate(){
if (cloned._prefab && cloned._prefab.asset) {
cloned._prefab.asset.addDynamicRef();
}
}
node.destroy () {
if (this._prefab && this._prefab.asset) {
this._prefab.asset.decDynamicRef();
}
}
接下来就是更新prefab.renderTime,最开始我想到的是update函数,但是这个函数是针对component的,node并没有update。
还是得从渲染逻辑下手,换个思路,因为prefab资源是和node关联的,所以只要node被渲染了,对应的prefab也算是被渲染了。
如何知道node被渲染了?是的!RenderFlow
我们可以在RenderFlow里面的children阶段,增加node.visit的机制
RenderFlow.prototype._children = function (node) {
node.visit && node.visit();
}
node.visit() {
if (this._prefab && this._prefab.asset) {
const total = cc.director.getTotalFrames();
this._prefab.asset.setLatestRenderFrame(total); // 更新prefab的渲染帧
}
}
至此,我们的prefab已经完全可以正常被gc管理了。
因为prefab在释放时,会连带释放依赖的资源,我们也恰巧是利用了这点,达到了prefab完美的释放。