Cocos2d-x 3.x版2048游戏开发

Cocos2d-x 3.x版2048游戏开发


本篇博客给大家介绍如何快速开发2048这样一款休闲游戏,理解整个2048游戏的开发流程,从本篇博客你将可以学习到以下内容:

这里注明一下,本教程来自极客学院,小巫对其中代码进行了解释。

  • 2048游戏的逻辑
  • Cocos2d-x中上下左右手势的识别
  • 游戏中卡片类的创建
  • 添加卡片到游戏中
  • 游戏中的逻辑实现
  • 游戏中随机卡片的生成
  • 游戏结束判断
  • 游戏分数的添加
  • 游戏美化

笔者的开发环境:
Cocos2d-x 3.1.1(开发引擎)
Visual Studio 2012(Win32)
Xcode 5.1(Mac系统下)

理解2048游戏逻辑

2048游戏逻辑并不复杂,4*4的卡片布局,玩家通过手势上下左右滑动来累加卡片数值,直到累加到2048。笔者用一张图说明:
Cocos2d-x 3.x版2048游戏开发_第1张图片
这是一张游戏中的图,在图中同一方向并且数值相同的卡片可以进行叠加,比如128和128在同一行,玩家可以通过向左或向右的手势,对其进行叠加。笔者向右滑动手势,则会变成以下效果:
Cocos2d-x 3.x版2048游戏开发_第2张图片


Cocos2d-x中上下左右手势的识别

玩家在玩2048游戏时,手势是最频繁的操作,所以我们需要对手势所产生的事件进行监听。
在HelloWorldScene.h头文件中声明两个需要实现的监听事件:
// 加入手势识别的事件
	virtual bool onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event);
	virtual void onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event);

声明点击的位置属性
// 点击的元素位置
	int firstX, firstY, endX, endY;


一个是触摸开始的事件,一个是触摸结束的事件。
然后再HelloWorldScene.cpp文件中实现这两个方法:
// 加入手势识别的事件
bool HelloWorld::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event){
	// 触摸点
	Point touchP0 = touch->getLocation();

	firstX = touchP0.x;
	firstY = touchP0.y;


	return true;
}

// 触摸结束触发
void HelloWorld::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event){
    // 获取触摸点位置
	Point touchP0 = touch->getLocation();
	// 获取X轴和Y轴的移动距离
	endX = firstX - touchP0.x;
	endY = firstY - touchP0.y;

	// 判断X轴和Y轴的移动距离,如果X轴的绝对值大于Y轴的绝对值就是左右否则是上下
	if (abs(endX) > abs(endY)){
		// 左右
		if (endX + 5 > 0) {
			// 左边
			if(doLeft()) {
                autoCreateCardNumber();
                doCheckGameOver();
            }

		} else {
			// 右边
			if(doRight()){
                autoCreateCardNumber();
                doCheckGameOver();
            }
		}

	} else {
        // 上下
		if (endY + 5 > 0) {
			// 下边
			if(doDown()) {
                autoCreateCardNumber();
                doCheckGameOver();
            }
		} else {
            // 上边
			if(doUp()) {
                autoCreateCardNumber();
                doCheckGameOver();
            };
		}

	}
}

在HelloWorld的init方法中设置监听器的这两个方法的监听回调:
	// 创建手势识别的事件监听器
	auto touchListener = EventListenerTouchOneByOne::create();
	touchListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
	touchListener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
    // 添加事件监听
	_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);

上面中根据计算开始位置到结束位置X轴和Y轴的移动距离来判断是向左、向右、向上还是向下的方向:
在头文件中声明四个方向的方法:
// 上下左右的方法
	bool doLeft();
	bool doRight();
	bool doUp();
	bool doDown();

然后对其进行实现。这四个方法的逻辑实现放到后面进行讲解。



游戏中卡片类的创建

卡片类是组成2048游戏的基础,4*4的方格的16个位置放置不同的卡片,每个位置为独立的一张卡片。

创建CardSprite.h的头文件:
//
//  CardSprite.h
//  2048Game
//
//  Created by mac on 14-7-16.
//
//

#ifndef ___048Game__CardSprite__
#define ___048Game__CardSprite__

#include "cocos2d.h"

class CardSprite:public cocos2d::Sprite {
    
public:
    // 初始化游戏卡片的方法
    static CardSprite *createCardSprite(int numbers, int width, int height, float CardSpriteX, float CardSpriteY);
    virtual bool init();
    CREATE_FUNC(CardSprite);
    
    // 设置数字
    void setNumber(int num);
    
    // 获取数字
    int getNumber();
private:
    // 显示在界面的数字
    int number;
    void enemyInit(int numbers, int width, int height, float CardSpriteX, float CardSpriteY);
    
    // 定义显示数字的控件
    cocos2d::LabelTTF *labTTFCardNumber;
    
    // 显示的背景
    cocos2d::LayerColor *layerColorBG;
    
    
};

#endif /* defined(___048Game__CardSprite__) */

我们可以从游戏中看到,每张卡片有背景颜色和一个数字,所以我们在头文件需要声明它这两个属性。


CardSprite.cpp中对头文件方法的实现:
//
//  CardSprite.cpp
//  2048Game
//
//  Created by wwj on 14-7-16.
//
//

#include "CardSprite.h"

USING_NS_CC;

// 初始化游戏卡片的方法
CardSprite* CardSprite::createCardSprite(int numbers, int width, int height, float CardSpriteX, float CardSpriteY) {
    // new一个卡片精灵
    CardSprite *enemy = new CardSprite();
    if (enemy && enemy->init()) {
        enemy->autorelease();
        enemy->enemyInit(numbers, width, height, CardSpriteX, CardSpriteY);
        return enemy;
    }
    CC_SAFE_DELETE(enemy);
    return NULL;
}

// 卡片初始化方法
bool CardSprite::init() {
    if (!Sprite::init()) {
        return false;
    }
    return true;
}


// 设置数字
void CardSprite::setNumber(int num) {
    number = num;
    
    // 判断数字的大小来调整字体的大小
    if (number >= 0) {
        labTTFCardNumber->setFontSize(80);
    }
    if (number >= 16) {
        labTTFCardNumber->setFontSize(60);
    }
    if (number >= 128) {
        labTTFCardNumber->setFontSize(40);
    }
    if (number >= 1024) {
        labTTFCardNumber->setFontSize(20);
    }
    
    // 判断数组的大小调整颜色
    if (number == 0) {
        layerColorBG->setColor(cocos2d::Color3B(200, 190, 180));
    }
    if (number == 2) {
        layerColorBG->setColor(cocos2d::Color3B(240, 230, 220));
    }
    if (number == 4) {
        layerColorBG->setColor(cocos2d::Color3B(240, 220, 200));
    }
    if (number == 8) {
        layerColorBG->setColor(cocos2d::Color3B(240, 180, 120));
    }
    if (number == 16) {
        layerColorBG->setColor(cocos2d::Color3B(240, 140, 90));
    }
    if (number == 32) {
        layerColorBG->setColor(cocos2d::Color3B(240, 120, 90));
    }
    if (number == 64) {
        layerColorBG->setColor(cocos2d::Color3B(240, 90, 60));
    }
    if (number == 128) {
        layerColorBG->setColor(cocos2d::Color3B(240, 90, 60));
    }
    if (number == 256) {
        layerColorBG->setColor(cocos2d::Color3B(240, 200, 70));
    }
    if (number == 512) {
        layerColorBG->setColor(cocos2d::Color3B(240, 200, 70));
    }
    if (number == 1024) {
        layerColorBG->setColor(cocos2d::Color3B(0, 130, 0));
    }
    if (number == 2048) {
        layerColorBG->setColor(cocos2d::Color3B(0, 130, 0));
    }
    
    
    // 更新显示的数字
    if (number > 0) {
        labTTFCardNumber->setString(__String::createWithFormat("%i", num)->getCString() );
    } else {
        labTTFCardNumber->setString("");
    }
    
}

// 获取数字
int CardSprite::getNumber() {
    return number;
}


//第1个参数为数字,第2、3个参数为卡片的宽高,第4、5个参数为卡片的位置
void CardSprite::enemyInit(int numbers, int width, int height, float CardSpriteX, float CardSpriteY) {
    // 初始化数字
    number = numbers;
    
    // 加入游戏卡片的背景颜色
    layerColorBG = cocos2d::LayerColor::create(cocos2d::Color4B(200, 190, 180, 255), width - 15, height - 15);
    layerColorBG->setPosition(Point(CardSpriteX, CardSpriteY));
    
    // 判断如果不等于0就显示,否则为空
    if (number > 0) {
        // 加入中间字体
        labTTFCardNumber = cocos2d::LabelTTF::create(__String::createWithFormat("%i", number)->getCString(), "HirakakuProN-W6", 100);
        // 显示卡片数字的位置,这里显示在背景的中间
        labTTFCardNumber->setPosition(Point(layerColorBG->getContentSize().width/2, layerColorBG->getContentSize().height/2));
        // 添加卡片数字到背景中
        layerColorBG->addChild(labTTFCardNumber);
    } else {
        // 加入中间字体
        labTTFCardNumber = cocos2d::LabelTTF::create("", "HirakakuProN-w6", 80);
        labTTFCardNumber->setPosition(Point(layerColorBG->getContentSize().width/2, layerColorBG->getContentSize().height/2));
        layerColorBG->addChild(labTTFCardNumber);
    }
    // 将卡片添加到层
    this->addChild(layerColorBG);
    
}


每张卡片都有相同的宽和高,但数字和位置不同,数字是显示在背景中间的,所以我们需要5个参数来初始化卡片的位置。


添加卡片到游戏中

卡片放置在4*4方框中的不同位置,首先我们需要计算出每张卡片的宽高,然后计算每张卡片所在的位置,最后进行显示。
在HelloWorldScene.h头文件中声明创建卡片的方法:
    // 创建卡片
    void createCardSprite(cocos2d::Size size);

在HelloWorldScene.cpp中实现该方法:
// 创建卡片,size为屏幕大小
void HelloWorld::createCardSprite(cocos2d::Size size) {
    // 求出单元格的宽度和高度,28为左右距离
    int lon = (size.width - 28) / 4;
    
    // 4*4的单元格
    for (int j = 0; j < 4; j++) {
        for (int i = 0; i < 4; i++) {
            // 数字0,宽高相同为lon,lon+j+20为卡片X轴位置,如lon+0+20为第一个卡片的位置,20是每张卡片的间隙,lon+i+20+size.height/6代表的意思是屏幕大小竖方向分了六份,我们这里只放4个位置
           CardSprite *card = CardSprite::createCardSprite(0, lon, lon, lon * j + 10, lon * i + 10 + size.height / 6);
            addChild(card);
            
            // 添加卡片到二维数组中
            cardArr[j][i] = card;
        }
    }
}

笔者对每张卡片显示的位置在代码中进行了说明,读者也可以思考一下为什么要这么做。

游戏中的逻辑实现

2048游戏最重要的部分就是四个方向的逻辑实现,也是开发这个游戏的难点所在。
// 向左
bool HelloWorld::doLeft(){
	log("doLeft");
    
    bool isdo = false;
    // 最外层循环为4*4迭代
    for (int y = 0; y < 4; y++) {
        for (int x = 0; x < 4; x++) {
            
            // 这一层循环为判断卡片是合并还是清空
            for (int x1 = x + 1; x1 < 4; x1++) {
                if (cardArr[x1][y]->getNumber() > 0) {// 有数字
                    if (cardArr[x][y]->getNumber() <= 0) { // 为空
                        // 设置为右边卡片的数值
                        cardArr[x][y]->setNumber(cardArr[x1][y]->getNumber());
                        cardArr[x1][y]->setNumber(0);
                        
                        x--;
                        isdo = true;
                        
                    } else if(cardArr[x][y]->getNumber() == cardArr[x1][y]->getNumber()) {
                        // 当前卡片的值与其比较卡片的值相等,设置为其的2倍
                        cardArr[x][y]->setNumber(cardArr[x][y]->getNumber()*2);
                        cardArr[x1][y]->setNumber(0);
                        
                        
                        // 设置分数
                        score += cardArr[x][y]->getNumber();
                        labelTTFCardNumber->setString(__String::createWithFormat("%i", score)->getCString());
                        isdo = true;
                    }
                    break;// 跳出
                }
            }
        }
    }
    
	return isdo;
}

// 向右
bool HelloWorld::doRight(){
	log("doRight");
    
    bool isdo = false;
    // 最外层循环为4*4迭代
    for (int y = 0; y < 4; y++) {
        for (int x = 3; x >= 0; x--) {
            
            // 循环判断左边卡片往右是合并还是清空
            for (int x1 = x - 1; x1 >= 0; x1-- ) {
                if (cardArr[x1][y]->getNumber() > 0) {
                    if (cardArr[x][y]->getNumber() <= 0) {
                        cardArr[x][y]->setNumber(cardArr[x1][y]->getNumber());
                        
                        x++;
                        isdo = true;
                    }
                    else if (cardArr[x][y]->getNumber() == cardArr[x1][y]->getNumber()) {
                        cardArr[x][y]->setNumber(cardArr[x][y]->getNumber() * 2);
                        cardArr[x1][y]->setNumber(0);
                        
                        // 设置分数
                        score += cardArr[x][y]->getNumber();
                        labelTTFCardNumber->setString(__String::createWithFormat("%i", score)->getCString());

                        isdo = true;
                    }
                    break;
                }

            }
        }
        
    }
    
	return isdo;

}

// 向上
bool HelloWorld::doUp(){
	log("doUp");
	bool isdo = false;
    // 最外层循环为4*4迭代
    for (int x = 0; x < 4; x++) {
        for (int y = 3; y >= 0; y--) {
            
            // 这一层循环为判断卡片是合并还是清空
            for (int y1 = y - 1; y1 >= 0; y1--) {
                if (cardArr[x][y1]->getNumber() > 0) {// 有数字
                    if (cardArr[x][y]->getNumber() <= 0) { // 为空
                        // 设置为右边卡片的数值
                        cardArr[x][y]->setNumber(cardArr[x][y1]->getNumber());
                        cardArr[x][y1]->setNumber(0);
                        
                        y++;
                        isdo = true;
                        
                    } else if(cardArr[x][y]->getNumber() == cardArr[x][y1]->getNumber()) {
                        // 当前卡片的值与其比较卡片的值相等,设置为其的2倍
                        cardArr[x][y]->setNumber(cardArr[x][y]->getNumber()*2);
                        cardArr[x][y1]->setNumber(0);
                        
                        // 设置分数
                        score += cardArr[x][y]->getNumber();
                        labelTTFCardNumber->setString(__String::createWithFormat("%i", score)->getCString());

                        isdo = true;
                    }
                    break;// 跳出
                }
            }
        }
    }
    
	return isdo;

}

// 向下
bool HelloWorld::doDown(){
	log("doDown");
    bool isdo = false;
    // 最外层循环为4*4迭代
    for (int x = 0; x < 4; x++) {
        for (int y = 0; y < 4; y++) {
            
            // 这一层循环为判断卡片是合并还是清空
            for (int y1 = y + 1; y1 < 4; y1++) {
                if (cardArr[x][y1]->getNumber() > 0) {// 有数字
                    if (cardArr[x][y]->getNumber() <= 0) { // 为空
                        // 设置为右边卡片的数值
                        cardArr[x][y]->setNumber(cardArr[x][y1]->getNumber());
                        cardArr[x][y1]->setNumber(0);
                        
                        y--;
                        isdo = true;
                        
                    } else if(cardArr[x][y]->getNumber() == cardArr[x][y1]->getNumber()) {
                        // 当前卡片的值与其比较卡片的值相等,设置为其的2倍
                        cardArr[x][y]->setNumber(cardArr[x][y]->getNumber()*2);
                        cardArr[x][y1]->setNumber(0);
                        
                        
                        // 设置分数
                        score += cardArr[x][y]->getNumber();
                        labelTTFCardNumber->setString(__String::createWithFormat("%i", score)->getCString());

                        isdo = true;
                    }
                    break;// 跳出
                }
            }
        }
    }
    
	return isdo;
}




游戏中随机卡片的生成

在HelloWorldScene.h头文件中声明随机生成卡片的方法:
    // 自动卡片生成
    void autoCreateCardNumber();

在HelloWorldScene.cpp实现该方法:
// 自动生成卡片
void HelloWorld::autoCreateCardNumber() {
    int i = CCRANDOM_0_1() * 4;
    int j = CCRANDOM_0_1() * 4;
    
    // 判断是否已经存在的位置
    if (cardArr[i][j]->getNumber() > 0) {
        // 已存在,递归创建
        autoCreateCardNumber();
    } else {
        // 生成2和4的比例是1:9的概率
        cardArr[i][j]->setNumber(CCRANDOM_0_1() * 10 < 1 ? 4:2);
    }
}

游戏结束判断

在HelloWorldScene.h中声明判断游戏结束的方法:
// 判断游戏是否还能继续运行下去
    void doCheckGameOver();
在HelloWorldScene.cpp中实现该方法:
// 游戏是否还能继续运行下去
void HelloWorld::doCheckGameOver() {
    bool isGameOver = true;
    
    for (int y = 0; y < 4; y++) {
        for (int x = 0; x < 4; x++) {
            if (cardArr[x][y]->getNumber() == 0
                || (x>0 && (cardArr[x][y]->getNumber() == cardArr[x-1][y]->getNumber() ))
                || (x<3 && (cardArr[x][y]->getNumber() == cardArr[x+1][y]->getNumber()))
                || (y<0 && (cardArr[x][y]->getNumber() == cardArr[x][y-1]->getNumber()))
                || (x<3 && (cardArr[x][y]->getNumber() == cardArr[x][y+1]->getNumber()))) {
                isGameOver = false;
            }
        }
    }
    if (isGameOver) {
        // 结束游戏
        Director::getInstance()->replaceScene(TransitionFade::create(1, HelloWorld::createScene()));
    }

}

游戏分数的添加

2048中需要对分数进行统计并显示给玩家,我们在HelloWolrdScene.h文件中声明分数和显示分数的控件:
// 整体游戏的分数
    int score;
    // 定义显示数据的控件
    cocos2d::LabelTTF *labelTTFCardNumber;

在HelloWorldScene.cpp中对分数初始化为0,并显示“分数”的文本:
   score = 0;
    
    
    // 获得屏幕可视大小
    Size visibleSize = Director::getInstance()->getVisibleSize();
    // 加入游戏的背景
    auto layerColorBG = cocos2d::LayerColor::create(cocos2d::Color4B(180,170,160, 255));
    this->addChild(layerColorBG);
    // 在上方加入游戏的分数
    auto labelTTFCardNumberName = LabelTTF::create("分数:","HirakakuProN-W6", 80);
    labelTTFCardNumberName->setPosition(Point(visibleSize.width/5, visibleSize.height-100));
    addChild(labelTTFCardNumberName);
    
    
    labelTTFCardNumber = LabelTTF::create("0", "HirakakuProN-W6", 80);
    labelTTFCardNumber->setPosition(visibleSize.width/2+100, visibleSize.height - 100);
    addChild(labelTTFCardNumber
             );
    

关于分数的计算,在游戏中的逻辑已经实现,具体读者可以查看代码,后面笔者也会提供完整的代码。

游戏美化

2048中我们会发现不同的数字会有不同的背景,然后字体大小也会随数值的变化而变化,实现这个需要通过判断数值的大小来显示不同的颜色背景和设置不同字体大小。

这个功能的实现是在卡片类设置数值的时候实现的,代码逻辑如下:
// 设置数字
void CardSprite::setNumber(int num) {
    number = num;
    
    // 判断数字的大小来调整字体的大小
    if (number >= 0) {
        labTTFCardNumber->setFontSize(80);
    }
    if (number >= 16) {
        labTTFCardNumber->setFontSize(60);
    }
    if (number >= 128) {
        labTTFCardNumber->setFontSize(40);
    }
    if (number >= 1024) {
        labTTFCardNumber->setFontSize(20);
    }
    
    // 判断数组的大小调整颜色
    if (number == 0) {
        layerColorBG->setColor(cocos2d::Color3B(200, 190, 180));
    }
    if (number == 2) {
        layerColorBG->setColor(cocos2d::Color3B(240, 230, 220));
    }
    if (number == 4) {
        layerColorBG->setColor(cocos2d::Color3B(240, 220, 200));
    }
    if (number == 8) {
        layerColorBG->setColor(cocos2d::Color3B(240, 180, 120));
    }
    if (number == 16) {
        layerColorBG->setColor(cocos2d::Color3B(240, 140, 90));
    }
    if (number == 32) {
        layerColorBG->setColor(cocos2d::Color3B(240, 120, 90));
    }
    if (number == 64) {
        layerColorBG->setColor(cocos2d::Color3B(240, 90, 60));
    }
    if (number == 128) {
        layerColorBG->setColor(cocos2d::Color3B(240, 90, 60));
    }
    if (number == 256) {
        layerColorBG->setColor(cocos2d::Color3B(240, 200, 70));
    }
    if (number == 512) {
        layerColorBG->setColor(cocos2d::Color3B(240, 200, 70));
    }
    if (number == 1024) {
        layerColorBG->setColor(cocos2d::Color3B(0, 130, 0));
    }
    if (number == 2048) {
        layerColorBG->setColor(cocos2d::Color3B(0, 130, 0));
    }
    
    
    // 更新显示的数字
    if (number > 0) {
        labTTFCardNumber->setString(__String::createWithFormat("%i", num)->getCString() );
    } else {
        labTTFCardNumber->setString("");
    }
    
}


最后笔者给出所有代码清单:
AppDelegate.h
AppDelegate.cpp
HelloWorldScene.h
HelloWorldScene.cpp
CardSprite.h
CardSprite.cpp

>>>AppDelegate.h
#ifndef  _APP_DELEGATE_H_
#define  _APP_DELEGATE_H_

#include "cocos2d.h"

/**
@brief    The cocos2d Application.

The reason for implement as private inheritance is to hide some interface call by Director.
*/
class  AppDelegate : private cocos2d::Application
{
public:
    AppDelegate();
    virtual ~AppDelegate();

    /**
    @brief    Implement Director and Scene init code here.
    @return true    Initialize success, app continue.
    @return false   Initialize failed, app terminate.
    */
    virtual bool applicationDidFinishLaunching();

    /**
    @brief  The function be called when the application enter background
    @param  the pointer of the application
    */
    virtual void applicationDidEnterBackground();

    /**
    @brief  The function be called when the application enter foreground
    @param  the pointer of the application
    */
    virtual void applicationWillEnterForeground();
};

#endif // _APP_DELEGATE_H_

>>>AppDelegate.cpp
#include "AppDelegate.h"
#include "HelloWorldScene.h"

USING_NS_CC;

AppDelegate::AppDelegate() {

}

AppDelegate::~AppDelegate() 
{
}

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
        glview = GLView::create("My Game");
        director->setOpenGLView(glview);
    }

	glview->setDesignResolutionSize(480, 800, ResolutionPolicy::SHOW_ALL);
    // turn on display FPS
	director->setDisplayStats(false);

    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0 / 60);

    // create a scene. it's an autorelease object
    auto scene = HelloWorld::createScene();

    // run
    director->runWithScene(scene);

    return true;
}

// This function will be called when the app is inactive. When comes a phone call,it's be invoked too
void AppDelegate::applicationDidEnterBackground() {
    Director::getInstance()->stopAnimation();

    // if you use SimpleAudioEngine, it must be pause
    // SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
}

// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground() {
    Director::getInstance()->startAnimation();

    // if you use SimpleAudioEngine, it must resume here
    // SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
}

>>>HelloWorldScene.h
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__

#include "cocos2d.h"
#include "CardSprite.h"

class HelloWorld : public cocos2d::Layer
{
public:
    static cocos2d::Scene* createScene();
    virtual bool init();  
    void menuCloseCallback(cocos2d::Ref* pSender);
    CREATE_FUNC(HelloWorld);

	// 加入手势识别的事件
	virtual bool onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event);
	virtual void onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event);

	// 上下左右的方法
	bool doLeft();
	bool doRight();
	bool doUp();
	bool doDown();
    // 自动卡片生成
    void autoCreateCardNumber();
    // 判断游戏是否还能继续运行下去
    void doCheckGameOver();
    

private:
	// 点击的元素位置
	int firstX, firstY, endX, endY;
    
    // 定义一个二维数组
    CardSprite *cardArr[4][4];
    
    // 创建卡片
    void createCardSprite(cocos2d::Size size);
    
    // 整体游戏的分数
    int score;
    // 定义显示数据的控件
    cocos2d::LabelTTF *labelTTFCardNumber;
};

#endif // __HELLOWORLD_SCENE_H__

>>>HelloWorldScene.cpp
#include "HelloWorldScene.h"
#include "CardSprite.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
	// 'scene' is an autorelease object
	auto scene = Scene::create();

	// 'layer' is an autorelease object
	auto layer = HelloWorld::create();

	// add layer as a child to scene
	scene->addChild(layer);

	// return the scene
	return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
	//////////////////////////////
	// 1. super init first
	if ( !Layer::init() )
	{
		return false;
	}
    
    score = 0;
    
    
    // 获得屏幕可视大小
    Size visibleSize = Director::getInstance()->getVisibleSize();
    // 加入游戏的背景
    auto layerColorBG = cocos2d::LayerColor::create(cocos2d::Color4B(180,170,160, 255));
    this->addChild(layerColorBG);
    // 在上方加入游戏的分数
    auto labelTTFCardNumberName = LabelTTF::create("分数:","HirakakuProN-W6", 80);
    labelTTFCardNumberName->setPosition(Point(visibleSize.width/5, visibleSize.height-100));
    addChild(labelTTFCardNumberName);
    
    
    labelTTFCardNumber = LabelTTF::create("0", "HirakakuProN-W6", 80);
    labelTTFCardNumber->setPosition(visibleSize.width/2+100, visibleSize.height - 100);
    addChild(labelTTFCardNumber
             );
    
   
	// 创建手势识别的事件监听器
	auto touchListener = EventListenerTouchOneByOne::create();
	touchListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
	touchListener->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
    // 添加事件监听
	_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);

    
    // 调用生成卡片的方法
    createCardSprite(visibleSize);
    
    // 调用生成随机数
    autoCreateCardNumber();
    autoCreateCardNumber();
	return true;
}

// 游戏是否还能继续运行下去
void HelloWorld::doCheckGameOver() {
    bool isGameOver = true;
    
    for (int y = 0; y < 4; y++) {
        for (int x = 0; x < 4; x++) {
            if (cardArr[x][y]->getNumber() == 0
                || (x>0 && (cardArr[x][y]->getNumber() == cardArr[x-1][y]->getNumber() ))
                || (x<3 && (cardArr[x][y]->getNumber() == cardArr[x+1][y]->getNumber()))
                || (y<0 && (cardArr[x][y]->getNumber() == cardArr[x][y-1]->getNumber()))
                || (x<3 && (cardArr[x][y]->getNumber() == cardArr[x][y+1]->getNumber()))) {
                isGameOver = false;
            }
        }
    }
    if (isGameOver) {
        // 结束游戏
        Director::getInstance()->replaceScene(TransitionFade::create(1, HelloWorld::createScene()));
    }

}

// 自动生成卡片
void HelloWorld::autoCreateCardNumber() {
    int i = CCRANDOM_0_1() * 4;
    int j = CCRANDOM_0_1() * 4;
    
    // 判断是否已经存在的位置
    if (cardArr[i][j]->getNumber() > 0) {
        // 已存在,递归创建
        autoCreateCardNumber();
    } else {
        // 生成2和4的比例是1:9的概率
        cardArr[i][j]->setNumber(CCRANDOM_0_1() * 10 < 1 ? 4:2);
    }
}

// 创建卡片,size为屏幕大小
void HelloWorld::createCardSprite(cocos2d::Size size) {
    // 求出单元格的宽度和高度,28为左右距离
    int lon = (size.width - 28) / 4;
    
    // 4*4的单元格
    for (int j = 0; j < 4; j++) {
        for (int i = 0; i < 4; i++) {
            // 数字0,宽高相同为lon,lon+j+20为卡片X轴位置,如lon+0+20为第一个卡片的位置,20是每张卡片的间隙,lon+i+20+size.height/6代表的意思是屏幕大小竖方向分了六份,我们这里只放4个位置
           CardSprite *card = CardSprite::createCardSprite(0, lon, lon, lon * j + 10, lon * i + 10 + size.height / 6);
            addChild(card);
            
            // 添加卡片到二维数组中
            cardArr[j][i] = card;
        }
    }
}

// 加入手势识别的事件
bool HelloWorld::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *unused_event){
	// 触摸点
	Point touchP0 = touch->getLocation();

	firstX = touchP0.x;
	firstY = touchP0.y;


	return true;
}

// 触摸结束触发
void HelloWorld::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *unused_event){
    // 获取触摸点位置
	Point touchP0 = touch->getLocation();
	// 获取X轴和Y轴的移动距离
	endX = firstX - touchP0.x;
	endY = firstY - touchP0.y;

	// 判断X轴和Y轴的移动距离,如果X轴的绝对值大于Y轴的绝对值就是左右否则是上下
	if (abs(endX) > abs(endY)){
		// 左右
		if (endX + 5 > 0) {
			// 左边
			if(doLeft()) {
                autoCreateCardNumber();
                doCheckGameOver();
            }

		} else {
			// 右边
			if(doRight()){
                autoCreateCardNumber();
                doCheckGameOver();
            }
		}

	} else {
        // 上下
		if (endY + 5 > 0) {
			// 下边
			if(doDown()) {
                autoCreateCardNumber();
                doCheckGameOver();
            }
		} else {
            // 上边
			if(doUp()) {
                autoCreateCardNumber();
                doCheckGameOver();
            };
		}

	}
}


// 向左
bool HelloWorld::doLeft(){
	log("doLeft");
    
    bool isdo = false;
    // 最外层循环为4*4迭代
    for (int y = 0; y < 4; y++) {
        for (int x = 0; x < 4; x++) {
            
            // 这一层循环为判断卡片是合并还是清空
            for (int x1 = x + 1; x1 < 4; x1++) {
                if (cardArr[x1][y]->getNumber() > 0) {// 有数字
                    if (cardArr[x][y]->getNumber() <= 0) { // 为空
                        // 设置为右边卡片的数值
                        cardArr[x][y]->setNumber(cardArr[x1][y]->getNumber());
                        cardArr[x1][y]->setNumber(0);
                        
                        x--;
                        isdo = true;
                        
                    } else if(cardArr[x][y]->getNumber() == cardArr[x1][y]->getNumber()) {
                        // 当前卡片的值与其比较卡片的值相等,设置为其的2倍
                        cardArr[x][y]->setNumber(cardArr[x][y]->getNumber()*2);
                        cardArr[x1][y]->setNumber(0);
                        
                        
                        // 设置分数
                        score += cardArr[x][y]->getNumber();
                        labelTTFCardNumber->setString(__String::createWithFormat("%i", score)->getCString());
                        isdo = true;
                    }
                    break;// 跳出
                }
            }
        }
    }
    
	return isdo;
}

// 向右
bool HelloWorld::doRight(){
	log("doRight");
    
    bool isdo = false;
    // 最外层循环为4*4迭代
    for (int y = 0; y < 4; y++) {
        for (int x = 3; x >= 0; x--) {
            
            // 循环判断左边卡片往右是合并还是清空
            for (int x1 = x - 1; x1 >= 0; x1-- ) {
                if (cardArr[x1][y]->getNumber() > 0) {
                    if (cardArr[x][y]->getNumber() <= 0) {
                        cardArr[x][y]->setNumber(cardArr[x1][y]->getNumber());
                        
                        x++;
                        isdo = true;
                    }
                    else if (cardArr[x][y]->getNumber() == cardArr[x1][y]->getNumber()) {
                        cardArr[x][y]->setNumber(cardArr[x][y]->getNumber() * 2);
                        cardArr[x1][y]->setNumber(0);
                        
                        // 设置分数
                        score += cardArr[x][y]->getNumber();
                        labelTTFCardNumber->setString(__String::createWithFormat("%i", score)->getCString());

                        isdo = true;
                    }
                    break;
                }

            }
        }
        
    }
    
	return isdo;

}

// 向上
bool HelloWorld::doUp(){
	log("doUp");
	bool isdo = false;
    // 最外层循环为4*4迭代
    for (int x = 0; x < 4; x++) {
        for (int y = 3; y >= 0; y--) {
            
            // 这一层循环为判断卡片是合并还是清空
            for (int y1 = y - 1; y1 >= 0; y1--) {
                if (cardArr[x][y1]->getNumber() > 0) {// 有数字
                    if (cardArr[x][y]->getNumber() <= 0) { // 为空
                        // 设置为右边卡片的数值
                        cardArr[x][y]->setNumber(cardArr[x][y1]->getNumber());
                        cardArr[x][y1]->setNumber(0);
                        
                        y++;
                        isdo = true;
                        
                    } else if(cardArr[x][y]->getNumber() == cardArr[x][y1]->getNumber()) {
                        // 当前卡片的值与其比较卡片的值相等,设置为其的2倍
                        cardArr[x][y]->setNumber(cardArr[x][y]->getNumber()*2);
                        cardArr[x][y1]->setNumber(0);
                        
                        // 设置分数
                        score += cardArr[x][y]->getNumber();
                        labelTTFCardNumber->setString(__String::createWithFormat("%i", score)->getCString());

                        isdo = true;
                    }
                    break;// 跳出
                }
            }
        }
    }
    
	return isdo;

}

// 向下
bool HelloWorld::doDown(){
	log("doDown");
    bool isdo = false;
    // 最外层循环为4*4迭代
    for (int x = 0; x < 4; x++) {
        for (int y = 0; y < 4; y++) {
            
            // 这一层循环为判断卡片是合并还是清空
            for (int y1 = y + 1; y1 < 4; y1++) {
                if (cardArr[x][y1]->getNumber() > 0) {// 有数字
                    if (cardArr[x][y]->getNumber() <= 0) { // 为空
                        // 设置为右边卡片的数值
                        cardArr[x][y]->setNumber(cardArr[x][y1]->getNumber());
                        cardArr[x][y1]->setNumber(0);
                        
                        y--;
                        isdo = true;
                        
                    } else if(cardArr[x][y]->getNumber() == cardArr[x][y1]->getNumber()) {
                        // 当前卡片的值与其比较卡片的值相等,设置为其的2倍
                        cardArr[x][y]->setNumber(cardArr[x][y]->getNumber()*2);
                        cardArr[x][y1]->setNumber(0);
                        
                        
                        // 设置分数
                        score += cardArr[x][y]->getNumber();
                        labelTTFCardNumber->setString(__String::createWithFormat("%i", score)->getCString());

                        isdo = true;
                    }
                    break;// 跳出
                }
            }
        }
    }
    
	return isdo;
}



void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT)
	MessageBox("You pressed the close button. Windows Store Apps do not implement a close button.","Alert");
	return;
#endif

	Director::getInstance()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
	exit(0);
#endif
}

>>>CardSprite.h
//
//  CardSprite.h
//  2048Game
//
//  Created by mac on 14-7-16.
//
//

#ifndef ___048Game__CardSprite__
#define ___048Game__CardSprite__

#include "cocos2d.h"

class CardSprite:public cocos2d::Sprite {
    
public:
    // 初始化游戏卡片的方法
    static CardSprite *createCardSprite(int numbers, int width, int height, float CardSpriteX, float CardSpriteY);
    virtual bool init();
    CREATE_FUNC(CardSprite);
    
    // 设置数字
    void setNumber(int num);
    
    // 获取数字
    int getNumber();
private:
    // 显示在界面的数字
    int number;
    void enemyInit(int numbers, int width, int height, float CardSpriteX, float CardSpriteY);
    
    // 定义显示数字的控件
    cocos2d::LabelTTF *labTTFCardNumber;
    
    // 显示的背景
    cocos2d::LayerColor *layerColorBG;
    
    
};

#endif /* defined(___048Game__CardSprite__) */

>>>CardSprite.cpp
//
//  CardSprite.cpp
//  2048Game
//
//  Created by wwj on 14-7-16.
//
//

#include "CardSprite.h"

USING_NS_CC;

// 初始化游戏卡片的方法
CardSprite* CardSprite::createCardSprite(int numbers, int width, int height, float CardSpriteX, float CardSpriteY) {
    // new一个卡片精灵
    CardSprite *enemy = new CardSprite();
    if (enemy && enemy->init()) {
        enemy->autorelease();
        enemy->enemyInit(numbers, width, height, CardSpriteX, CardSpriteY);
        return enemy;
    }
    CC_SAFE_DELETE(enemy);
    return NULL;
}

// 卡片初始化方法
bool CardSprite::init() {
    if (!Sprite::init()) {
        return false;
    }
    return true;
}


// 设置数字
void CardSprite::setNumber(int num) {
    number = num;
    
    // 判断数字的大小来调整字体的大小
    if (number >= 0) {
        labTTFCardNumber->setFontSize(80);
    }
    if (number >= 16) {
        labTTFCardNumber->setFontSize(60);
    }
    if (number >= 128) {
        labTTFCardNumber->setFontSize(40);
    }
    if (number >= 1024) {
        labTTFCardNumber->setFontSize(20);
    }
    
    // 判断数组的大小调整颜色
    if (number == 0) {
        layerColorBG->setColor(cocos2d::Color3B(200, 190, 180));
    }
    if (number == 2) {
        layerColorBG->setColor(cocos2d::Color3B(240, 230, 220));
    }
    if (number == 4) {
        layerColorBG->setColor(cocos2d::Color3B(240, 220, 200));
    }
    if (number == 8) {
        layerColorBG->setColor(cocos2d::Color3B(240, 180, 120));
    }
    if (number == 16) {
        layerColorBG->setColor(cocos2d::Color3B(240, 140, 90));
    }
    if (number == 32) {
        layerColorBG->setColor(cocos2d::Color3B(240, 120, 90));
    }
    if (number == 64) {
        layerColorBG->setColor(cocos2d::Color3B(240, 90, 60));
    }
    if (number == 128) {
        layerColorBG->setColor(cocos2d::Color3B(240, 90, 60));
    }
    if (number == 256) {
        layerColorBG->setColor(cocos2d::Color3B(240, 200, 70));
    }
    if (number == 512) {
        layerColorBG->setColor(cocos2d::Color3B(240, 200, 70));
    }
    if (number == 1024) {
        layerColorBG->setColor(cocos2d::Color3B(0, 130, 0));
    }
    if (number == 2048) {
        layerColorBG->setColor(cocos2d::Color3B(0, 130, 0));
    }
    
    
    // 更新显示的数字
    if (number > 0) {
        labTTFCardNumber->setString(__String::createWithFormat("%i", num)->getCString() );
    } else {
        labTTFCardNumber->setString("");
    }
    
}

// 获取数字
int CardSprite::getNumber() {
    return number;
}


//第1个参数为数字,第2、3个参数为卡片的宽高,第4、5个参数为卡片的位置
void CardSprite::enemyInit(int numbers, int width, int height, float CardSpriteX, float CardSpriteY) {
    // 初始化数字
    number = numbers;
    
    // 加入游戏卡片的背景颜色
    layerColorBG = cocos2d::LayerColor::create(cocos2d::Color4B(200, 190, 180, 255), width - 15, height - 15);
    layerColorBG->setPosition(Point(CardSpriteX, CardSpriteY));
    
    // 判断如果不等于0就显示,否则为空
    if (number > 0) {
        // 加入中间字体
        labTTFCardNumber = cocos2d::LabelTTF::create(__String::createWithFormat("%i", number)->getCString(), "HirakakuProN-W6", 100);
        // 显示卡片数字的位置,这里显示在背景的中间
        labTTFCardNumber->setPosition(Point(layerColorBG->getContentSize().width/2, layerColorBG->getContentSize().height/2));
        // 添加卡片数字到背景中
        layerColorBG->addChild(labTTFCardNumber);
    } else {
        // 加入中间字体
        labTTFCardNumber = cocos2d::LabelTTF::create("", "HirakakuProN-w6", 80);
        labTTFCardNumber->setPosition(Point(layerColorBG->getContentSize().width/2, layerColorBG->getContentSize().height/2));
        layerColorBG->addChild(labTTFCardNumber);
    }
    // 将卡片添加到层
    this->addChild(layerColorBG);
    
}


最后,骚年们就不要向我要源码了,所有的代码在这里均有实现,只要把这些代码集成到你的项目中去就可以实现跟笔者一样的效果,希望各位能好好体会2048开发的流程。




你可能感兴趣的:(Cocos2d-x 3.x版2048游戏开发)