【Cocos Creator 踩坑笔记】如何用代码动态读取资源

最近项目需要,研究 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.audiosourceclip ,然后播放。

我尝试了下面几种写法(以下三种写法都是错误示范)。

写法一:

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.audiosourcenull

上述是我进行资源读取的尝试过程。

当然代码是有问题的,因为结果都是失败的。

如果你能看出来上面的代码问题出在哪儿,那么恭喜你,成功跳过了这个坑。如果你也遇到了同样的问题,那么继续看,一起填坑吧。


下面是错误原因分析及正确写法:

核心原因是:资源加载时异步的。

第一种写法失败的原因。第 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();
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页