cocos2d-js 系列教程之2048开发一

(这是本人第一次教程写作之旅,没有什么乱七八糟的套路,就是为了将自己的技术写出来,让大家一同来学习,得以提高自己的水平。由于水平有限,如在过程中发现错误的或者有更好的方法不吝贵读者的评论。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的工程

cocos new game2048 -l js

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

image
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
image

3. 创建游戏场景

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

//层
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文件:

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个卡片:

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
image

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容