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。笔者用一张图说明:
这是一张游戏中的图。在图中同一方向而且数值同样的卡片能够进行叠加。比方128和128在同一行。玩家能够通过向左或向右的手势,对其进行叠加。笔者向右滑动手势,则会变成下面效果:

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游戏开发的更多相关文章

  1. cocos2d-x游戏开发实战原创视频讲座系列1之2048游戏开发

     cocos2d-x游戏开发实战原创视频讲座系列1之2048游戏开发 的产生 视持续更新中.... 视频存放地址例如以下:http://ipd.pps.tv/user/1058663622     ...

  2. 一个用 C 语言写的迷你版 2048 游戏,仅仅有 500个字符

    Jay Chan 用 C 语言写的一个迷你版 2048 游戏,仅仅有 487 个字符. 来围观吧 M[16],X=16,W,k;main(){T(system("stty cbreak&qu ...

  3. Weex 版扫雷游戏开发

    扫雷是一个喜闻乐见的小游戏,今天在看 Weex 文档的过程中,无意中发现用 Weex 完全可以开发一个扫雷出来.当然这个扫雷和 Windows 那个有一点差距,不过麻雀虽小五脏俱全,随机布雷.自动挖雷 ...

  4. 微信小程序开发(5) 2048游戏

    在这篇微信小程序开发教程中,我们将介绍如何使用微信小程序开发2048小游戏. 本文主要分为两个部分,小程序主体部分及小游戏页面部分 一.小程序主体部分 一个小程序主体部分由三个文件组成,必须放在项目的 ...

  5. HTML5游戏开发 PDF扫描版​

    很多从事Web前端开发的人对HTML总有些不满,比如需要手动检查和设计很多格式代码,不仅容易出错,而且存在大量重复.好在HTML5让我们看到了曙光.作为下一代Web开发标准,HTML5成为主流的日子已 ...

  6. Cocos2d-html5入门之2048游戏

    一.介绍 Cocos2d-JS是Cocos2d-x的Javascript版本,它的前身是Cocos2d-html5.在3.0版本以前叫做Cocos2d-html5,从3.0版本开始叫做Cocos2d- ...

  7. C++游戏开发需要阅读的书籍

    如果要自学游戏程序开发的话,可以看看下面的,呵呵. 游戏开发资料(PDF书都是中文版的,非英文,很多是本人自己扫描制作,从未网上发布过,所以独家啦):  1.Gamebryo 2.2游戏引擎(盛大.腾 ...

  8. iOS cocos2d 2游戏开发实战(第3版)书评

    2013是游戏爆发的一年,手游用户也是飞速暴增.虽然自己不做游戏,但也是时刻了解手机应用开发的新动向.看到CSDN的"写书评得技术图书赢下载分"活动,就申请了一本<iOS c ...

  9. 从零開始开发Android版2048 (一)初始化界面

    自学Android一个月多了,一直在工作之余零零散散地看一些东西.感觉经常使用的东西都有些了解了,可是一開始写代码总会出各种奇葩的问题.感觉还是代码写得太少.这样继续杂乱地学习下去进度也太慢了,并且学 ...

随机推荐

  1. 设计模式-观察者模式(Observer Pattern)

    本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 观察者模式简述 观察者模式的使用非常广泛,常用于建立起一种多对一的关系,该模式一定会包含两个角色 ...

  2. 简化的INSERT语句

    INSERT语句中也并不需要我们指定表中的所有列,比如在插入数据的时候某些字段没有值,我们可以忽略这些字段.下面我们插入一条没有备注信息的数据: INSERT INTO T_Person(FAge,F ...

  3. 【带修莫队】bzoj2120 数颜色

    块大小为n2/3. 把询问和修改分开. 每次两个询问之间的修改进行暴力转移,如果修改在上一次询问的区间里,就会对当前状态形成影响. 好慢. #include<cstdio> #includ ...

  4. Scala实战高手****第15课:Scala类型参数编程实战及Spark源码鉴赏

    1.Scala的类和方法.函数都可以是泛型 2.上界:表示泛型的类型必须是某种类型或者其类型的子类,语法:<: ,对类型进行限定 3.下界:表示泛型的类型必须是某种类型或者其类型的父类,语法:& ...

  5. Linux下date常用格式化输出

    date "+%Y-%m-%d" --date "+%H:%M:%S" ::date "+%Y-%m-%d %H:%M:%S" -- ::d ...

  6. HTML5本地缓存localStorage和sessionStorage的操作方法收集

    说明: Web Storage 包含如下两种机制: sessionStorage 为每一个给定的源(given origin)维持一个独立的存储区域,该存储区域在页面会话期间可用(即只要浏览器处于打开 ...

  7. EF 通用数据层父类方法小结

    MSSql 数据库 数据层 父类 增删改查: using System;using System.Collections.Generic;using System.Data;using System. ...

  8. [PATCH] ARM: add dtbImage.<dt> and dtbuImage.<dt> rules

    转载: http://permalink.gmane.org/gmane.linux.kbuild.devel/8755 This rules are useful for appended devi ...

  9. Web服务器在外网能裸奔多久?

      很多时候我们轻易地把Web服务器暴露在公网上,查看一下访问日志,可以看到会收到大量的攻击请求,这个是网站开通后几个小时收到的请求: 1.  探测服务器信息 在上线一分钟,收到OPTION请求探测. ...

  10. Gradle项目学习 & HttpAsyncClient学习 & CountDownLatch学习

    装了Intellij,就是装了Gradle. 新建一个Gradle项目.然后下面这个页面要勾选上面两项: Use auto-import和Create directories for empty co ...