【cocos2d-js 3.0】制作2048

2048在一个4X4的方阵中,玩家需要滑动上面的数字,如果俩个数字相邻并且相等,则相加,需要达到2048,方可胜利。

因为在浏览器操作,所以此例的操作方法为:键盘上的w,s,a,d代表上下左右,也可用小键盘左边的上下左右键。

下面给一张游戏截图,也可以点击这里进行试玩:)

IDE:webstorm

好,现在开始讲解我的制作过程。

首先,新建一个项目,名字叫mini2048。我是用终端cocos new Project出来的,所以要删除一些不必要的代码,并且添加一些游戏相关的资源。

因为这个游戏主要是用做demo并且分享制作过程,所以美术啥的也没好好搞,就一个标题的2048字体用了外部的fnt资源。大家粗略看看就行~

添加游戏背景

        /*一个灰色的背景和白色的线组成的方格*/
        var background = new cc.LayerColor(cc.color(180,170,160,255),size.width,size.height); //灰色背景
        this.addChild(background,0);
        var draw = cc.DrawNode.create();
        this.addChild(draw, 1);
        for(var index=0; index<5; index++){
            draw.drawSegment(cc.p(40+index*60, 20), cc.p(40+index*60, 260), 1, cc.color(255, 255, 255, 255));  //竖线
            draw.drawSegment(cc.p(40, 20+index*60), cc.p(280, 20+index*60), 1, cc.color(255, 255, 255, 255));  //横线
        }

再继续添加一些界面元素,标题,得分,重新游戏什么的

        //标题
        var title = cc.LabelBMFont.create("2 0 4 8", res.LabelFont_Fnt);
        title.x = size.width/2;
        title.y = size.height-40;
        this.addChild(title,1);

        //得分
        var scoreLabelText = cc.LabelTTF.create("score","Scissor Cuts",20);
        scoreLabelText.x = 60;
        scoreLabelText.y = size.height*4/5;
        scoreLabelText.setColor(cc.color(0,0,0,255));
        this.addChild(scoreLabelText,1);
        this.scoreLabel = cc.LabelTTF.create("0","Scissor Cuts",20);
        this.scoreLabel.x = 60;
        this.scoreLabel.y = size.height*4/5-25;
        this.scoreLabel.setColor(cc.color(0,0,0));
        this.addChild(this.scoreLabel,1);

        //重新游戏
        cc.MenuItemFont.setFontName("Arial");
        var restartItem = cc.MenuItemFont.create("restart", this.restartGame, this);
        restartItem.setColor(cc.color(22,100,255));
        restartItem.x = size.width - 60;
        restartItem.y = size.height*4/5-12.5;
        var restartMenu = cc.Menu.create(restartItem);
        restartMenu.x = 0;
        restartMenu.y = 0;
        this.addChild(restartMenu,1);

        //结束label
        var gameOverLabel = cc.LabelTTF.create("  游戏结束! ","Scissor Cuts",50);
        gameOverLabel.setColor(cc.color(255,0,255));
        gameOverLabel.x = size.width/2;
        gameOverLabel.y = size.height/2+60;
        gameOverLabel.visible = false;
        this.addChild(gameOverLabel,11,5);

        //通关label
        var passTheGameLabel = cc.LabelTTF.create(" 恭喜通关!","Scissor Cuts",50);
        passTheGameLabel.setColor(cc.color(255,255,0));
        passTheGameLabel.x = size.width/2;
        passTheGameLabel.y = size.height/2+60;
        passTheGameLabel.visible = false;
        this.addChild(passTheGameLabel,11,6);

随后添加键盘事件来相应w,s,a,d和小键盘旁上下左右键的操作。

 

        if (cc.sys.capabilities.hasOwnProperty('keyboard'))
            cc.eventManager.addListener({
                event: cc.EventListener.KEYBOARD,
                onKeyReleased:function (key, event) {
                    if(key==[cc.KEY.w] || key==[cc.KEY.up] ){
                        event.getCurrentTarget().slideUp();               //向上滑
                    }
                    else if(key==[cc.KEY.a]  || key==[cc.KEY.left] ){
                        event.getCurrentTarget().slideLeft();             //向左滑
                    }
                    else if(key==[cc.KEY.d]  || key==[cc.KEY.right] ){
                        event.getCurrentTarget().slideRight();            //向右滑
                    }
                    else if(key==[cc.KEY.s]  || key==[cc.KEY.down] ){
                        event.getCurrentTarget().slideDown();             //向下滑
                    }
                }
            }, this);

 

然后制作Card类,继承cc.Sprite,用来做游戏中的卡片。我这里的逻辑是这样的,游戏开始前,初始化4X4的16张卡片,并且每张卡片在对应的位置,并且卡片数字都设置为0,

当游戏运作的时候,不移动卡片,只改变对应卡片上的数字,并且对改变后的数字进行判断,如果是0,则隐藏数字,如果是>0的值,则显示数字并根据值来对数字进行美化。

        this.backgroundPic = new cc.LayerColor(cc.color(0, 0, 255, 111), 50, 50); //卡片底色
        this.labelText = cc.LabelTTF.create("0", "Trebuchet MS", 21);             //卡片数字
        this.backgroundPic.ignoreAnchorPointForPosition(false);
        this.addChild(this.backgroundPic, 0);
        this.addChild(this.labelText,1);

并且给Card类添加一些方法,如设置卡片数字,获取卡片数字的值,对卡片数字大小和卡片背景进行美化等等,因为这些比较简单,所以就不贴码了,尽量避免此博文显得过于冗长。

下面介绍下2048的算法,我觉得也是这部游戏唯一的难点~

因为游戏是上下左右进行滑动,所以只要知道一个方向上是如何运作的,那么也就可以举一反三了,譬如向左滑动这个操作,再对其进行剖析,

我们会发现算法对4排上的数字都是进行同样的操作,所以我们只研究一行数字的算法就可以了。其他的只要循环3次就行。

 

/*因为算法比较简单,所以我就用注释解释下*/
for(var y=0; y<4; y++){
    for(var x=0; x<4; x++){
        for(var xRight=x+1; xRight<4; xRight++){
            if(game2048array[xRight][y].getCardNumber() != 0){                       //game2048array是存放16张卡片的数组,y代表某一行,如果对应卡片右边的卡片数值为0,则对下一个右边的卡片进行循环判断
                if(game2048array[x][y].getCardNumber() == 0){                        //如果对应卡片右边的值不为0,并且对应卡片的值为0,则将对应卡片的值改为其右边卡片的值
                    game2048array[x][y].setCardNumber(game2048array[xRight][y].getCardNumber());
                    game2048array[xRight][y].setCardNumber(0);                       //然后设置其右边卡片的值为0
                    this.zeroCardIndex.removeZeroCard(x,y);                          //this.zeroCardIndex是一个存放值为0的卡片的数组,后面会用到
                    this.zeroCardIndex.push({coordX:xRight,coordY:y});
                    x--;
                }
                else if(game2048array[x][y].getCardNumber() == game2048array[xRight][y].getCardNumber()){
                    game2048array[x][y].setCardNumber(game2048array[x][y].getCardNumber()*2);      //如果对应卡片右边的值不为0,并且对应卡片的值和其右边卡片的值相等,则对应卡片的值位置为原来的两倍
                    game2048array[xRight][y].setCardNumber(0);                                     //同样设置其右边卡片的值为0
                    if(game2048array[x][y].getCardNumber()==2048){                                 //进行判断,如果对应卡片的值达到2048,则游戏通关
                        this.gameStop = true;
                        this.passGameLabelAppear();                         //通关
                    }
                    else{
                        this.zeroCardIndex.push({coordX:xRight,coordY:y});
                    }
                }
                break;        //如果对应卡片其右边的卡片值非0,并且对应卡片的值和其右边卡片的值不相等,那么跳出此轮循环,继续判断下一个卡片
            }
        }
    }
}

 

OK,2048的核心算法已经充分展示,接着,每滑动一次就需要添加一张值为2或者4的卡片。我是这么做的,把游戏中非0卡片的位置都记录到一个数组this.zeroCardIndex中,然后每一次滑动后在此数组中随机取一个元素,

并且在元素上对应的位置信息上添加2或者4的数字。看代码

    addOneCard:function(){
        var cardIndex = parseInt(Math.random()*this.zeroCardIndex.length);                                     //随机获取一张值为0的卡片
        var card = game2048array[this.zeroCardIndex[cardIndex].coordX][this.zeroCardIndex[cardIndex].coordY];  //获取对应位置信息上的卡片
        card.setCardNumber(Math.random()<0.2?4:2);                                                             //给卡片设置数字 20%的几率会随机到4的卡片
        this.zeroCardIndex.splice(cardIndex, 1);                                                               //从数组中删除此张卡片
    },

最后,对游戏“死亡”进行判定,首先对卡片0的数组的长度(this.zeroCardIndex.lenght)进行判断,如果为0,则判断横向或者竖向的数字,俩俩是不是都不相同,如果是,则表示游戏结束。

 

    isGameOver:function(){
        for(var x=0;x<4;x++){
            for(var y=0;y<4;y++){
                if(x<3 && (game2048array[x][y].getCardNumber()==game2048array[x+1][y].getCardNumber())){    //横向俩俩判断
                    return false;
                }
                if(y<3 && (game2048array[x][y].getCardNumber()==game2048array[x][y+1].getCardNumber())){    //竖向俩俩判断
                    return false;
                }
            }
        }
        return true;
    }

 

OK!至此,2048的基本游戏逻辑都介绍完毕,剩下的譬如分数累加什么的简单逻辑就不罗列了,第一次写博客教程,大家多多包涵,有不合适的地方尽管吐槽砸墙!

希望每一个游戏人都能实现自己的游戏梦!:)

posted @ 2014-07-16 12:01  奋斗中的小鸟  阅读(2002)  评论(0编辑  收藏  举报