如何学习

如何开始学习Cocos2d-JS?我我觉得比较好的方式是:

1)看测试例:测试例

2)看API文档:

  • 在线API索引(中文版)

  • 下载版API索引

3)看源码

另外还有大神录制了进阶视频教程:Cocos2d-JS进阶视频教程,里面关于自学的艺术还是讲的挺好的,在这里我就不累赘了。

如何开始

如何开始很简单了,就两条:

  • 搭环境

  • 编码

如果你是做纯web的游戏,搭建环境很简单:

  • 下载引擎包

  • 下载Python2.7.6,Ant,如果没有安装java,请去下载jdk至少1.7的版本

  • 配环境:无疑就是配置java环境、python环境、ant环境变量。基本上就是在系统环境变量path加入相关的bin目录地址。不会的自行google吧。

  • 创建项目:cocos new -l js ProjectName

  • 开始编码:sublime、webstorm等等用你喜欢的开发工具吧。

官网也有相关文章:用Cocos Console工作流开发网页/原生平台游戏(JSB开发环境简介)

如何编码

如何编码?如果团队协作,遵循团队的编码规范咯。这里我只是简单说一点点:

1. 关于project.json文件的配置信息

这个其实在main.js里面有注释说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 debugMode: 0 
 //0 不显示任何错误信息 
 //1 显示cc.assert, cc.warn, cc.log
 showFPS : true 
 //为真会在屏幕左下方显示游戏帧率 id: gameCanvas 
 //游戏画布的id
 renderMode: 0 
 // 0 : 自动选择渲染引擎:webGl、canvas
 // 1 : canvas 
 // 2 : WebGL(在webgl模式下drawNode会存在严重的锯齿) 
 // modules : 游戏引用的模块 
 // 模块的名字可以在frameworks/cocos2d-html5/moduleConfig.json 
 // 里面找到,所以后期在上线的时候可以选择自己用到的模块引入来减少js文件的大小


2. 关于CCBoot.js

CCBoot.js是入口js文件,所以你很有必要认真看一看其中的代码,比如其中提供一些可用的工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 //创建元素 
 cc.newElement
 //监听事件 
 cc._addEventListener
 //循环操作 
 cc.each
 //继承 
 cc.extend 
 cc.isFunction 
 cc.isNumber 
 cc.isString 
 cc.isArray 
 cc.isUndefined 
 cc.isObject
 ...
 //原生的渲染引擎: CanvasRenderingContext2D/WebGLRenderingContext 
 cc._renderContext
 //包裹游戏画布的外层
 div cc._gameDiv / cc.container

比如一些你可能想要修改的东西:

包裹游戏的外层div的id名字:

1
2
 //大约CCBoot.js的1864行:
 localContainer.setAttribute('id''Cocos2dGameContainer');

初始加载的时候的画布颜色:

1
2
 //大约CCBoot.js的641行: 
 canvasNode.style.backgroundColor = "black";

frameworks/cocos2d-html5/core/platform/目录下也有很多东西,主要是平台相关的东西。

如简单操作dom的工具miniFramework.js里面的:

1
 cc.$

屏幕适配相关的东西’CCEGLView.js’里面的:

1
2
3
 cc.view.adjustViewPort
 cc.view.setDesignResolutionSize
 cc.view.resizeWithBrowserSize ...

文件加载相关的CCLoader.js:

Cocos2d-JS的文件加载是通过文件后缀的,如后缀名为["png", "jpg", "bmp","jpeg","gif", "ico"]的文件会通过cc._imgLoader来加载,理解了这里可以用于后期自定义文件加载插件。

还有一些编码上面的东西放在下面再说。

关于音频

音频,在移动端上一直是个巨坑的问题。能自动播放,不能自动播放、不能循环播放、根本就不播放等等简直就是处处都是坑啊。

开始游戏中的音频用的是cocos里面的封装的音频函数,会对音频文件进行预加载、预处理。但是这里有个严重的问题,那就是加载的时候很容易卡在音频那里,而且循环播放也有问题。于是决定用会原生的audio标签。

1
<audio id="gameAudio" src="res/gameMusic.mp3" loop="loop" ></audio>

看起来应该是不错的样子,但是当我们在iphone4s微信内置浏览器里面测试的时候发现这个audio标签居然占据页面空间(不是说加了controls这个属性才会占空间么,坑啊!),于是自然而然想到的解决方案就是none掉:

1
<audio style="display:none" id="gameAudio" src="res/gameMusic.mp3" loop="loop" ></audio>

感觉好像轻易解决了这个问题,于是开始编写一堆音频处理的代码。编写完毕之后,拿起iphone试一试,我去啊iphone上音频不能循环播放了啊,能不能再坑点!这个问题困扰了好久不知道什么原因,拿着代码去找师傅去吧!师傅只有一句话:ios上面音频如果不显示或者在屏幕之外有可能就会出问题哦!终于把display:none去掉一切又好了。孩子你还是太年轻了啊!对于占据空间的问题我们现在只能把音频标签作为body最后一个child节点,依然没有很好的解决方案。

对于音频的自动播放,目前只发现在iphone6 plus是可以的(iphone 6没有所以没试过),所以只能通过用户触摸事件来加载。如果在HTML标记中使用了autoplay属性,将会忽略这个属性,并且不会在加载页面时播放此文件,对于 preload 属性,同样会忽略。唯一能解决的就是用户进入页面是,让用户触发 touch 事件,并且只能在touch事件的回调里面加载才有效。

1
2
3
4
5
6
7
8
9
10
var $audio = document.getElementById('gameAudio');
//用户点击屏幕的任何一个地方就去加载音频
document.addEventListener('touchstart', function(e){    
    $audio.load();
},false);
//在某个cc.MenuItemImage/cc.MenuItemSprite/onTouchBegan的回调里面执行播放操作
try{    
    $audio.play();
}catch(e){}

音频的暂停:

1
2
3
try{    
    $audio.pause();
}catch(e){}

音频的停止:

1
2
3
4
try{    
    $audio.currentTime = 0;    
    $audio.pause();
}catch(e){}

如果是多个音频文件,可以使用音频audio sprite,所有的音频综合到一个单音频流中,然后播放此流的各个部分。

1
2
3
4
5
6
7
8
9
10
var audioData = {
    bg: {
        start: 0,        
        length: 1
    },    
    run: {
        start: 1.3,        
        length: 1.5
    }
};

要播放bg这段声音:

1
2
3
4
try{    
    $audio.currentTime = audioData.bg.start;   
    $audio.play();
}catch(e){}

当播放结束时:

1
2
3
4
5
6
7
$audio.addEventListener('timeupdate', function()
{    
    if (this.currentTime >= audioData.bg.start + audioData.bg.length) 
    {        
        this.pause();
    }
}, false);

需要注意的是,更改 currentTime 并不是百分百正确的。假设 currentTime 设为 3.2,而实际得到的却是 3.4。每个 audio sprite 之间需要少量的空间,以避免寻找到另一个 sprite 的头部。

一些工具

  • 精灵图制作:TexturePacker

相当好用的精灵图制作工具,虽然收费,但是可以申请免费的密钥,具体申请地址自己查吧。

用它制作精灵图的时候注意一个地方就可以了:

TexturePacke.jpg

勾上这个Reduce border artifacts,它会让你的精灵图边缘没有锯齿,其它的配置地方我基本上都是默认的。

  • 位图字体制作:Bitmap Font Generator

位图字体其实是一张图片,然后记录每个文字的位置和大小,因此字体的大小在生成的时候已经定义了,所以在用的时候最好不要改变字体的大小,如果改变了大小会对图片进行缩放,可能会出现锯齿。

用Bitmap Font Generator这个软件制作的字体会生成一个png文件和一个fnt文件。选择左上方的Options->Font settings

bmfont.jpg

设置字体格式和字体大小。其它基本上都是默认设置就好了。

可以在主界面上面一个个点击选择需要制作的文字,也可以把要选择的字体放在一个txt文件里面(txt文件编码为Utf-8),然后选择菜单栏的Edit->Select chars from file来选择要制作的文字。

Bitmap Font Generator还支持从image文件生成字体纹理图,选择菜单栏的Edit->Open Image Manager->Image->Import image(注意路径里面不要有中文)

bmfonti.jpg

Id那里填写对应文字的ASCII码,如我上传的图片里面的文字是2,其对应的ASCII码是50。如果不知道某个文字的ASCII码,可以去这里查:ASCII码对照表。

选择好之后点击菜单栏Options->Export options

bmfonte.jpg

  • 字体精简:FontCreator

为什么需要加载字体?因为有时候页面上会有不同大小的特殊字体,用位图又比较麻烦,加载整个字体库又太大了,于是我找到了这个软件。

选择菜单栏File->New Project,填写你的字体名字和字体样式。然后选择菜单栏File->Open,打开你要精简的字体。(在新建项目的时候有一个Include outlines/Don’n include outlines的选项。如果选择Include outlines它会默认帮你添加一些字符)

fontc.jpg

如果是可以直接看得到的字符,我可以直接在原有的字体文件中选中这个字符,然后复制粘贴到我的项目里面对应的位置上去,如果不是的话,我们可以通过下面的方法添加进去。

ctrl+f键查找某个字符,如我们要添加的字是“我”,然后选择Glyph Properties

fontc1.jpg

复制Codepoints里面的参数,然后选中自己的项目,选择菜单栏Insert->Charaacters,把刚才复制的Codepoints粘贴到对应的位置。

fontc2.jpg

这时你会发现在你项目字符的最后会添加一个灰色方块,回到原始字体那里,选中那个字直接复制粘贴到刚才添加的那个方块里面就可以了:

fontc3.jpg

在上面的图里面你可以看到有很多灰色的方块,看看里面显示的字符,如果你用不到你可以直接点Delete键把它删掉。

除了用这个软件外,最近发现一个工具也可以用来精简压缩字体,感兴趣的可以试试:字蛛——中文字体自动化压缩工具

  • 地图制作:Tiled Map Editor

这个其实在我们的项目中没有用到,所以在这里提一下而已,有用到的可以自己去琢磨一下。

项目发布

项目发布意味着什么?简单来说就意味着资源压缩、代码合并。首先来说下资源压缩吧。

让你爱恨交加的closure compiler

不是说Cocos2d-JS么?怎么会扯到closure compiler。如果你想要发布你的Cocos2d游戏,我觉得就不得不说说closure compiler这玩意,这也是为何安装jdk的时候要求要jdk7以上的版本了。

在cocos项目发布的时候,有一个命令是cocos compile -p web -m release --advanced,后面加了一个advanced的参数,这种模式下面使用的是closure compiler的高级js压缩模式,压缩比例惊人,同时会优化js的执行,因此压缩之后的js性能也会得到一定提升。但是想要用这个高级压缩模式可是有坑的,很可能你会发现你的代码经过这种压缩模式压缩之后就报错了。

所以你最好去closure compiler官网(google出品,所以需要翻墙)看一看,这里我只提简单的几点。

需要保留变量名的,要么用@expose关键字申明,要么使用[]来引用,如:

1
2
3
4
5
6
7
8
9
10
11
/** @expose */
window.MM;
/** @expose */
M.add;
/** @expose */
M.add.Pos;
//or
window['MM'];
MM['add'];
MM['add']['Pos'];

比如在ajax请求数据的时候的请求参数和响应参数,你要写成这样:

1
2
3
4
5
6
7
//request data
//加引号
{'name''dddd''id''123'}
//response data
//通过数组的方式取值
data['list'];
data['list']['userName']

记住,任何你需要让它在压缩之后能保持原样输出的你都应该这样写。如果要了解更多你可以去它官网看看,或者看一看cocos2d-js的源码,里面有很多应对closure compiler高级压缩模式的注解。

plist文件合并

一次请求是比较耗费资源的,所以我们把plist文件合并在一个文件里面来加载,同时也对plist进行了压缩,下面是加载解析合并后的plist文件的插件源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
 * 将plist合并成一个加载
 */
cc._pjsonLoader = {
    KEY : {
        frames : 0,
        rect : 0, size : 1, offset : 2, rotated : 3, aliases : 4,
        meta : 1,
        image : 0
    },
    _parse : function(data){
        var KEY = this.KEY;
        var frames = {}, meta = data[KEY.meta] ? {image : data[KEY.meta][KEY.image]} : {};
        var tempFrames = data[KEY.frames];
        for (var frameName in tempFrames) {
            var f = tempFrames[frameName];
            var rect = f[KEY.rect];
            var size = f[KEY.size];
            var offset = f[KEY.offset];
            frames[frameName] = {
                rect : {x : rect[0], y : rect[1], width : rect[2], height : rect[3]},
                size : {width : size[0], height : size[1]},
                offset : {x : offset[0], y : offset[1]},
                rotated : f[KEY.rotated],
                aliases : f[KEY.aliases]
            }
        }
        return {_inited : true, frames : frames, meta : meta};
    },
    load : function(realUrl, url, res, cb){
        var self = this, locLoader = cc.loader, cache = locLoader.cache;
        locLoader.loadJson(realUrl, function(err, pkg){
            if(err) return cb(err);
            var dir = cc.path.dirname(url);
            for (var key in pkg) {
                var filePath = cc.path.join(dir, key);
                cache[filePath] = self._parse(pkg[key]);
            }
            cb(null, true);
        });
    }
};
//注册这个插件,前面说过cocos文件加载是通过文件后缀名来判断使用哪个加载器来加载的,所以这里会加载后缀名为.pjson的文件
cc.loader.register(["pjson"], cc._pjsonLoader);

那么如何来生成这个.pjson文件呢?下面是我用nodejs写的一个脚本,可以读取目录下面所有的.plist文件,然后生成一个.pjson文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//pjson.js
var fs = require('fs');
var plist = require('plist');
var pjson = {};
//var src = "res/*.plist";
//node pjson
fs.readdir('./res', function(err, files) {
    if (err) {
        throw err;
    }
    for (var i = files.length - 1; i >= 0; i--) {
        var file = files[i],
            ext = file.split('.')[1];
        if(ext === 'plist'){
            pjson[file] = [];
            pjson[file][0] = {};
            var data = fs.readFileSync('./res/'+file, 'UTF-8');
            var frames = plist.parse(data.toString()).frames;
            var fileName = Object.keys(frames);
            for(var j=0; j< fileName.length; j++){
                var dat = pjson[file][0][fileName[j]] = [];
                var frame = frames[fileName[j]];
                dat[0] = frame.frame.replace(/{|}/g, '').split(',');
                dat[1] = frame.sourceSize.replace(/{|}/g, '').split(',');
                dat[2] = frame.offset.replace(/{|}/g, '').split(',');
                if(frame.rotated)  dat[3] = 1;
            }
        }
    };
    fs.writeFile('./res/plists.pjson', JSON.stringify(pjson), function (err) {
          if (err) throw err;
          console.log('done!');
    });
});

所以你需要在nodejs命令行里面运行:

1
2
3
4
5
//第一次运行请先安装依赖包
npm install plist
//生成pjson文件
node pjson.js

那么如何使用这个文件呢?很简单,你只需要在resource.jsg_resources数组里面删除plist相关的,然后添加这个plists.pjson的路径就可以了,其它不用作任何改动。

图片资源压缩

关于图片和其他静态资源,我用了一个前端自动化任务工具gulp,不懂的可以看我上篇博文:Gulp上手,下面是我针对项目配置的gulp任务gulpfile.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var gulp = require('gulp'),
    imagemin = require('gulp-imagemin'),
    pngquant = require('imagemin-pngquant'),
    cache = require('gulp-cache'),
    autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    rev = require('gulp-rev'),
    revReplace = require('gulp-rev-replace'),
    useref = require('gulp-useref');
var basePath = 'publish/html5/';
gulp.task('image', function () {
    return gulp.src(basePath+'res/**/*.png')
        .pipe(cache(imagemin({
            optimizationLevel: 7,
            use: [pngquant({ quality: '60-80', speed: 1 })]
        })))
        .pipe(gulp.dest(basePath+'res'));
});
gulp.task('style', function () {
    return gulp.src('css/**/*.css')
        .pipe(autoprefixer('Android''BlackBerry''iOS''OperaMobile''ChromeAndroid''FirefoxAndroid''ExplorerMobile'))
        .pipe(minifycss())
        .pipe(gulp.dest(basePath+'css'));
});
gulp.task('static', function () {
    var userefAssets = useref.assets();
    return gulp.src(basePath+'index.html')
        .pipe(userefAssets)
        .pipe(rev()) 
        .pipe(userefAssets.restore())
        .pipe(useref())
        .pipe(revReplace())
        .pipe(gulp.dest(basePath));
});
gulp.task('default', ['image''style''static']);

因为其中用到了对每次生成的js、css文件进行md5命名和合并操作,所以你需要在你的html文件里面作如下配置:

1
2
3
4
5
6
7
8
<!-- build:css css/main.css -->
<link rel="stylesheet" href="css/a.css">
<link rel="stylesheet" href="css/b.css">
<!-- endbuild --><!-- build:js game.js -->
<script src="frameworks/cocos2d-html5/CCBoot.js"></script>
<script src="main.js"></script>
<!-- endbuild -->

整个项目发布

接着我写了一个整个项目的发布脚本build.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var exec = require('child_process').exec;
var buildProcess = exec('cocos compile -p web -m release --advanced', {});
buildProcess.on('close', function () {
    console.log('start gulp task');
    var nextProcess = exec('gulp default', {});
    nextProcess.on('close', function () {
        console.log('gulp task end');
    });
    nextProcess.stdout.setEncoding('utf-8');
    nextProcess.stdout.on('data', function (data) {
        console.log(data);
    });
    nextProcess.stderr.setEncoding('utf-8');
    nextProcess.stderr.on('data', function (data) {
        throw new Error(data);
    });
});
buildProcess.stdout.setEncoding('utf-8');
buildProcess.stdout.on('data', function (data) {
    console.log(data);
});
buildProcess.stderr.setEncoding('utf-8');
buildProcess.stderr.on('data', function (data) {
    throw new Error(data);
});

所以每次项目发布的时候,你只需要打开nodejs命令行,然后执行一下一个命令就完事:

1
node build.js

一些可能对你有用的代码

ajax简单封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
Utils.ArrayProto = Array.prototype;
Utils.slice = Utils.ArrayProto.slice;
Utils.decode = decodeURIComponent;
Utils.encode = encodeURIComponent;
Utils.defaults = function(obj){
    cc.each(Utils.slice.call(arguments, 1), function(o){
        for(var k in o){
            if (obj[k] == null) obj[k] = o[k];
        }    
    });
    return obj;
};
Utils.formData = function(o) {
    var kvps = [], regEx = /%20/g;
    for (var k in o) kvps.push(Utils.encode(k).replace(regEx, "+") + "=" + Utils.encode(o[k].toString()).replace(regEx, "+"));
    return kvps.join('&');
};
Utils.ajax = function(o){
    var xhr = cc.loader.getXMLHttpRequest();
    o = Utils.defaults(o, {type: "GET", data: null, dataType: 'json', progress: null, contentType: "application/x-www-form-urlencoded"});
    //ajax进度的
    //if(o.progress) Utils.Progress.start(o.progress);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4){
            if (xhr.status < 300){
                var res;
                if(o.dataType == 'json'){
                    res = window.JSON ? window.JSON.parse(xhr.responseText): eval(xhr.responseText);
                }else{
                    res = xhr.responseText;
                }
                if(!!res) o.success(res);
                ajax进度的
                //if(o.progress) Utils.Progress.done();
            }else{
                if(o.error) o.error(xhr, xhr.status, xhr.statusText); 
            }
        }
    };
    //是否需要带cookie的跨域
    //if("withCredentials" in xhr) xhr.withCredentials = true;
    var url = o.url, data = null;
    var isPost = o.type == "POST" || o.type == "PUT";
    if( o.data && typeof o.data == 'object' ){
        data = Utils.formData(o.data);
    }
    if (!isPost && data) {
        url += "?" + data;
        data = null;
    }
    xhr.open(o.type, url, true);
    if (isPost) {
        xhr.setRequestHeader("Content-Type", o.contentType);
    }
    xhr.send(data);
    return xhr;
};
Utils.get = function(url, data, success){
    if(cc.isFunction(data)){
        success = data;
        data = null;
    }
    Utils.ajax({url: url, type: "GET", data: data, success: success});
};
Utils.post = function(url, data, success){
    if(cc.isFunction(data)){
        success = data;
        data = null;
    }
    Utils.ajax({url: url, type: "POST", data: data, success: success});
};

用法和jquery的ajax用法类似


LoaderScene重定义

下面是我的loaderScene的一部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
var LoaderScene = cc.Scene.extend({
    _interval : null,
    _label : null,
    _className:"LoaderScene",
    numberSprites: [],
    init : function(){
        var self = this;
        // bg
        var bgLayer = self._bgLayer = new cc.LayerColor(cc.color(216, 216, 216));
        bgLayer.setPosition(cc.visibleRect.bottomLeft);
        self.addChild(bgLayer, 0);
        //you codes
        return true;
    },
    onEnter: function () {
        cc.Node.prototype.onEnter.call(this);
        var loader = (this.loadType == 'resource') ? this._startLoading : this._startAjax;
        this.schedule(loader, 0.3);
    },
    onExit: function () {
        cc.Node.prototype.onExit.call(this);
        //you codes
    },
    initWithResources: function (resources, cb) {
        if(cc.isString(resources))
            resources = [resources];
        this.resources = resources || [];
        this.cb = cb;
        this.loadType = 'resource';
    },
    _startLoading: function () {
        var self = this;
        self.unschedule(self._startLoading);
        var res = self.resources;
        cc.loader.load(res, self._loadProgress.bind(self), function () {
            if (self.cb) self.cb();
        });
    },
    initAjax: function(ajaxSetting){
        this.ajaxSetting = ajaxSetting;
        this.loadType = 'ajax';
    },
    _startAjax: function(){
        var self = this,
            preNum = 0;
        self.unschedule(self._startAjax);
        if(!this.ajaxSetting.progress){
            this.ajaxSetting.progress = function(n){
                n = n.toFixed(2);
                if(preNum === n) return;
                preNum = n;
                self._loadProgress(null, 1, n);
            };
        }
        Utils.ajax(this.ajaxSetting);
    },
    _loadProgress: function(result, count, loadedCount){
        var percent = (loadedCount / count * 100) | 0;
        percent = Math.min(percent, 100);
        console.log(percent);
    }
});
LoaderScene.preload = function(resources, cb){
    var _cc = cc;
    if(!_cc.loaderScene) {
        _cc.loaderScene = new LoaderScene();
        _cc.loaderScene.init();
    }
    _cc.loaderScene.initWithResources(resources, cb);
    cc.director.runScene(_cc.loaderScene);
    return _cc.loaderScene;
};
LoaderScene.ajaxLoad = function(ajaxSetting){
    var _cc = cc;
    if(!_cc.loaderScene) {
        _cc.loaderScene = new LoaderScene();
        _cc.loaderScene.init();
    }
    _cc.loaderScene.initAjax(ajaxSetting);
    cc.director.runScene(_cc.loaderScene);
    return _cc.loaderScene;
};

如果是普通的资源加载调用LoaderScene.preload,如果是ajax加载定调用LoaderScene.ajaxLoad,配合上面的ajax封装来用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
LoaderScene.preload(g_resources, function () {
    console.log('loaded!!!');
});
LoaderScene.ajaxLoad({
    url: 'test.json',
    data: {'name''ddd'}
    success: function(data){
        console.log(data);
    },
    error: function(xhr, status, msg){
    }
});

其中有个显示ajax加载百分比的方法,需要用到一个生成加载百分比的方法,同时要把ajax封装里面的if(o.progress)的注释打开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Utils.noop = function(){};
Utils.clamp = function(n, min, max) {
    if (n < min) return min;
    if (n > max) return max;
    return n;
};
Utils.Progress = {};
//设置最小值、更新频率和速度
Utils.Progress.settings = {
    minimum: 0.1,
    trickle: true,
    trickleRate: 0.3,
    trickleSpeed: 100
};
Utils.Progress.status = null;
Utils.Progress.set = function(n) {
    var progress = Utils.Progress;
    n = Utils.clamp (n, progress.settings.minimum, 1);
    progress.status = n;
    progress.cb(progress.status);
    return this;
};
Utils.Progress.inc = function(amount){
    var progress = Utils.Progress,
        n = progress.status;
    if (!n) {
        return progress.start();
    }else{
        amount = (1 - n) * Utils.clamp(Math.random() * n, 0.1, 0.95);
        n = Utils.clamp(n + amount, 0, 0.994);
        return progress.set(n);
    }
};
Utils.Progress.trickle = function() {
    var progress = Utils.Progress;
    return progress.inc(Math.random() * progress.settings.trickleRate);
};
Utils.Progress.start = function(cb) {
    var progress = Utils.Progress;
    progress.cb = cb || Utils.noop;
    if (!progress.status) progress.set(0);
    var timer = function(){
        if (progress.status === 1) {
            clearTimeout(timer);
            timer = null;
            return;
        }
        progress.trickle();
        work();
    };
    var work = function() {
      setTimeout(timer, progress.settings.trickleSpeed);
    };
    if (progress.settings.trickle) work();
    return this;
};
Utils.Progress.done = function() {
    var progress = Utils.Progress;
    return progress.inc(0.3 + 0.5 * Math.random()).set(1);
};


LoaderLayer的定义

LoaderLayer是在加载的时候在当前场景上面添加一个遮罩层,这里主要是ajax加载的,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
Config.winSize = cc.size(720, 1134);
Config.w =  Config.winSize.width;
Config.h =  Config.winSize.height;
Config.w_2 = Config.w / 2;
Config.h_2 = Config.h / 2;
var LoaderLayer = cc.LayerColor.extend({
    count: 0,
    ctor:function () {
        this._super(cc.color(0, 0, 0, 160));
        var draw = new cc.DrawNode();
        draw.x = Config.w_2 - 50;
        draw.y = 260;
        for(var i=0; i<3; i++){
            draw.drawRect(cc.p(40*i, 0), cc.p(40*i+20, 20), cc.color(181, 181, 181), 1, cc.color(181, 181, 181));
        }
        var draw2 = this.draw = new cc.DrawNode();
        draw2.x = Config.w_2 - 50;
        draw2.y = 260;
        draw2.drawRect(cc.p(0, 0), cc.p(20, 20), cc.color(239, 178, 82), 1, cc.color(239, 178, 82));
        this.addChild(draw);
        this.addChild(draw2);
        this.schedule(this.updateLoad, 0.2);
        return true;
    },
    updateLoad: function(){
        var draw = this.draw;
        draw.clear();
        if(this.count >3){
            this.count = 0;
        }
        for(var i=0; i< this.count; i++){
            draw.drawRect(cc.p(40*i, 0), cc.p(40*i+20, 20), cc.color(239, 178, 82), 1, cc.color(239, 178, 82));
        }
        this.count++;
    },
    onRemove: function(){
        var parent = this.parent;
        cc.eventManager.resumeTarget(parent,true);
        parent.resume();
        parent.removeChild(thistrue);
    }
});
LoaderLayer.preload = function(url, data, cb, parent){
    var loader = cc.LoaderLayer;
    if(!loader) {
        loader = new LoaderLayer();
    }
    //parent.pause();
    cc.eventManager.pauseTarget(parent,true);
    parent.addChild(loader, 99);
       Utils.ajax({
           url: url,
           type: "POST",
           data: data,
           success: function(data){
               loader.onRemove();
               cb(data);
           },
           error: function(xhr, status, msg){
               loader.onRemove();
               alert(msg);
           }
       });
};

使用也很简单:

1
2
//这里默认使用了ajax的post方法,parent一般都是this。
LoaderLayer.preload(url, data, cb, parent);


H5离线缓存

HTML5离线存储Application Cache可以让用户在离线状态下浏览网站内容,缓存之后的内容不会再次从服务器获取,除非缓存文件改变了。如何使用呢?这需要打开你的html文件在里面:

1
2
3
<!DOCTYPE html>
<html lang="en" manifest="manifest.appcache">
<head>...

接着在html文件的同级目录下面新建manifest.appcache,并加入如下内容:

1
2
3
4
5
6
7
8
//manifest.appcache
CACHE MANIFEST#
version 1.0
CACHE:
    css/main.css
    img/test.png
NETWORK:   
    *

关于manifest.appcache文件,基本格式为三段: CACHE, NETWORK,与 FALLBACK,其中NETWORK和FALLBACK为可选项,而第一行CACHE MANIFEST为固定格式,必须写在前面。

CACHE是必须参数,标识出哪些文件需要缓存,可以是相对路径也可以是绝对路径

NETWORK是可选参数,这一部分是要绕过缓存直接读取的文件,可以使用通配符*,也就是说除了上面的cache文件,剩下的文件每次都要重新拉取。

FALLBACK也是可选参数,指定了一个后备页面,当资源无法访问时,浏览器会使用该页面。该段落的每条记录都列出两个 URI—第一个表示资源,第二个表示后备页面。两个 URI 都必须使用相对路径并且与清单文件同源。可以使用通配符。

有了上面两个文件之后还要配置服务器的mime.types类型,找大盘apache的mime.types文件,添加

1
text/cache-manifest .appcache

上面文件配置完成之后,application cache就可以运行了。

更新缓存的方式有三种:

可以修改一下manifest文件,把version改为1.1,然后刷新页面。

js添加一个监听事件:

1
2
3
4
5
window.applicationCache.addEventListener('updateready', function()
{    
    console.log('updateready!');    
    window.applicationCache.swapCache();
});

站点离线存储的容量限制是5M;如果manifest文件,或者内部列举的某一个文件不能正常下载,整个更新过程将视为失败,浏览器继续全部使用老的缓存。

源引:http://cn.cocos2d-x.org/tutorial/show?id=2219

使用Cocos2d-JS开发H5游戏相关推荐

  1. 开发H5游戏引擎的选择:Egret或Laya?

    一.H5游戏开发的引擎介绍 开发H5游戏的引擎有很多,比如egret.laya.cocos-js等等.这里主要是分析的是egret和laya,因为我们团队是从as3转过来的.所以天然地在有as3基因的 ...

  2. JS开发HTML5游戏《神奇的六边形》(四)

    近期出现一款魔性的消除类HTML5游戏<神奇的六边形>,今天我们一起来看看如何通过开源免费的青瓷引擎(www.zuoyouxi.com)来实现这款游戏. (点击图片可进入游戏体验) 因内容 ...

  3. JS开发HTML5游戏《神奇的六边形》(七)

    近期出现一款魔性的消除类HTML5游戏<神奇的六边形>,今天我们一起来看看如何通过开源免费的青瓷引擎(www.zuoyouxi.com)来实现这款游戏. (点击这里可进入游戏体验) 因内容 ...

  4. createjs开发h5游戏: 指尖大冒险

    之前看到一个指尖冒险游戏,觉得挺有意思,就想学习一下怎么实现,毕竟当产经提出类似的需求时,问我等开发可不可以实现的时候,不至于回答不知道. 本文的主要思路,参考的是凹凸实验室的这篇文章:H5游戏开发: ...

  5. android ios能否用cocos2d js开发,cocos2d-x支持c++、js、lua开发

    作者:左文 链接:https://www.zhihu.com/question/21130385/answer/21789568 来源:知乎 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请 ...

  6. 开发H5游戏练手, 黑暗堡垒-炼狱传奇H5 (一) 登陆界面开发

    项目介绍 使用JS lufylegend框架编写RPG游戏练手项目,首先展示几项目的效果图: 项目地址 https://github.com/mangenotwork/HABL-H5 介绍 lufyl ...

  7. cocoscreator开发h5游戏提升加载速度

    有个cocoscreator开发的游戏项目需要导出h5端.开发完毕,导出web-mobile端,然后部署到nginx容器里,问题来了,加载速度有点慢. 解决这个问题的直接方式,就是直接把游戏包放在cd ...

  8. js开发html5游戏,JS开发HTML5游戏《神奇的六边形》(五)

    近期出现一款魔性的消除类HTML5游戏<神奇的六边形>,今天我们一起来看看如何通过开源免费的青瓷引擎(www.zuoyouxi.com)来实现这款游戏. (点击这里可进入游戏体验) 因内容 ...

  9. php开发h5游戏,H5的canvas实现贪吃蛇小游戏

    这次给大家带来H5的canvas实现贪吃蛇小游戏,H5的canvas实现贪吃蛇小游戏注意事项有哪些,下面就是实战案例,一起来看一下. 本文介绍了H5 canvas实现贪吃蛇小游戏,分享给大家,具体如下 ...

最新文章

  1. 【自动驾驶】8. MDC通信架构 + DDS + SOME/IP
  2. 容器学习 之 dockerfile 命令(七)
  3. OpenGL ES 2.0 for iPhone Tutorial
  4. html5的狭义概念,“资源”这一概念,可以有狭义和广义两种理解。狭义的资源是指...
  5. 《软件需求》阅读笔记之一
  6. 【HTML】中国天气天气插件调用
  7. 实训汇编语言设计——内存多字节10进制数相加
  8. Mysql:日志管理:二进制事务日志
  9. Origin 软件去除demo 水印
  10. php读取excel文件_如何用PHP读取excel文件内容、获取单元格数据
  11. NOIP 2017 列队 (线段树动态开点)
  12. 设置开机自动启动程序,需要管理员权限程序
  13. 怎么把荣耀8x的手机备忘录导到电脑里?
  14. iOS环境股票行情报价功能历程
  15. 前沿关注 | 5G和边缘计算将如何改变AR和VR?
  16. PLC可编程控制器控制热水供暖循环系统实训
  17. 新学期,我的目标与展望。(中秋快乐)
  18. 【WLAN】【测试】Linux下aircrack-ng的应用之破解WPA/WPA2、WEP密钥
  19. Python3中pass语句介绍
  20. ArcEngine下纵断面图的绘制

热门文章

  1. 耗时一个月,开发了一款基于腾讯语音自动给视频添加字幕的软件
  2. html语言参考手册,html
  3. python重命名文件excel_运用python实现提取文章title重命名
  4. omnetpp 添加inet_inet 初探
  5. Sqlserver数据库性能优化
  6. linux 搭建智能dns,Linux配置智能DNS服务
  7. php中克隆对象,复制与克隆对象《 PHP 面向对象 》
  8. Vysor无线连接投屏使用
  9. 使用短信管理你自己的短信网关
  10. 代码分享|时频分析时绘制热图进行平滑的代码