爆红游戏《2048》的cocos2d-js版本

一张图告诉你2048有多火

小3的悲剧

在每个阶段手机里面都会有一两款让人爱不释手的游戏存在,无论是在公车上、床铺上、马桶上还是在睡觉前、吃饭前、总会有让你拿出手机打开它的欲望。大部分人被Flappy bird给蹂躏的体无完肤,生活不能自理。在这危难降临的时刻,一款名为小3传奇的数字益智游戏出现在我们的视野中,游戏既简单又复杂,既单一又千变万化,简直就让人停不下来!

但故事的发展是这样的,小3传奇的两位开发者花了近一年半时间才开发出这个游戏的核心玩法,却在其上架appstore后仅21天就被《1024》抄了过去。而《2048》则更为迅速,19岁的Gabriele Cirulli只用了一个星期改编前两者游戏,却获得最大的成功,而小3却不被人知;这类游戏从发布到现在,不管是app store还是android市场,下载量已经远远超越了千万,网上各种版本的都有;

CocosEditor开源版

笔者历时一个晚上,终于完成了cocos2d-js开源版本,编码虽易,创意不易,且行且珍惜;

此版本包含了网上流行的各种版本。包括原版,朝代版,金庸版,星座,豪车等等近10个版本,代码开源,希望读者基于开源代码做出各种版本,供全名娱乐;

运行demo需要配置好CocosEditor,暂不支持其他工具。demo是跨平台的,可移植运行android,ios,html5网页等,代码是基于javascript语言,cocos2d-x游戏引擎,CocosEditor手游开发工具完成的。

本文共两篇,第一篇分析原版,第二篇分析各种版本,本代码仅供参考,有更好的实现方法可以评论中探讨;

已发布上线apk演示效果

几个小时开发,一天审核上线,这就是CocosEditor开发游戏的速度

360应用市场(2048 聚合版):http://zhushou.360.cn/detail/index/soft_id/1634607?recrefer=SE_D_2048%20聚合

CocosEditor版源代码下载:

cocos2d-js源代码请到集中营下载:http://blog.makeapp.co/?p=523
http://blog.makeapp.co/?p=523
github版本管理:https://github.com/makeapp/cocoseditor-2048

不同平台下的效果图:

windows

html5网页

android平台(各种主题版本)

代码分析:
1初始化;进入游戏,初始化4*4表格,并随机产生两个2;

二维数组this.tables表格循环存入数据

#random1, random2 ,random11, random22四个随机数可以确定两个2的xy位置;
#方法newNumber里面,根据位置i,j和级别num可以确定一个新的数字;创建背景cell和cell上面的数字标签cellLabel;并根据num确定是否显示cellLabel;最后给cell关联一个data数据;特别说明这里的number:num不是精灵上面的数字而是精灵的级别,比如number=11 则数字是1024

MainLayer.prototype.onEnter = function () {
    //version
    this.versionNum = indexVersions;
    this.indexVersion = VERSIONS;
    this.title.setString(this.indexVersion.name + "目标:" + this.indexVersion.array + "");

    var random1 = getRandom(4);
    var random2 = getRandom(4);
    while (random1 == random2) {
        random2 = getRandom(4);
    }

    var random11 = getRandom(4);
    var random22 = getRandom(4);

    this.tables = new Array(4);
    for (var i = 0; i < 4; i++) {
        var sprites = new Array(4);
        for (var j = 0; j < 4; j++) {
            if (i == random1 && j == random11) {
                sprites = this.newNumber(i, j, 1);
            } else if (i == random2 && j == random22) {
                sprites = this.newNumber(i, j, 1);
            } else {
                sprites = this.newNumber(i, j, 0);
            }
        }
        this.tables = sprites;
    }

    this.totalScore = 0;

};

MainLayer.prototype.newNumber = function (i, j, num) {
    var cell = cc.MySprite.create(this.rootNode, "5.png", this.getPosition(i, j), 1);
    var cellLabel = cc.MySprite.createLabel(cell, "");

    if (num > 0) {
        cell.setColor(COLOR);
        cellLabel.setVisible(true);
        cellLabel.setString(this.indexVersion.array);
        cellLabel.setFontSize(this.indexVersion.labelFontSize);
    } else {
        cellLabel.setVisible(false);
    }
    cell.data = {col: i, row: j, numberLabel: cellLabel, number: num};
    return cell;
};
```

*
*2 四个方向算法;玩游戏的时候触摸四个方向,表格就向四个方向合并靠拢leftCombineNumber,rightCombineNumber,downCombineNumber,upCombineNumber,四个方法函数的算法都是一样的,我只分析一个leftCombineNumber;
第一步 相同数据叠加 :
#j从左到右变大,i从下到上变大;也就初始位置是左下角;
#如果该单元格级别不是空背景  cell.data.number != 0 ;
#从它的右边开始var k = i + 1;  循环遍历while (k < 4)  {k++};
#如果遍历到单元格级别也不是空背景 if (nextCell.data.number != 0) 遍历结束 k = 4; break;;
#而且如果发现两个单元的级别一样if (cell.data.number == nextCell.data.number) 
#级别数据number刷新变化
cell.data.number += 1;
nextCell.data.number = 0;

第二步 填充空数据;
#同理第一步,如果是空背景if (cell.data.number == 0),也是循环遍历while (k < 4)  {k++};
#如果遍历到单元格级别不是空背景 if (nextCell.data.number != 0)  ,空背景获得该单元格的数据,而该单元格则设为空背景;
cell.data.number = nextCell.data.number;
nextCell.data.number = 0;

//direction left
MainLayer.prototype.leftCombineNumber = function () {
    for (var j = 0; j < 4; j++) {
        for (var i = 0; i < 4; i++) {
            var cell = this.tables;
            if (cell.data.number != 0) {
                var k = i + 1;
                while (k < 4) {
                    var nextCell = this.tables;
                    if (nextCell.data.number != 0) {
                        if (cell.data.number == nextCell.data.number) {
                            cell.data.number += 1;
                            nextCell.data.number = 0;
                            this.totalScore += SCORES;
                        }
                        k = 4;
                        break;
                    }
                    k++;
                }
            }
        }
    }

    for (j = 0; j < 4; j++) {
        for (i = 0; i < 4; i++) {
            cell = this.tables;
            if (cell.data.number == 0) {
                k = i + 1;
                while (k < 4) {
                    nextCell = this.tables;
                    if (nextCell.data.number != 0) {
                        cell.data.number = nextCell.data.number;
                        nextCell.data.number = 0;
                        k = 4;
                    }
                    k++;
                }
            }
        }
    }

    this.refreshNumber();
};

//direction right
MainLayer.prototype.rightCombineNumber = function () {
    for (var j = 0; j < 4; j++) {
        for (var i = 3; i >= 0; i--) {
            var cell = this.tables;
            if (cell.data.number != 0) {
                var k = i - 1;
                while (k >= 0) {
                    var nextCell = this.tables;
                    if (nextCell.data.number != 0) {
                        if (cell.data.number == nextCell.data.number) {
                            cell.data.number += 1;
                            nextCell.data.number = 0;
                            this.totalScore += SCORES;
                        }
                        k = -1;
                        break;
                    }
                    k--;
                }
            }
        }
    }

    for (j = 0; j < 4; j++) {
        for (i = 3; i >= 0; i--) {
            cell = this.tables;
            if (cell.data.number == 0) {
                k = i - 1;
                while (k >= 0) {
                    nextCell = this.tables;
                    if (nextCell.data.number != 0) {
                        cell.data.number = nextCell.data.number;
                        nextCell.data.number = 0;
                        k = -1;
                    }
                    k--;
                }
            }
        }
    }

    this.refreshNumber();
};

MainLayer.prototype.downCombineNumber = function () {
    for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            var cell = this.tables;
            if (cell.data.number != 0) {
                var k = j + 1;
                while (k < 4) {
                    var nextCell = this.tables;
                    if (nextCell.data.number != 0) {
                        if (cell.data.number == nextCell.data.number) {
                            cell.data.number += 1;
                            nextCell.data.number = 0;
                            this.totalScore += SCORES;
                        }
                        k = 4;
                        break;
                    }
                    k++;
                }
            }
        }
    }

    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            cell = this.tables;
            if (cell.data.number == 0) {
                k = j + 1;
                while (k < 4) {
                    nextCell = this.tables;
                    if (nextCell.data.number != 0) {
                        cell.data.number = nextCell.data.number;
                        nextCell.data.number = 0;
                        k = 4;
                    }
                    k++;
                }
            }
        }
    }

    this.refreshNumber();
};

//touch up
MainLayer.prototype.upCombineNumber = function () {
    for (var i = 0; i < 4; i++) {
        for (var j = 3; j >= 0; j--) {
            var cell = this.tables;
            if (cell.data.number != 0) {
                var k = j - 1;
                while (k >= 0) {
                    var nextCell = this.tables;
                    if (nextCell.data.number != 0) {
                        if (cell.data.number == nextCell.data.number) {
                            cell.data.number += 1;
                            nextCell.data.number = 0;
                            this.totalScore += SCORES;
                        }
                        k = -1;
                        break;
                    }
                    k--;
                }
            }
        }
    }

    for (i = 0; i < 4; i++) {
        for (j = 3; j >= 0; j--) {
            cell = this.tables;
            if (cell.data.number == 0) {
                k = j - 1;
                while (k >= 0) {
                    nextCell = this.tables;
                    if (nextCell.data.number != 0) {
                        cell.data.number = nextCell.data.number;
                        nextCell.data.number = 0;
                        k = -1;
                    }
                    k--;
                }
            }
        }
    }

    this.refreshNumber();
};
```


3 刷新数据和颜色;
上面的算法完成了,只是该精灵的data里面的数据发生了变化,但视觉上没有任何变化,所以需要刷新数据和颜色
#新建一个空背景数组emptyCellList;
#又是循环二维数组this.tables
#得到单元格的文字标签label,和级别cellNumber
#如果不是空背景cellNumber!=0,label显示和设置文字内容和大小,同时如果检测到是最高级别,游戏成功结束
#如果是空背景,label隐藏 emptyCellList添加该元素emptyCellList.push(cell);;

#得到一个emptyCellList后,如果发现该数组大小为空,也就无法再产生一个数字2了,游戏over;
#而如果数组大小不是空,随机取一个位置randomCell,设置数据等级为0,数字为2,并播放缩放动画runAction;

MainLayer.prototype.refreshNumber = function () {
    var emptyCellList = ];
    for (var i = 0; i < 4; i++) {
        var numbers = " ";
        for (var j = 0; j < 4; j++) {
            var cell = this.tables;
            var label = cell.data.numberLabel;
            var cellNumber = cell.data.number;
            if (cellNumber != 0) {
                cell.setColor(COLOR);
                label.setString(this.indexVersion.array + " ");
                label.setFontSize(this.indexVersion.labelFontSize);
                label.setVisible(true);
                if (cellNumber == (this.indexVersion.array.length - 1)) {
                    //check success
                    var toast = cc.Toast.create(this.rootNode, "成功到达:" + this.indexVersion.array, 2);
                    toast.setColor(cc.c3b(255, 0, 0));
                    this.rootNode.scheduleOnce(function () {
                        cc.BuilderReader.runScene("", "MainLayer");
                    }, 2)
                }
            } else {
                cell.setColor(COLOR);
                label.setVisible(false);
                emptyCellList.push(cell);
            }
            numbers += "  " + cellNumber;
        }
        cc.log("numbers==" + numbers);
    }

    //score
    this.scoreLabel.setString("分数:" + this.totalScore);

    if (emptyCellList.length < 1) {
        //check fail
        var toast = cc.Toast.create(this.rootNode, "失败!", 2);
        toast.setColor(cc.c3b(255, 0, 0));
        this.rootNode.scheduleOnce(function () {
            cc.BuilderReader.runScene("", "MainLayer");
        }, 2)
    } else {
        //create random cell
        var randomCell = emptyCellList;
        randomCell.data.number = 1;
        randomCell.data.numberLabel.setVisible(true);
        randomCell.data.numberLabel.setString(VERSIONS.array + "");
        randomCell.data.numberLabel.setFontSize(this.indexVersion.labelFontSize);
        randomCell.setColor(COLOR);
        randomCell.runAction(cc.Sequence.create(cc.ScaleTo.create(0, 0.8), cc.ScaleTo.create(0.5, 1)));

    }

};
```


4 触摸检测 两个触摸点this.pEnded this.pBegan 根据x y确定方向,再根据距离确定左右和上下;
MainLayer.prototype.onTouchesEnded = function (touches, event) {
    this.pEnded = touches.getLocation();
    if (this.pBegan) {
        if (this.pEnded.x - this.pBegan.x > 50) {
            this.rightCombineNumber();
        }

        else if (this.pEnded.x - this.pBegan.x < -50) {
            this.leftCombineNumber();
        }

        else if (this.pEnded.y - this.pBegan.y > 50) {
            this.upCombineNumber();
        }

        else if (this.pEnded.y - this.pBegan.y < -50) {
            this.downCombineNumber();
        }
    }
};
```

思路很清晰简单,游戏却是简约不简单。***
***
1赞

运行demo需要配置CocosEditor

CocosEditor,它是开发跨平台的手机游戏工具,运行window/mac系统上,javascript脚本语言,基于cocos2d-x跨平台游戏引擎, 集合代码编辑,场景设计,动画制作,字体设计,还有粒子,物理系统,地图等等的,而且调试方便,和实时模拟;
CocosEditor 下载,介绍和教程:http://blog.csdn.net/touchsnow/article/details/19070665;
CocosEditor官方博客:http://blog.makeapp.co/;

《2048》 Cocos2d-x-js版来袭,cpp,lua版还不快快现身!!!:2::2::2:

感谢楼主分享!:801:

不错,我去学习下CocosEditor

您的文章已被推荐到CocoaChina首页热门文章精选:2:

快出 其他语言的:880:

其实之前自己有实现过C++版本,基于Cocosd-x 2.1.4版本,纯代码写的;因为游戏有bug,所以有时间贴出来不防高手帮忙看下……

等了这么久 cocos2d-x版本终于现身 http://www.cocoachina.com/bbs/read.php?tid=198237,cocos2d-lua版本何在?:8:

CE什么时候开源?

估计不会开源了 工具开源意义不大 官方的cocostudio也没开源 对不:883: