模拟场景:加载spriteFrameA后,立刻加载了spriteFrameB设置到sprite中。
问题1:spriteFrameA是不是可能比spriteFrameB更晚加载完成,导致sprite设置成了spriteFrameA;
问题2:如果问题1成立,那怎么解决顺序问题?
有可能
你都知道问题了,还不知道怎么解决吗 你想要A 那就死等A完成就是了
sprite先加载spriteFrameA,然后又立即加载spriteFrameB, 有可能spriteFrameB比spriteFrameA先来·我的做法是加载spriteFrameB的时候清理掉了spriteFrameA的回调处理·就能保证资源的加载和操作有序了。
那就是需要记录sprite和回调的关系,同一个sprite加载时,只保留最新的回调方法。
是的·我是这么搞的
感谢大佬!在思考资源加载管理的问题,看了一两个开源框架,框架上都没有处理加载顺序的代码,所以有点搞不清是本来就没有加载顺序的问题,还是有问题但是框架没有做而已。
const sp = new cc.Node().addComponent(cc.Sprite)
sp.spriteFrame= await 加载 spriteFrameA
sp.spriteFrame= await 加载 spriteFrameB
这得等所有的加载完才能结束,本来等一个加载完就行。并且不一定是同一个地方加载的
我的意思是 加上await 基本都能解决 除非一些特殊情况 比如在不同组件的生命周期中 赋值同个sprite对象的spriteframe 这个就麻烦了
理解了
我的做法加载资源封装里直接给节点身上带个 node.index = 0,每次load时let index = node.index + 1后node.index += 1,异步回调里判断 index 跟 node.index不一致就return了,不管一帧内load了几次只有最后那次set spriteframe
异步加载,异步异步就表示是不同步,就意味着你不知道谁先完成。
如果要有顺序的执行,要么做回调,要么写await。
也可以看看API,可以单个加载,可以一组加载,也可以一个文件夹加载。
这样就可以不管顺序等全部加载完再执行你的操作。
是有这样子的问题,所以设置头像的时候 我给精灵加了一个头像路径的属性,在回调中检查是不是这个属性
private static headCache: Map<string, cc.SpriteFrame> = new Map()
//设置玩家头像 远程加载的 url xxx.png
public static setPlayerHeadImg(headImgUrl: string, nodeSp: cc.Sprite, designWidth: number, designHight: number) {
if (!headImgUrl) {
//这里处理一个问题 网络差的时候 头像已经置空了 加载成功回来了
(nodeSp as unknown as any).originalHeadImgUrl = headImgUrl;
nodeSp.spriteFrame = null;
return
}
headImgUrl = GameLogic.getImgCdn() + headImgUrl
let findSpriteFrame = this.headCache.get(headImgUrl);
(nodeSp as unknown as any).originalHeadImgUrl = headImgUrl;
if (!findSpriteFrame) {
cc.assetManager.loadRemote<cc.Asset>(headImgUrl, (err, imageAsset: cc.Texture2D) => {
if (err) {
LogMgr.err(`加载头像失败,${headImgUrl},${err}`);
return;
}
findSpriteFrame = this.headCache.get(headImgUrl)
if (!findSpriteFrame) {
findSpriteFrame = new cc.SpriteFrame(imageAsset);
this.headCache.set(headImgUrl, findSpriteFrame)
}
if ((nodeSp as unknown as any).originalHeadImgUrl != headImgUrl) {
return
}
if (nodeSp.isValid) {
let obj = this.getCurWidAHig(designWidth, designHight, imageAsset.width, imageAsset.height)
nodeSp.node.setContentSize(obj.designWidth, obj.designHight)
nodeSp.spriteFrame = findSpriteFrame;
}
});
} else {
let obj = this.getCurWidAHig(designWidth, designHight, findSpriteFrame.getTexture().width, findSpriteFrame.getTexture().height)
nodeSp.node.setContentSize(obj.designWidth, obj.designHight)
nodeSp.spriteFrame = findSpriteFrame
nodeSp.node.active = true;
}
}
/**
* 获取一个头像后头像大小问题
*/
private static getCurWidAHig(designWidth: number, designHight: number, sourceWidth: number, sourceHight: number) {
if (sourceWidth == sourceHight) {
//等比
return { designWidth: designWidth, designHight: designHight }
}
if (sourceHight > sourceWidth) { //高大于宽
let newHight = 0
newHight = (designWidth * sourceHight) / sourceWidth
return { designWidth: designWidth, designHight: newHight }
}
let newWidth = 0 //宽大于高
newWidth = (designHight * sourceWidth) / sourceHight
return { designWidth: newWidth, designHight: designHight }
}
为啥要先 as unknown 再 as any 啊?纯好奇
方案1、loading预加载,就不存在异步问题了
方案2、用await加载A再加载B
方案3、加载A以后,如果图片已有B则啥也不干
加载的时候分配一个加载序号。 然后加载回来以后比较这个回调的加载序号与当前的加载序号大小, 小就扔掉
加载的时候对当前的sprite加一个属性名并赋值当前加载图片名,加载回调的时候,判断是否一致,一致再进行赋值,不然直接返回~
其实说白了,你需要一个取消异步机制,自己封装一个到框架去,比如加载一个预制体因为网络问题导致过了一段时间需要更新预制体的时候之前的还没加载完,这个时候就需要把之前的取消加载,否则就混乱了,这在开发中很容易碰到,不处理一旦到弱网环境一堆问题
你可以用回调层层嵌套解决你需要按顺序加载的问题。
当你解决后发现代码可读性差的时候,你可以用promise代替回调方式处理加载顺序问题。
这个时候你已经完成了解决回调地狱的问题。
当你发现即便用promise解决了顺序加载问题,但当加载文件数量很多时,可读性,可维护性依然达不到心理预期,
这是你async await 语法糖可以解决你的问题。