• Cocos2d-html5入门之2048游戏


    一、介绍

    Cocos2d-JS是Cocos2d-x的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,一个用于简化项目创建和不同目标平台编译发布流程的终端工具。


    二、Cocos2d-html5目录结构

    为了快速入门,所以决定开发一个最近比较热门的2048小游戏。当然,首先需要去Cocos2d-x官网下载Cocos2d-html5-v2.2.3引擎,下载后解压就行了。Cocos2d-html5的目录结构如下:

    • cocos2d:游戏引擎的主要文件
    • extensions:包含一些扩展的功能,且支持自定义扩展
    • external:包含box2d和chipmunk这两个物理引擎
    • HelloHTML5World:一个最简单的HelloWorld项目
    • samples:官网例子和几款小游戏
    • template:项目模板

    我们可以通过拷贝 HelloHTML5World 或 template中的项目模板,然后基于它编写自己的代码程序。

    这里使用WebStorm作为IDE进行游戏开发,WebStorm有非常强大的智能补全功能,提高了开发效率。还能与 Google Chrome 浏览器配合,完成实时编辑和调试。只需要下载WebStorm然后载入引擎目录稍微配置一下就可以了,不知道怎么做的可以看看快速搭建 Cocos2d-HTML5 开发调试环境这篇博文。


    三、2048游戏开发


    1、创建项目

    Cocos2d-html5中创建项目很简单,只需要拷贝HelloHTML5World项目(当然你也可以复制template下的文件夹),然后重命名就行了。

    • res:资源文件夹(图片、音频等)
    • src:js脚本存放目录(我们自己的项目源码目录)
    • index.html:主页面
    • build.xml:文件的引用,包括引擎库文件和自己写的文件
    • cocos2d.js:定义了程序运行需要的一些参数,如 是否显示FPS,是否加载扩展库等
    • main.js:定义程序的入口,初始化导演类等等。

    我们需要在src文件夹里编写我们自己的代码,首先我们删除myApp.js中的所有代码,开始写我们自己的2048游戏。


    2、创建游戏场景

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

    // 层
    var Helloworld = cc.Layer.extend({
        init:function () {
            this._super();
            return true;
        }
    });
    
    // 场景
    var HelloWorldScene = cc.Scene.extend({
        onEnter:function () {
            this._super();
            var layer = new Helloworld();
            layer.init();
            this.addChild(layer);
        }
    });

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


    3、创建卡片类

    我们把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 = cc.LayerColor.create(new cc.Color4B(200, 190, 180, 255), width-15, height-15);
            this.cardColorBG.setPosition(positionX, positionY);
            // 添加数字,大于0显示,否则不显示
            if(this.number > 0)
            {
                this.labelCardNumber = cc.LabelTTF.create(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 = cc.LabelTTF.create("","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("");
            }
        }
    });
    
    // 静态函数
    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文件以后需要在build.xml和cocos2d.js相关位置添加文件路径。
    • 继承都需要有 this._super();,一般写在构造函数ctor:function()中。
    • 注意静态函数的写法,类名.函数名 = function(){}

    4、初始化界面

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

    var Helloworld = cc.Layer.extend({
        cardArr:null,  // 存放4x4=16个卡片
        // 初始化
        init:function () {
            this._super();
            var size = cc.Director.getInstance().getWinSize();
    
            // 主界面背景层->设置颜色
            var lazyLayer = cc.LayerColor.create(new cc.Color4B(180, 170, 160, 255) , null, null);
            this.addChild(lazyLayer);
    
            // 创建卡片数组
            this.cardArr = new Array(4);
            for(var i=0; i<4; i++) {
                this.cardArr[i] = new Array(4);
            }
    
            // 初始化所有卡片,数字为0,不显示
            this.createCards(size);
    
            // 开始的时候,随机生成两个数字
            this.autoCreateCardNumber();
            this.autoCreateCardNumber();
    
            return true;
        },
    
        // 初始化卡片数组*******************************
        createCards:function(size)
        {
            var unitSize = (size.height - 80)/4;
            for(var i=0; i<4; i++) {
                for(var j=0; j<4; j++) {
                    var card = CardSprite.createCardSprite(0, unitSize, unitSize, unitSize*i + 210, unitSize*j + 30);
                    this.cardArr[i][j] = card;
                    this.addChild(card);
                }
            }
        },
    
        // 随机生成新的2或4的卡片****************
        autoCreateCardNumber:function()
        {
            while(1) {
                var i = Math.floor(Math.random()*4);  // 随机生成0~3
                var j = Math.floor(Math.random()*4);
    
                if (this.cardArr[i][j].getNumber() == 0) {   
                    // 4与2生成的概率比为1:9
                    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;
        }
    });
    通过调用this.createCards(size);初始化所有16个卡片,由于0不显示,所以卡片上都没有数字。然后调用this.autoCreateCardNumber();在随机的两个卡片上生成数字,在随机生成的时候我们应该先判断还有没有空位,否则有可能会陷入死循环。
    初始化的界面如下图:


    5、定义手势动作

    我们需要通过触摸滑动来操作游戏,所以就需要定义上下左右的手势动作。这就要用到引擎的触摸响应机制,Cocos2d-html5与Cocos2d-x一样,有多点触控单点触控。默认情况下是多点触控,要使用单点触控,我们要使用addTargetedDelegate()方法设置代理。
    那么如何判断上下左右呢?当然是根据起始触摸点和结束触摸点的坐标变化:

    var Helloworld = cc.Layer.extend({
        firstX:null,
        firstY:null,
        cardArr:null,
    
        init:function () {
            this._super();
            // 设置单点触摸
            cc.Director.getInstance().getTouchDispatcher()._addTargetedDelegate(this, 0, true);
            ...
            // 打开触摸
            this.setTouchEnabled(true);
            return true;
        },
        // onTouchBegan函数,需要返回true
        onTouchBegan:function (touch, event) {
            var touchPoint = touch.getLocation();
            this.firstX = touchPoint.x;
            this.firstY = touchPoint.y;
            return true;
        },
        // onTouchEnded函数
        onTouchEnded:function (touch, event) {
            var touchPoint = touch.getLocation();
            var offsetX = this.firstX - touchPoint.x;
            var offsetY = this.firstY - touchPoint.y;
    
            if(Math.abs(offsetX) > Math.abs(offsetY)) {
                if(offsetX > 5) {
                    this.doLeft();
                    //this.doCheckGameOver();
                    //this.setScore(this.score);
                }
                else if(offsetX < -5) {
                    this.doRight();
                    //this.doCheckGameOver();
                    //this.setScore(this.score);
                }
            }
            else {
                if(offsetY > 5) {
                    this.doDown();
                    //this.doCheckGameOver();
                    //this.setScore(this.score);
                }
                else if(offsetY < -5) {
                    this.doUp();
                    //this.doCheckGameOver();
                    //this.setScore(this.score);
                }
            }
        },
        // ...other methods
    });

    6、卡片合并

    游戏2048主要玩法就是通过合并相同数字的卡片以达到2048。通过手势动作往一个方向进行合并。我们的思路就是根据手势方向,遍历每一行或每一列,将在这个方向上相邻(中间没有其他数字)且数字相同的卡片合并加倍。

    // 向上***************************************
    doUp:function(){
    var isdo = false;
    for (var x=0; x<4; ++x)
    {
        for (var y=3; y>=0; --y)
        {
            for (var y1=y-1; y1>=0; --y1)
            {
                if (this.cardArr[x][y1].getNumber() > 0)
                {
                    if (this.cardArr[x][y].getNumber() <= 0)
                    {
                        this.cardArr[x][y].setNumber(this.cardArr[x][y1].getNumber());
                        this.cardArr[x][y1].setNumber(0);
                        ++y;
                        isdo = true;
                    }
                    else if(this.cardArr[x][y].getNumber() == this.cardArr[x][y1].getNumber())
                    {
                        this.cardArr[x][y].setNumber(this.cardArr[x][y].getNumber()*2);
                        this.cardArr[x][y1].setNumber(0);
                        //this.score += this.cardArr[x][y].getNumber(); 
                        isdo = true;
                    }
                    break;
                }
            }
        }
    }
    return isdo;
    },
    // 向下***************************************
    doDown:function() {
    var isdo = false;
    for (var x=0; x<4; ++x)
    {
        for (var y=0; y<4; ++y)
        {
            for (var y1=y+1; y1<4; ++y1)
            {
                if (this.cardArr[x][y1].getNumber() > 0)
                {
                    if (this.cardArr[x][y].getNumber() <= 0)
                    {
                        this.cardArr[x][y].setNumber(this.cardArr[x][y1].getNumber());
                        this.cardArr[x][y1].setNumber(0);
                        --y;
                        isdo = true;
                    }
                    else if(this.cardArr[x][y].getNumber() == this.cardArr[x][y1].getNumber())
                    {
                        this.cardArr[x][y].setNumber(this.cardArr[x][y].getNumber()*2);
                        this.cardArr[x][y1].setNumber(0);
                        //this.score += this.cardArr[x][y].getNumber(); 
                        isdo = true;
                    }
                    break;
                }
            }
        }
    }
    return isdo;
    },
    // 向左***************************************
    doLeft:function() {
    var isdo = false;
    for (var y=0; y<4; ++y)
    {
        for(var x=0; x<4; ++x)
        {
            for(var x1=x+1; x1<4; ++x1)
            {
                if(this.cardArr[x1][y].getNumber() > 0)
                {
                    if(this.cardArr[x][y].getNumber() <= 0)
                    {
                        this.cardArr[x][y].setNumber(this.cardArr[x1][y].getNumber());
                        this.cardArr[x1][y].setNumber(0);
                        --x;
                        isdo = true;
                    }
                    else if(this.cardArr[x][y].getNumber() == this.cardArr[x1][y].getNumber())
                    {
                        this.cardArr[x][y].setNumber(this.cardArr[x][y].getNumber()*2);
                        this.cardArr[x1][y].setNumber(0);
                        //this.score += this.cardArr[x][y].getNumber();
                        isdo = true;
                    }
                    break;
                }
            }
        }
    }
    return isdo;
    },
    // 向右***************************************
    doRight:function() {
    var isdo = false;
    for (var y = 0; y < 4; ++y)
    {
        for (var x = 3; x >= 0; --x)
        {
            for (var x1 = x - 1; x1 >= 0; --x1)
            {
                if (this.cardArr[x1][y].getNumber() > 0)
                {
                    if (this.cardArr[x][y].getNumber() <= 0)
                    {
                        this.cardArr[x][y].setNumber(this.cardArr[x1][y].getNumber());
                        this.cardArr[x1][y].setNumber(0);
                        ++x;
                        isdo = true;
                    }else if(this.cardArr[x][y].getNumber() == this.cardArr[x1][y].getNumber())
                    {
                        this.cardArr[x][y].setNumber(this.cardArr[x][y].getNumber()*2);
                        this.cardArr[x1][y].setNumber(0);
                        //this.score += this.cardArr[x][y].getNumber(); 
                        isdo = true;
                    }
                    break;
                }
            }
        }
    }
    return isdo;
    }

    7、添加分数

    添加两个变量:

    score:0,  // 分数
    scoreLabel:null,  // 显示分数的控件
    然后初始化分数显示:

    // 显示分数
    var label = cc.LabelTTF.create("Score : ", "Arial", 20);
    label.setAnchorPoint(0,0);
    label.setPosition(size.width/2 - 50, size.height-50);
    this.addChild(label);
    
    this.scoreLabel = cc.LabelTTF.create("0", "Arial", 20);
    this.scoreLabel.setAnchorPoint(0,0);
    this.scoreLabel.setPosition(size.width/2 + 20, size.height-50);
    this.addChild(this.scoreLabel);
    卡片合并的时候要增加分数,然后更新分数显示:

    // 更新分数 -> this.setScore(this.score);
    setScore:function(s)
    {
    	this.scoreLabel.setString(s);
    }

    8、判断游戏结束和胜利

    每一次卡片合并操作后,我们都需要判断游戏是否胜利或者结束。利用五个条件判断游戏是否还能够继续:(1)还有空卡片 (2)还可以向右滑 (3)还可以向左滑 (4)还可以向上滑 (5)还可以向下滑。只要以上条件满足一个,游戏就可以再继续。否则,游戏不能再继续了。判断胜利则是看卡片中有没有数字达到2048。

    // 判断游戏是否结束*******************************
    doCheckGameOver:function()
    {
    	var size = cc.Director.getInstance().getWinSize();
    
    	var isGameOver = true;
    	for(var y=0; y<4; ++y)
    	{
    	    for(var x=0; x<4; ++x)
    	    {
    		if(this.cardArr[x][y].getNumber() == 0 ||
    		    (x>0&&(this.cardArr[x][y].getNumber() == this.cardArr[x-1][y].getNumber())) ||
    		    (x<3&&(this.cardArr[x][y].getNumber() == this.cardArr[x+1][y].getNumber())) ||
    		    (y>0&&(this.cardArr[x][y].getNumber() == this.cardArr[x][y-1].getNumber())) ||
    		    (y<3&&(this.cardArr[x][y].getNumber() == this.cardArr[x][y+1].getNumber())))
    		{
    		    isGameOver = false;
    		}
    	    }
    	}
    
    	if (isGameOver)  {     // if the game is over
    	    console.log("The Game Is Over!");
    	    cc.Director.getInstance().replaceScene(cc.TransitionFade.create(1, new HelloWorldScene()));
    	}
    	else {
    	    if (this.shouldCreateCardNumber()) {
    		this.autoCreateCardNumber();
    	    }
    	}
    
    	if(this.isWin())  {     // if win
    	    cc.Director.getInstance().replaceScene(cc.TransitionFade.create(1, new HelloWorldScene()));
    	}
    }
    
    // 判断是否胜利**********************
    isWin:function() {
    var Win = false;
    	for (var i=0; i<4; ++i) {
    	    for(var j=0; j<4; ++j) {
    		if (this.cardArr[i][j].getNumber() == 2048) {
    		    Win = true;
    		    break;
    		}
    	    }
    	}
    	return Win;
    }

    9、界面优化

    在一个卡片里面,当数字变成两位数、三位数的时候,就需要调整一下数字的大小,在CardSprite的setNumber方法中添加代码:

    // 设置数字大小
    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)
    }
    我们还需要给不同的数字设置不同的卡片颜色,同样在setNumber方法中添加代码:

    //判断数字的大小来调整颜色
    if(this.number == 0){
        this.cardColorBG.setColor(new cc.Color3B(200,190,180));
    }
    if (this.number == 2) {
        this.cardColorBG.setColor(new cc.Color3B(240,230,220));
    }
    if (this.number == 4) {
        this.cardColorBG.setColor(new cc.Color3B(240,220,200));
    }
    if (this.number == 8) {
        this.cardColorBG.setColor(new cc.Color3B(240,180,120));
    }
    if (this.number == 16) {
        this.cardColorBG.setColor(new cc.Color3B(240,140,90));
    }
    if (this.number == 32) {
        this.cardColorBG.setColor(new cc.Color3B(240,120,90));
    }
    if (this.number == 64) {
        this.cardColorBG.setColor(new cc.Color3B(240,90,60));
    }
    if (this.number == 128) {
        this.cardColorBG.setColor(new cc.Color3B(240,90,60));
    }
    if (this.number == 256) {
        this.cardColorBG.setColor(new cc.Color3B(240,200,70));
    }
    if (this.number == 512) {
        this.cardColorBG.setColor(new cc.Color3B(240,200,70));
    }
    if (this.number == 1024) {
        this.cardColorBG.setColor(new cc.Color3B(0,130,0));
    }
    if (this.number == 2048) {
        this.cardColorBG.setColor(new cc.Color3B(0,130,0));
    }

    10、最终游戏截图



    结语

    采用Cocos2D-HTML5引擎进行游戏开发,开发效率更高,加上丰富的工具集支持、实时的各种调试手段和云合作开发手段,更加直接的效果调校方式,完全没有开发环境依赖,各种优势的叠加,可以大幅度降低开发成本,提升游戏的上架速度。

    如果觉得这样还不够,我们可以使用Cocos2d-JS-v3.0,同一份JavaScript游戏代码不用修改或者小范围修改,配合Cocos2D-X JavaScript-Binding就能以Hybrid的方式直接作为本地应用发布,也可以编译到Android、iOS等移动平台。

    通过Cocos2D-HTML5或Cocos2d-JS,开发者能以一个更快、更惬意的方式实现自己的想法。




    参考:http://blog.csdn.net/column/details/love2048.html




  • 相关阅读:
    android在开发过程中的数据库存储位置
    JSONArray的初始化的形式
    Android中asset文件夹与raw文件夹的区别深入解析(转)
    Android 建立Menu选单&&onOptionsItemSelected (转)
    onItemLongClick+onCreateContextMenu实现长按ListItem弹出不同菜单
    thrift框架
    64 位 win7 使用PLSQL Developer(转)
    Java-->服务器的响应(Servlet--doGet&doPost)
    JAVA printWriter中write()和println()区别
    JavaWeb学习总结(六)—HttpServletResponse
  • 原文地址:https://www.cnblogs.com/songlee/p/5738140.html
Copyright © 2020-2023  润新知