(这是本人第一次教程写作之旅,没有什么乱七八糟的套路,就是为了将自己的技术写出来,让大家一同来学习,得以提高自己的水平。由于水平有限,如在过程中发现错误的或者有更好的方法不吝贵读者的评论。2017 撸起袖子干!联系方式:qq: 785861210. )


一、介绍

cocos2d-JS是cocos2d-x (官网 :http://cocos2d-x.org/) 的JavaScript版本,它的前身是cocos2d-html5。在3.0版本以前叫做cocos2d-html5,从3.0版本开始叫做cocos2d-JS。我们知道cocos2d-x支持使用C++、Lua、Javascript来进行程序开发,其所内置的是一个Javascript引擎,通过用C++解析Javascript去执行;而cocos2d-html5是使用Javascript进行开发,最终运行在浏览器里的。那么在v3.0的时候,cocos2d-html5和cocos2d-x JSBinding被合到了一起,称作cocos2d-JS。和cocos2d-html5不同的是,cocos2d-JS开发的程序不仅可以运行在浏览器里,还可以编译运行在Mac OSX, Windows, iOS, Android的原生平台上,真正做到“一次开发,全平台运行”。cocos2d-JS支持cocos2d-x的所有特性并提供更简单易用的Javascript风格API,它还自带了cocos Console,一个用于简化项目创建和不同目标平台编译发布流程的终端工具。


二、2048游戏开发

1. 创建项目 (默认读者已经搭建好一系列的开发环境)

通过如下命令创建一个名为game2048的工程

1
cocos new game2048 -l js

其中,-l表示采用的语言,可选值为cpp、lua以及js。运行命令之后,便可在所在目录看到game2048文件夹。
图1-1为game2048工程的创建过程

image

除创建命令外,cocos console还为工程提供了运行、编译等命令,具体如下:
a. cocos run -p web|ios|android|mac|win32 //运行在指定平台
b. cocos compile -p web|ios|android|mac|win32 -m release//将项目工程打包到指定平台上

2. 项目game2048的目录结构

打开刚刚创建好的game2048工程,可以看到其目录结构图如图1-2所示。

image

3. 创建游戏场景

由于2048游戏相对简单,它只需要一个场景,下面我们来创建一个场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//层
var gameLayer = cc.Layer.extend({
sprite:null,
ctor:function () {
this._super();

return true;
}
});
//场景
var gameScene = cc.Scene.extend({
onEnter:function () {
this._super();
var _gameLayer = new gameLayer();
this.addChild(layer);
}
});

很简单吧!我们只需要创建一个Layer类,然后将它的一个实例加入Scene中,程序运行时main.js会创建一个Scene的实例作为程序入口。

4. 创建卡牌类

我们把2048游戏中的每一个方格看作一个卡片,上面的数字是它的属性。也就是说我们需要4x4=16个卡片类的对象。新建一个CardSprite.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
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
90
91
92
93
94
95
96
97
98
99
var CardSprite = cc.Layer.extend({
number:0,
labelCardNumber:null,
cardColorBG:null,
ctor:function(){
this._super();
},
initCard:function(num, width, height, positionX, positionY){
this.number = num;

this.cardColorBG = new cc.LayerColor(new cc.color(200, 190, 180, 255), width-15, height-15);
this.cardColorBG.setPosition(positionX, positionY);

if(this.number > 0){
this.labelCardNumber = new cc.LabelTTF(this.number,"Arial", 60);
this.labelCardNumber.setPosition(this.cardColorBG.getContentSize().width/2, this.cardColorBG.getContentSize().height/2);
this.labelCardNumber.setTag(8);
this.cardColorBG.addChild(this.labelCardNumber);
}else{
this.labelCardNumber = new cc.LabelTTF(" ","Arial", 60);
this.labelCardNumber.setPosition(this.cardColorBG.getContentSize().width/2, this.cardColorBG.getContentSize().height/2);
this.labelCardNumber.setTag(8);
this.cardColorBG.addChild(this.labelCardNumber);
}
this.addChild(this.cardColorBG);
},
getNumber:function(){
return this.number;
},
setNumber:function(num){
this.number = num;
if(this.number > 0){
this.labelCardNumber.setString(this.number);
}else{
this.labelCardNumber.setString("");
}
// 设置数字大小
if(num >= 0){
this.labelCardNumber.setFontSize(60);
}
if(num >= 16){
this.labelCardNumber.setFontSize(55);
}
if(num >= 128){
this.labelCardNumber.setFontSize(40);
}
if(num >= 1024){
this.labelCardNumber.setFontSize(30)
}
//判断数字的大小来调整颜色
if(this.number == 0){
this.cardColorBG.setColor(new cc.color(200,190,180));
}
if (this.number == 2) {
this.cardColorBG.setColor(new cc.color(240,230,220));
}
if (this.number == 4) {
this.cardColorBG.setColor(new cc.color(240,220,200));
}
if (this.number == 8) {
this.cardColorBG.setColor(new cc.color(240,180,120));
}
if (this.number == 16) {
this.cardColorBG.setColor(new cc.color(240,140,90));
}
if (this.number == 32) {
this.cardColorBG.setColor(new cc.color(240,120,90));
}
if (this.number == 64) {
this.cardColorBG.setColor(new cc.color(240,90,60));
}
if (this.number == 128) {
this.cardColorBG.setColor(new cc.color(240,90,60));
}
if (this.number == 256) {
this.cardColorBG.setColor(new cc.color(240,200,70));
}
if (this.number == 512) {
this.cardColorBG.setColor(new cc.color(240,200,70));
}
if (this.number == 1024) {
this.cardColorBG.setColor(new cc.color(0,130,0));
}
if (this.number == 2048) {
this.cardColorBG.setColor(new cc.color(0,130,0));
}
}
});

// 静态函数
CardSprite.createCardSprite = function(num, width, height, positionX, positionY)
{
var card = new CardSprite();
if(card){
card.initCard(num, width, height, positionX, positionY);
return card;
}
return null;
}

这里将CardSprite类继承自Layer,然后初始化卡片背景和上面的数字,还定义了number的set/get方法。这里需要注意几点:
新建.js文件以后需要在project.json相关位置添加文件路径。
继承都需要有 this._super();,一般写在构造函数ctor:function()中。
注意静态函数的写法,类名.函数名 = function(){}。

5. 初始化游戏界面

主要是在Layer的init()函数中初始化,我们使用一个4x4的二维组来放置在主界面创建16个卡片:

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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
var gameLayer = cc.Layer.extend({
name : 'gameLayer',
firstX:null,
firstY:null,
cardArr:null,
score:0,
scoreLabel:null,
gameOverLayer:null,
gameWinLayer:null,
ctor : function(){
this._super();

this.init();

this.ignoreAnchorPointForPosition(false);

this.initContent();

this.loadListener();
},
init : function(){
var gameBg = new cc.Sprite(res.loadingpage);
gameBg.x = size.width/2;
gameBg.y = size.height/2;
this.addChild(gameBg,0);
},
initContent : function(){
var lazyLayer = this.lazyLayer = new cc.LayerColor(cc.color(180, 170, 160, 255), 560, 560);
lazyLayer.ignoreAnchorPointForPosition(false);
lazyLayer.x = size.width/2;
lazyLayer.y = size.height/2;
this.addChild(lazyLayer);

// 显示分数
var label = new cc.LabelTTF("Score : ", "Arial", 32);
label.fillStyle = cc.color.RED;
label.setAnchorPoint(0,0);
label.x = 100;
label.y = size.height - 100;
this.addChild(label, 10);
this.scoreLabel = new cc.LabelTTF("0", "Arial", 32);
this.scoreLabel.setAnchorPoint(0,0);
this.scoreLabel.x = 100 + 120;
this.scoreLabel.y = size.height - 100;
this.addChild(this.scoreLabel, 10);

// 创建卡片数组
this.cardArr = new Array(4);
for(var i=0; i<4; i++)
{
this.cardArr[i] = new Array(4);
}

var _size = cc.director.getWinSize();
// 初始化卡片数组
this.createCards(_size);

// 随机生成两个数字
this.autoCreateCardNumber();
this.autoCreateCardNumber();
},
createCards:function(size){
var unitSize = (size.height/2 - 80)/4;
var unitSizeY = unitSize - 30;
for(var i=0; i<4; i++){
for(var j=0; j<4; j++){
var card = CardSprite.createCardSprite(0, unitSize, unitSize, unitSize*i + 80, unitSize*j + 330);
this.cardArr[i][j] = card;
this.addChild(card);
}
}
},
autoCreateCardNumber:function(){//生成随机的卡片,数字2/4
while(1){
var i = Math.floor(Math.random()*4); // generate a number between 0 and 3
var j = Math.floor(Math.random()*4);

if (this.cardArr[i][j].getNumber() == 0){
this.cardArr[i][j].setNumber(Math.floor(Math.random()*10) < 1 ? 4 : 2);
break;
}

if (!this.shouldCreateCardNumber()){
break;
}
}
},
shouldCreateCardNumber:function(){// 判断是否需要自动生成新的卡片
var should = false;
for(var i=0; i<4; ++i){
for(var j=0; j<4; ++j){
if (this.cardArr[i][j].getNumber() == 0){
should = true;
break;
}
}
}
return should;
}
});
var gameScene = cc.Scene.extend({
ctor:function(){
this._super();

var layer = new gameLayer();
this.addChild(layer);
layer.x = size.width/2;
layer.y = size.height/2;
}
});

通过调用
this.createCards(size);
初始化所有16个卡片,由于0不显示,所以卡片上都没有数字。然后调用
this.autoCreateCardNumber();
在随机的两个卡片上生成数字,在随机生成的时候我们应该
先判断还有没有空位
,否则有可能会陷入死循环。

初始化的界面如下图:
image

今天就先到这里啦~~~(下节内容定义手势动作,卡片合并,添加分数,判断游戏结束和顺利,界面优化)下节继续,下节再见!