最近项目需要,研究 Cocos Crater 的使用,初次使用还是踩了挺多坑的,解决之后,特此记录一下。
首先资源文件要放在 resources 文件夹下,然后使用 cc.resources.load
函数来进行资源加载。
cc.resources.load(path, type, onComplete:()=>void);
下面是一段代码示例:
cc.resources.load("music/bgm", cc.AudioClip, (err, clip :cc.AudioClip)=>{
if (err) {
cc.error(`AudioClip load error : ${err}`);
return;
}
clip.addref();
// clip 就是已经加载好的 AudioClip 资源
// To do something with clip
});
在最开始使用这个函数的时候,踩了一个挺烦人的坑,记录一下。
我获取了某节点上的 AudioSource
组件(即 this.audiosource
变量), 我希望读取 resources/music
文件夹下的 bgm.ogg
文件,设置为 this.audiosource
的 clip
,然后播放。
我尝试了下面几种写法(以下三种写法都是错误示范)。
写法一:
let mClip : cc.AudioClip = null;
cc.resources.load("music/bgm", cc.AudioClip, (err, clip)=>{
if (err) {
cc.error(`AudioClip load error : ${err}`);
return;
}
clip.addref();
mClip = clip;
});
this.audiosource.clip = mClip;
this.audiosource.play();
运行结果是失败的。
断点调试时发现,程序先执行了第 10 行的赋值语句,再执行第 8 行的赋值语句,所以给 this.audiosource.clip
赋值时 mClip
值仍为 null
。
写法二:
我将代码拆分开来,读取资源先执行,给 this.audiosource.clip
赋值通过调用其他函数来执行,mClip
声明为类的成员变量。
//声明一个成员变量 mClip 用来记录 clip 的值
private mClip : cc.AudioClip = null;
cc.resources.load("music/bgm", cc.AudioClip, (err, clip)=>{
if (err) {
cc.error(`AudioClip load error : ${err}`);
return;
}
clip.addref();
this.mClip = clip;
});
this.audiosource.clip = this.mClip;
this.audiosource.play();
运行结果是失败的。
断点调试发现,在第二块代码中,this.mClip
确实已经赋值成功,但是在第三块代码调用的时候,this.mClip
值仍为 null
。
写法三:
cc.resources.load("music/bgm", cc.AudioClip, (err, clip)=>{
if (err) {
cc.error(`AudioClip load error : ${err}`);
return;
}
clip.addref();
this.audiosource.clip = clip;
this.audiosource.play();
});
既然 clip
的值传不出来,我决定尝试直接在加载资源的函数里为 this.audiosource.clip
赋值。
运行结果是失败的。
经过断点调试发现 clip 值是正常的,但是 this.audiosource
为 null
。
上述是我进行资源读取的尝试过程。
当然代码是有问题的,因为结果都是失败的。
如果你能看出来上面的代码问题出在哪儿,那么恭喜你,成功跳过了这个坑。如果你也遇到了同样的问题,那么继续看,一起填坑吧。
下面是错误原因分析及正确写法:
核心原因是:资源加载时异步的。
第一种写法失败的原因。第 8 行的代码是资源加载成功后执行的,而由于异步加载,第 10 行代码是直接执行的(不会因为等待加载资源而阻塞),所以执行顺序是反过来的。
第二、三种写法失败的原因,是在于 this
指针。由于异步加载,在 onCompleted
执行时,this
指向的对象,其实跟 this.audiosource
这里的 this
指向的对象不是同一个。
let self = this;
cc.resources.load("music/bgm", cc.AudioClip, (err, clip)=>{
if (err) {
cc.error(`AudioClip load error : ${err}`);
return;
}
clip.addref();
self.audiosource.clip = clip;
self.audiosource.play();
});
解决方法,使用一个临时变量 self 来记录 this 指向的对象就可以了,如上代码所示。
2020.12.14 更新
随着学习的深入,我发现是我想复杂了,其实还有最简单的一种方法。
就是回调函数的 .bind(this)
函数。示例代码如下
//调用回调函数时,使用 .bind(this) 可以绑定当前对象
cc.resources.load("music/bgm", cc.AudioClip, _callback.bind(this));
//使用 bind(this) 之后,回调函数中可以正常使用 this 对象了
_callback(err, clip) : void {
if (err) {
cc.error(`AudioClip load error : ${err}`);
return;
}
clip.addref();
this.audiosource.clip = mClip;
this.audiosource.play();
}