注意点:
-
在 Creator 中,所有继承自 cc.Asset 的类型都统称资源,如 cc.Texture2D, cc.SpriteFrame, cc.AnimationClip, cc.Prefab 等。它们的加载是统一并且自动化的,相互依赖的资源能够被自动预加载。
2. 动态加载资源要注意两点,一是所有需要通过脚本动态加载的资源,都必须放置在resources文件夹或它的子文件夹下。resources需要在 assets 文件夹中手工创建,并且必须位于 assets 的根目录
3. 第二个要注意的是 Creator 相比之前的 Cocos2d-JS,资源动态加载的时候都是异步的,需要在回调函数中获得载入的资源。这么做是因为 Creator 除了场景关联的资源,没有另外的资源预加载列表,动态加载的资源是真正的动态加载。
一.加载本地资源
Creator 提供了cc.loader.loadRes这个 API 来专门加载那些位于 resources 目录下的 Asset。和cc.loader.load不同的是,loadRes 一次只能加载单个 Asset。调用时,你只要传入相对 resources 的路径即可,并且路径的结尾处不能包含文件扩展名。
cc.loader.loadRes("相对于resources的文件路径,无后缀!!!!",[类型 无重名可不写],回调)
1.加载图片
加载 SpriteFrame //以为图片会自动生成同名的Texture2D文件,所以必须指定类型
图片设置为 Sprite 后,将会在 资源管理器 中生成一个对应的 SpriteFrame。但如果直接加载 test assets/image,得到的类型将会是 cc.Texture2D。你必须指定第二个参数为资源的类型,才能加载到图片生成的 cc.SpriteFrame:
// 加载 SpriteFrame
var self = this;
cc.loader.loadRes("test assets/image", cc.SpriteFrame, function (err, spriteFrame) {
self.node.getComponent(cc.Sprite).spriteFrame = spriteFrame;
});复制代码
2.加载音乐
start () {
var self=this;
cc.loader.loadRes("sound/rainbowcom",function(err,audioClip){ //其实第二个参数可以不写
if(err){
console.log(err);
return;
self.node.getComponent(cc.AudioSource).clip=audioClip;
self.node.getComponent(cc.AudioSource).play();
}, 复制代码
3.虽然默认传递的第二个参数就是加载好的资源,但如果跨场景调用加载好的资源可以用cc.loader.getRes()方法
比如上面的音乐:
start () {
var self=this;
cc.loader.loadRes("rainbowcom",function(err,audioClip){
if(err){
console.log(err);
return;
self.node.getComponent(cc.AudioSource).clip=cc.loader.getRes("rainbowcom");
self.node.getComponent(cc.AudioSource).play();
},复制代码
如果是图片,第二个参数还是要传cc.SpriteFrame
二.远程加载资源
cc.loader.load(url,回调); //回调自动传参 err 和 对应对象
cc.loader.load({url:"",type:""},回调); //回调自动传参 err 和 对应对象
1.可能遇到的跨域访问问题
这里遇到了 浏览器跨域访问的问题 模拟器没有遇到.
原因是服务器禁止了跨域访问.解决办法:配置服务器 方法如下:(以宝塔面板为例 N)
①.点击软件商店 ,
②. 选择Nginx 设置
③.选择配置修改
④.在http配置字段的末尾加入如下配置语句
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers X-Requested-With;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS;
add_header 'Access-Control-Allow-Credentials' "true";复制代码
⑤.重启一下nginx
⑥.如果还有问题 点击网站
⑦. 对应的网站点配置
⑧. 在配置文件中的 location /{}字段加入如下配置代码
location /
add_header Access-Control-Allow-Origin *;
add_header 'Access-Control-Allow-Credentials' "true";
}复制代码
2.加载图片
加载图片会返回texture2D的类型(纹理数据)
pic_loadonline:function(){
cc.loader.load("http://www.dh70.top/game/resources/cat_128_128.png",function(err,ret){
if(err){
console.log(err);
return;
var spriteFrame1 = new cc.SpriteFrame(ret);
this.pic_sp1.spriteFrame=spriteFrame1;
}.bind(this));
start () {
this.pic_loadonline();
},复制代码
// 使用 spriteFrame.setTexture();方法加载纹理始终失败.目前还没弄明白为什么.(留坑再填)
//所以,创建了一个spriteFrame对象作为中间量来传递此纹理
3. 加载声音
load_soundonline:function(){
cc.loader.load("http://www.dh70.top/game/resources/Countdown_3.mp3",function(err,ret){
if(err){
console.log(err);
return;
this.node.getComponent(cc.AudioSource).clip=ret;
this.node.getComponent(cc.AudioSource).play();
}.bind(this))
start () {
this.load_soundonline();
},复制代码
4. 加载json //返回json对象
load_json_online:function(){
cc.loader.load({url:"http://www.dh70.top/game/resources/myjson.json",type:"json"},function(err,ret){
if(err){
console.log(err);
return;
console.log(ret);
}.bind(this));
start () {
this.load_json_online();
},复制代码
5.加载自定义文件 //返回文件文本化字符串
load_priv_data_online:function(){
cc.loader.load({url:"http://www.dh70.top/game/resources/condata.aydat",type:"aydat"},function(err,ret){
if(err){
console.log(err);
return;
console.log(ret);
}.bind(this));
start () {
this.load_priv_data_online();
},复制代码
6. 删除资源
场景中可以勾选自动释放资源,用来转场景时释放资源
但是,代码加载的资源不会自动释放
手动释放资源:
loadres加载的 用 releaseRes来释放
load加载的 用release来释放复制代码
关于资源释放的知识点
在加载完资源之后,所有的资源都会临时被缓存到cc.loader中,以避免重复加载资源时发送无意义的 http 请求,当然,缓存的内容都会占用内存,有些资源可能用户不再需要了,想要释放它们,这里介绍一下在做资源释放时需要注意的事项。
首先最为重要的一点就是:资源之间是互相依赖的。
比如下图,Prefab 资源中的 Node 包含 Sprite 组件,Sprite 组件依赖于 SpriteFrame,SpriteFrame 资源依赖于 Texture 资源,而 Prefab,SpriteFrame 和 Texture 资源都被 cc.loader 缓存起来了。这样做的好处是,有可能有另一个 SpriteAtlas 资源依赖于同样的一个 SpriteFrame 和 Texture,那么当你手动加载这个 SpriteAtlas 的时候,就不需要再重新请求贴图资源了,cc.loader 会自动使用缓存中的资源。
当你选择释放一个 Prefab 时,我们是不会自动释放它依赖的其他资源的,因为有可能这些依赖资源还有其他的用处。所以用户在释放资源时经常会问我们,为什么我都把资源释放了,内存占用还是居高不下?原因就是真正占用内存的贴图等基础资源并不会随着你释放 Prefab 或者 SpriteAtlas 而被释放。
接下来要介绍问题的另一个核心:JavaScript 中无法跟踪对象引用。
在 JavaScript 这种脚本语言中,由于其弱类型特性,以及为了代码的便利,往往是不包含内存管理功能的,所有对象的内存都由垃圾回收机制来管理。这就导致 JS 层逻辑永远不知道一个对象会在什么时候被释放,这意味着引擎无法通过类似引用计数的机制来管理外部对象对资源的引用,也无法严谨得统计资源是否不再被需要了。基于以上的原因,目前 cc.loader 的设计实际上是依赖于用户根据游戏逻辑管理资源,用户可以决定在某一时刻不再需要某些资源以及它依赖的资源,立即将它们在 cc.loader 中的缓存释放。也可以选择在释放依赖资源的时候,防止部分共享资源被释放。
最后一个值得关注的要点:JavaScript 的垃圾回收是延迟的。
想象一种情况,当你释放了 cc.loader 对某个资源的引用之后,由于考虑不周的原因,游戏逻辑再次请求了这个资源。此时垃圾回收还没有开始(垃圾回收的时机不可控),或者你的游戏逻辑某处,仍然持有一个对于这个旧资源的引用,那么意味着这个资源还存在内存中,但是 cc.loader 已经访问不到了,所以会重新加载它。这造成这个资源在内存中有两份同样的拷贝,浪费了内存。如果只是一个资源还好,但是如果类似的资源很多,甚至不止一次被重复加载,这对于内存的压力是有可能很高的。如果观察到游戏使用的内存曲线有这样的异常,请仔细检查游戏逻辑,是否存在泄漏,如果没有的话,垃圾回收机制是会正常回收这些内存的。
以上就是管理资源依赖和释放时需要注意的细节,这部分的功能和 API 设计还没有完全定案,我们还是希望尽力给大家带来尽可能方便的引擎 API,所以后续也会尝试一些其他的办法提升友好度,届时会更新这篇文档。
// 直接释放某个贴图
cc.loader.release(texture);
// 释放一个 prefab 以及所有它依赖的资源
var deps = cc.loader.getDependsRecursively('prefabs/sample');
cc.loader.release(deps);
// 如果在这个 prefab 中有一些和场景其他部分共享的资源,你不希望它们被释放,可以将这个资源从依赖列表中删除
var deps = cc.loader.getDependsRecursively('prefabs/sample');
var index = deps.indexOf(texture2d._uuid);
if (index !== -1)
deps.splice(index, 1);
cc.loader.release(deps);复制代码