1.游戏场景:
只有一个场景,进入即开始玩耍!!!
2.逻辑分析:
在屏幕上显示4*4=16的网格布局
在游戏开始时,在网格中的随机位置产生一个元素快(数值2)
用户通过触摸屏幕可以移动该块向上、下、左、右移动
在屏幕上的所有块将按滑动的方向移动,直到移动到定点或被其他块挡住
如果在移动过程中,出现了相同数字的块,则相加产生一个新的块,前方旧的块消失
如果有3块数字相同则消除最前方的2个块
如果有四个块数字相同,则前方2个块相加,后面两个块相加
如果有4个块数字两两相邻的相同,则两两相加,产生新的2个块
每移动一次,在屏幕上将会在空白区域产生一个新的块(2或4)
如果屏幕上没有了空白区域:
判断4*4范围内是否有相邻的数字相同的块,不产生新块,等待用户移动
如果在4*4范围内没有相邻的数字相同的块,GameOver
AppDelegate.h/.cpp略
========================================================================
SplashScene.h
#include "cocos2d.h"
#include <iostream>
#include "GameDefine.h"
#include "GameScene.h"
using namespace cocos2d;
class Splash:public Layer
{
public:
//init初始化函数
bool init();
//create函数
CREATE_FUNC(Splash);
//静态函数包装当前图层
static Scene *createScene();
//一秒钟跳转到游戏场景
void jumpToGame(float t);
};
===========================================================================
SplashScene,cpp
#include "SplashScene.h"
//声音引擎头文件
#include"SimpleAudioEngine.h"
using namespace CocosDenshion;
//init初始化函数
bool Splash::init()
{
if(!Layer::init())
{
return false;
}
//显示游戏名称
auto labelGame=Label::createWithBMFont("futura-48.fnt","2048");
labelGame->setPosition(Point(GAME_SCREEN_WIDTH/2,2*GAME_SCREEN_HEIGHT/3 ));
this->addChild(labelGame);
labelGame->setScale(1.5);
//显示制作单位
auto labelGameVec=Label::createWithBMFont("futura-48.fnt","Soldoros");
labelGameVec->setPosition(Point(GAME_SCREEN_WIDTH/2,GAME_SCREEN_HEIGHT/4 ));
this->addChild(labelGameVec);
labelGameVec->setScale(0.8);
auto labelGameVec2=Label::createWithBMFont("futura-48.fnt","2014.8.30");
labelGameVec2->setPosition(Point(GAME_SCREEN_WIDTH/2,GAME_SCREEN_HEIGHT/6 ));
this->addChild(labelGameVec2);
labelGameVec2->setScale(0.8);
//计划任务 一秒钟跳转到游戏场景
this->scheduleOnce(schedule_selector(Splash::jumpToGame),2);
return true;
}
//静态函数包装当前图层
Scene* Splash::createScene()
{
auto scene=Scene::create();
auto layer=Splash::create();
scene->addChild(layer);
return scene;
}
//跳转到游戏界面
void Splash::jumpToGame(float t)
{
//提前 音效加载 避免以后加载会卡
SimpleAudioEngine::getInstance()->preloadEffect("17.wav");
SimpleAudioEngine::getInstance()->preloadEffect("2.wav");
auto scene=Game::createScene();
Director::getInstance()->replaceScene(TransitionProgressOutIn::create(0.5,scene));
}
================================================================================
MoveTiled.h
//初始化数字块
#ifndef Game_GameTtiled_h
#define Game_GameTtiled_h
#include"iostream"
#include"cocos2d.h"
#include"GameDefine.h"
using namespace cocos2d;
class MoveTiled:public Node
{
public:
//显示的行数
int m_row;
//显示的列数
int m_col;
//显示的数字
int m_number;
//根据坐标显示
void showAt(int r,int w);
//移动块到该位置
void moveTo(int r,int c);
//使块的值加倍
void doubleNumber();
CREATE_FUNC(MoveTiled);
bool init();
};
#endif
=====================================================================
MoveTiled.cpp
#include"MoveTiled.h"
#include"math.h"
//显示在当前位置
void MoveTiled::showAt(int r,int w)
{
moveTo(r,w);
//动画效果 产生新块的时候 块缩小放大 恢复
auto bk = this->getChildByTag(110);
bk->runAction(Sequence::create(ScaleTo::create(0.2,0.8),
ScaleTo::create(0.2,1.2),
ScaleTo::create(0.2,1.0),
NULL));
}
//移动块到该位置
void MoveTiled::moveTo(int r,int c)
{
this->m_row=r;
this->m_col=c;
//此坐标是相对于背景块的,所以添加块到背景colorBack
this->setPosition(Point(m_col*GAME_TILED_WIDTH+GAME_TILED_BOARD_WIDTH*(m_col+1),
m_row*GAME_TILED_HEIGTH+GAME_TILED_BOARD_WIDTH*(m_row+1)));
}
//使块上面数字的值翻倍
void MoveTiled::doubleNumber()
{
//先计算值
this->m_number=this->m_number*2;
//通过tag值获取背景层
auto bk=this->getChildByTag(110);
//通过tag值获取数字层
Label *label = (Label *)bk->getChildByTag(10);
//将数字层的值设置成计算所得的值
label->setString(StringUtils::format("%d",m_number));
//计算数值翻倍是也产生动画效果
bk->runAction(Sequence::create(ScaleTo::create(0.2,0.8),
ScaleTo::create(0.2,1.2),
ScaleTo::create(0.2,1.0),
NULL));
//设置背景颜色
int num=0;
int k=this->m_number;
while(k!=1)
{
num++;
k=k/2;
}
bk->setColor(Color3B(255-20*num ,num%2==0?30:210 ,35 ));
label->setColor(Color3B::WHITE);
//字体缩小
if(num>=10)
{
label->setScale(0.5);
}
}
//初始化块
bool MoveTiled::init()
{
if(!Node::init())
{
return false;
}
//背景层
auto bk=LayerColor::create(Color4B(200,200,200,255),GAME_TILED_WIDTH,GAME_TILED_HEIGTH);
bk->setTag(110);
this->addChild(bk);
//数字层
int n=rand()%10;
this->m_number= n==7?4:2;
auto label=Label::createWithSystemFont(StringUtils::format("%d",this->m_number),"宋体",40);
label->setTag(10);
label->setColor(Color3B::WHITE);
label->setPosition(Point(GAME_TILED_WIDTH/2,GAME_TILED_HEIGTH/2));
bk->addChild(label);
//产生新块的颜色
this->m_number==2? bk->setColor(Color3B(230,170,120)):
bk->setColor(Color3B( 215, 30 ,35));
return true;
}
=========================================================================
GameScene.h
#ifndef Game_GameScene_h
#define Game_GameScene_h
#include"cocos2d.h"
#include"GameDefine.h"
#include"MoveTiled.h"
using namespace cocos2d;
class Game:public Layer
{
private:
//游戏的背景层
LayerColor *colorBack;
//是否触摸开始移动
bool m_startMove;
//触摸坐标
int m_x,m_y;
//判断音乐是否应该播放
bool m_sound_clear;
//是否在移动
bool isMove;
//分数
int m_score;
//判断是否还有可移动的块
bool isGameOver();
//切换游戏画面
void goToGameOver();
public:
//移动的方向
E_MOVE_DIR m_dir;
//保存已存在块的位置
int map[GAME_ROWS][GAME_COLS];
//保存所有块
Vector<MoveTiled *>m_allTiled;
//移动所有可移动的块
void moveAllTiled(E_MOVE_DIR dir);
//产生一个新块
void newMoveTiled();
//向四个方向移动
void moveDown();
void moveUp();
void moveLeft();
void moveRight();
//init初始化函数
bool init();
//create函数
CREATE_FUNC(Game);
//静态函数包装当前图层
static Scene *createScene();
};
#endif
=============================================================
GameScene.cpp
#include"GameScene.h"
#include"SimpleAudioEngine.h"
#include"GameOver.h"
using namespace CocosDenshion;
bool Game:: init()
{
if(!Layer::init())
{
return false;
}
//初始化是否在滑动块
isMove = false;
//初始化游戏标题
auto labelGame=Label::createWithBMFont("futura-48.fnt","2048");
labelGame->setPosition(Point(labelGame->getContentSize().width/2 ,
GAME_SCREEN_HEIGHT - labelGame->getContentSize().height/2));
this->addChild(labelGame);
labelGame->setScale(0.5);
//初始化游戏网格
int tiledmapwidth=GAME_TILED_WIDTH*GAME_COLS+GAME_TILED_BOARD_WIDTH*(GAME_COLS+1);
int tiledmapheight=GAME_TILED_HEIGTH*GAME_ROWS+GAME_TILED_BOARD_WIDTH*(GAME_ROWS+1);
colorBack=LayerColor::create(Color4B(170,170,170,255),tiledmapwidth,tiledmapheight);
//设置锚点
//colorBack->setAnchorPoint(Point(colorBack->getContentSize().width/2,colorBack->getContentSize().height/2));
//设置中心位置
colorBack->setPosition(Point((GAME_SCREEN_WIDTH-tiledmapwidth)/2,(GAME_SCREEN_HEIGHT-tiledmapheight)/2));
this->addChild(colorBack);
//初始化分数
m_score=0;
auto labelScore=Label::createWithBMFont("futura-48.fnt","Score:0");
labelScore->setPosition(Point(GAME_SCREEN_WIDTH/2 ,
GAME_SCREEN_HEIGHT - labelScore->getContentSize().height));
labelScore->setTag(120);
this->addChild(labelScore);
labelScore->setScale(0.8);
//初始化网格的每一个块
for(int row=0;row<GAME_ROWS;++row)
{
for(int col=0;col<GAME_COLS;++col)
{
auto layerTiled=LayerColor::create(Color4B(70,70,70,255),GAME_TILED_WIDTH,GAME_TILED_HEIGTH);
layerTiled->setPosition(Point(GAME_TILED_WIDTH*col+GAME_TILED_BOARD_WIDTH*(col+1),
GAME_TILED_HEIGTH*row+GAME_TILED_BOARD_WIDTH*(row+1)));
colorBack->addChild(layerTiled);
}
}
//初始化逻辑的网格数组
for(int i=0;i<GAME_ROWS;++i)
{
for(int j=0;j<GAME_COLS;++j)
{
map[i][j]=0;
}
}
//初始化数字块
newMoveTiled();
//触摸的处理
auto event=EventListenerTouchOneByOne::create();
event->onTouchBegan=[&](Touch *tou,Event *eve)
{
m_x=tou->getLocation().x;
m_y=tou->getLocation().y;
m_startMove=true;
return true;
};
event->onTouchMoved=[&](Touch *tou,Event *eve)
{
int x=tou->getLocation().x;
int y=tou->getLocation().y;
//判断移动的像素大于10 则移动成功
if(m_startMove && (abs(m_x-x)>10 || abs(m_y-y)>10) )
{
m_startMove=false;
E_MOVE_DIR dir;
//判断移动的像素 向移动像素多的方向移动
if(abs(m_x-x)>abs(m_y-y))
{
//向右移动
if(m_x<x)
{
dir=E_MOVE_DIR::RIGHT;
}
//向左移动
else
{
dir=E_MOVE_DIR::LEFT;
}
}
//上下移动
else
{
//向上移动 初始位置小于结束位置(左下角为原点)
if(m_y<y)
{
dir=E_MOVE_DIR::UP;
}
//向下移动
else
{
dir=E_MOVE_DIR::DOWN;
}
}
//移动所有的元素块
moveAllTiled(dir);
}
};
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(event,this);
//分数
return true;
}
void Game::moveAllTiled(E_MOVE_DIR dir)
{
//如果游戏结束 跳转画面
if(isGameOver())
{
goToGameOver();
return;
}
if(isMove) {return ;}
isMove=true;
//音乐未播放
m_sound_clear=false;
//移动所有块 消除
switch(dir)
{
case E_MOVE_DIR::UP: moveUp(); break;
case E_MOVE_DIR::DOWN: moveDown(); break;
case E_MOVE_DIR::LEFT: moveLeft(); break;
case E_MOVE_DIR::RIGHT: moveRight(); break;
default: break;
}
//播放音乐
if(m_sound_clear)
{
SimpleAudioEngine::getInstance()->playEffect("2.wav");
}
else
{
SimpleAudioEngine::getInstance()->playEffect("17.wav");
}
//分数变化
Label *labelScore = (Label*)this->getChildByTag(120);
labelScore->setString(StringUtils::format("score:%d",m_score));
//判定输赢
//消除了块之后产生新块
newMoveTiled();
isMove=false;
}
//产生新块
void Game::newMoveTiled()
{
auto tiled=MoveTiled::create();
//返回 还剩多少空格
int freeCount=16-m_allTiled.size();
//如果没有消除 也没结束 则不产生新块
if(!freeCount)
{
return;
}
int num=rand()%freeCount;
//锁定空白区域 并用count计数
int row=0;
int col=0;
int count=-1;
bool find=false;
//判断该区域是否是空白区域
for(;row<GAME_ROWS;++row)
{
for(col=0;col<GAME_COLS;++col)
{
//空白区域超过了随机数 则成功
if(map[row][col]==0)
{
++count;
if(count==num)
{
find=true;
break;
}
}
}
if(find)
{
break;
}
}
colorBack->addChild(tiled);
tiled->showAt(row,col);
m_allTiled.pushBack(tiled);
map[row][col]=m_allTiled.getIndex(tiled)+1;
}
//向上移动所有的块
void Game::moveUp()
{
//从左上角开始 一行行扫描
for(int col=0;col<GAME_COLS;++col)
{
//判断消除相同块的次数
int hit=0;
for(int row=GAME_ROWS-1;row>=0;--row)
{
if(map[row][col]>0)
{
for(int row1=row;row1<GAME_ROWS-1;++row1)
{
if(map[row1+1][col]==0)
{
map[row1+1][col]=map[row1][col];
map[row1][col]=0;
m_allTiled.at(map[row1+1][col]-1)->moveTo(row1+1,col);
}
else
{
//判断是否可以消除
int numObj = m_allTiled.at(map[row1+1][col]-1)->m_number;
int numNow = m_allTiled.at(map[row1][col]-1)->m_number;
if(numObj==numNow && hit%2==0)
{
++hit;
m_sound_clear=true;
m_score+=numObj*2;
//如果成功合并块,撞击结束 不继续检测
hit=true;
//将下一个的块的值翻倍
m_allTiled.at(map[row1+1][col]-1)->doubleNumber();
//将当前快从父节点移除
m_allTiled.at(map[row1][col]-1)->removeFromParent();
//在容器中清除map[row1][col]
m_allTiled.erase(map[row1][col]-1);
int index = map[row1][col];
for(int r=0;r<GAME_ROWS;++r)
{
for(int c=0;c<GAME_COLS;++c)
{
if(map[r][c]>index)
{
map[r][c]--;
}
}
}
map[row1][col]=0;
}
else if(hit!=0)
{
++hit;
}
break;
}
}
}
}
}
}
//向下移动所有的块
void Game::moveDown()
{
//从左下角开始 一行行扫描
for(int col=0;col<GAME_COLS;++col)
{
int hit=0;
for(int row=0;row<GAME_ROWS;++row)
{
if(map[row][col]>0)
{
for(int row1=row;row1>0;--row1)
{
if(map[row1-1][col]==0)
{
map[row1-1][col]=map[row1][col];
map[row1][col]=0;
m_allTiled.at(map[row1-1][col]-1)->moveTo(row1-1,col);
}
else
{
//判断是否可以消除
int numObj = m_allTiled.at(map[row1-1][col]-1)->m_number;
int numNow = m_allTiled.at(map[row1][col]-1)->m_number;
if(numObj==numNow && hit%2==0)
{
++hit;
m_sound_clear=true;
//加分处理
m_score+=numObj*2;
//将下一个的块的值翻倍
m_allTiled.at(map[row1-1][col]-1)->doubleNumber();
//将当前快从父节点移除
m_allTiled.at(map[row1][col]-1)->removeFromParent();
//在容器中清除map[row1][col]
m_allTiled.erase(map[row1][col]-1);
int index = map[row1][col];
for(int r=0;r<GAME_ROWS;++r)
{
for(int c=0;c<GAME_COLS;++c)
{
if(map[r][c]>index)
{
map[r][c]--;
}
}
}
map[row1][col]=0;
}
else if(hit!=0)
{
++hit;
}
break;
}
}
}
}
}
}
//向左移动所有的块
void Game::moveLeft()
{
//从左下角开始 一列列扫描
for(int row=0;row<GAME_ROWS;++row)
{
int hit=0;
for(int col=0;col<GAME_COLS;++col)
{
if(map[row][col]>0)
{
for(int col1=col;col1>0;--col1)
{
if(map[row][col1-1]==0)
{
map[row][col1-1]=map[row][col1];
map[row][col1]=0;
m_allTiled.at(map[row][col1-1]-1)->moveTo(row,col1-1);
}
else
{
//判断是否可以消除
int numObj = m_allTiled.at(map[row][col1-1]-1)->m_number;
int numNow = m_allTiled.at(map[row][col1]-1)->m_number;
if(numObj==numNow && hit%2==0)
{
++hit;
m_sound_clear=true;
m_score+=numObj*2;
//将下一个的块的值翻倍
m_allTiled.at(map[row][col1-1]-1)->doubleNumber();
//将当前快从父节点移除
m_allTiled.at(map[row][col1]-1)->removeFromParent();
//在容器中清除map[row1][col1]
m_allTiled.erase(map[row][col1]-1);
int index = map[row][col1];
for(int r=0;r<GAME_ROWS;++r)
{
for(int c=0;c<GAME_COLS;++c)
{
if(map[r][c]>index)
{
map[r][c]--;
}
}
}
map[row][col1]=0;
}
else if(hit!=0)
{
++hit;
}
break;
}
}
}
}
}
}
//向右移动所有的块
void Game::moveRight()
{
//从左下角开始 一列列扫描
for(int row=0;row<GAME_ROWS;++row)
{
int hit=0;
for(int col=GAME_COLS-1;col>=0;--col)
{
if(map[row][col]>0)
{
for(int col1=col;col1<GAME_COLS-1;++col1)
{
if(map[row][col1+1]==0)
{
map[row][col1+1]=map[row][col1];
map[row][col1]=0;
m_allTiled.at(map[row][col1+1]-1)->moveTo(row,col1+1);
}
else
{
//判断是否可以消除
int numObj = m_allTiled.at(map[row][col1+1]-1)->m_number;
int numNow = m_allTiled.at(map[row][col1]-1)->m_number;
if(numObj==numNow && hit%2==0)
{
++hit;
m_sound_clear=true;
m_score+=numObj*2;
//将下一个的块的值翻倍
m_allTiled.at(map[row][col1+1]-1)->doubleNumber();
//将当前快从父节点移除
m_allTiled.at(map[row][col1]-1)->removeFromParent();
//在容器中清除map[row][col1]
m_allTiled.erase(map[row][col1]-1);
int index = map[row][col1];
for(int r=0;r<GAME_ROWS;++r)
{
for(int c=0;c<GAME_COLS;++c)
{
if(map[r][c]>index)
{
map[r][c]--;
}
}
}
map[row][col1]=0;
}
else if(hit!=0)
{
++hit;
}
break;
}
}
}
}
}
}
//静态函数包装场景
Scene* Game::createScene()
{
auto scene=Scene::create();
auto layer=Game::create();
scene->addChild(layer);
return scene;
}
//判断是否还有可移动的块
bool Game::isGameOver()
{
bool isGameOver=true;
int freeCount=16-m_allTiled.size();
//如果没有空格
if(freeCount!=0)
{
isGameOver=false;
}
//没有可移动块
if(freeCount==0)
{
//判断四个方向有没有可移动的块
for(int r=0;r<GAME_ROWS;++r)
{
for(int c=0;c<GAME_COLS;++c)
{
//只有有一对相等的块 则有可移动块
int num=m_allTiled.at(map[r][c]-1)->m_number;
int objNum=0;
//上
if(r+1<GAME_ROWS)
{
objNum=m_allTiled.at(map[r+1][c]-1)->m_number;
if(num==objNum)
{
isGameOver=false;
}
}
//下
if(r-1>=0)
{
objNum=m_allTiled.at(map[r-1][c]-1)->m_number;
if(num==objNum)
{
isGameOver=false;
}
}
//左
if(c-1>=0)
{
objNum=m_allTiled.at(map[r][c-1]-1)->m_number;
if(num==objNum)
{
isGameOver=false;
}
}
//右
if(c+1<GAME_COLS)
{
objNum=m_allTiled.at(map[r][c+1]-1)->m_number;
if(num==objNum)
{
isGameOver=false;;
}
}
//如果找到相同的块 跳出循环
if(!isGameOver)
{
break;
}
}
if(!isGameOver)
{
break;
}
}
}
return isGameOver;
}
//切换到游戏结束画面
void Game::goToGameOver()
{
auto scene=GameOver::createScene();
Director::getInstance()->replaceScene(TransitionFadeDown::create(0.3,scene));
}
========================================================================
GameOver.h
#ifndef Game_GameOver_h
#define Game_GameOver_h
#include "cocos2d.h"
#include <iostream>
using namespace cocos2d;
class GameOver:public Layer
{
public:
//init初始化函数
virtual bool init();
//create函数
CREATE_FUNC(GameOver);
//静态函数包装当前图层
static Scene *createScene();
//跳转到游戏场景的菜单
void menuCallBack(Ref* pObject);
};
#endif
=================================================================
GameOver.cpp
#include"GameOver.h"
#include"GameScene.h"
#include"GameDefine.h"
//init初始化函数
bool GameOver::init()
{
if(!Layer::init())
{
return false;
}
//显示游戏结束
auto labelGame=Label::createWithBMFont("futura-48.fnt","GAME OVER");
labelGame->setPosition(Point(GAME_SCREEN_WIDTH/2,2*GAME_SCREEN_HEIGHT/3));
this->addChild(labelGame);
labelGame->setScale(1.2);
//重新开始
auto label=Label::createWithBMFont("futura-48.fnt","Start Again");
auto labelItem=MenuItemLabel::create(label,CC_CALLBACK_1(GameOver::menuCallBack,this));
labelItem->setPosition(Point(GAME_SCREEN_WIDTH/2,GAME_SCREEN_HEIGHT/3));
auto menu=Menu::create(labelItem,NULL);
menu->setPosition(Point::ZERO);
this->addChild(menu);
return true;
}
//跳转到游戏场景的菜单
void GameOver::menuCallBack(Ref* pObject)
{
auto scene=Game::createScene();
Director::getInstance()->replaceScene(TransitionFadeDown::create(0.2,scene));
}
//静态函数包装当前图层
Scene *GameOver::createScene()
{
auto scene=Scene::create();
auto layer=GameOver::create();
scene->addChild(layer);
return scene;
}
==============================================================
GameDefine.h
#ifndef Game2048_GameDefine_
#define Game2048_GameDefine_
#define GAME_SCREEN_WIDTH 320
#define GAME_SCREEN_HEIGHT 480
#define GAME_TILED_WIDTH 64
#define GAME_TILED_HEIGTH 64
#define GAME_TILED_BOARD_WIDTH 2
#define GAME_ROWS 4
#define GAME_COLS 4
//移动的方向
enum class E_MOVE_DIR
{
UP,
DOWN,
LEFT,
RIGHT
};
#endif