項目展示
遊戲思路
其實2048這個遊戲最重要的地方就是數字的合併,相同的數字可以合併成更大的數字。首先我們肯定是要創建一個二維數組,但是實際上我們每一次合併的操作都是對一維數組進行操作的,例如:
2 2 0 0
0 2 0 2
4 2 0 2
4 0 2 0
//如果是從向上合併,那麼
2 0 4 4 -> 2 8 0 0
2 2 2 0 -> 4 2 0 0
0 0 0 2 -> 2 0 0 0
0 2 2 0 -> 4 0 0 0
我們需要從上往下獲取數據,然後把獲取到的數據形成一個一維數組,然後把這個一維數組先去0,就是把非0數字往前面挪動,再把相鄰且相同的數字進行合併(將後一個元素累加到前一個元素上,後一個元素清0),合併之後再進行一次去0操作,最後再把這個一維數組還原至原行或者原列。大體的思路是這樣,接下來附上代碼。
遊戲代碼
Number.h
#pragma once
#include "cocos2d.h"
class Number : public cocos2d::Sprite{
public :
static Number* create(int number);
bool init(int number);
void setImage(int number);
};
Number.cpp
#include "Number.h"
Number* Number::create(int number) {
Number* ret = new (std::nothrow)Number();
if (ret && ret->init(number)) {
ret->autorelease();
} else {
delete ret;
ret = nullptr;
}
return ret;
}
bool Number::init(int number) {
char filename[40];
sprintf_s(filename, "image/%d.png", number);
if (!Sprite::initWithFile(filename)) {
return false;
}
return true;
}
void Number::setImage(int number) {
char filename[40];
sprintf_s(filename, "image/%d.png", number);
this->setTexture(filename);
}
這個Number類的作用是在把創建數字精靈的操作給封裝一下,用到的時候更方便,接下來是遊戲場景的類
GameLayer.h
#ifndef __GAMELAYER_SCENE_H__
#define __GAMELAYER_SCENE_H__
#include "cocos2d.h"
#include "Number.h"
//定義一個枚舉的方向類
enum Dir {
left,
right,
up,
down
};
class GameLayer : public cocos2d::Layer
{
public:
static cocos2d::Scene* createScene();
virtual bool init();
//初始化棋盤極其數字
void initNumber();
//默認調度器
void update(float dt);
//去0
void removeZero(bool inverted, bool again);
//從原行或原列獲取數據
void getDataForColORRow(int arr[][4], int getNum);
//合併
void merge(bool inverted);
//把一維數組還原到原行或原列中
void reduction(int arr[][4], int reductionNum);
//設置是否開啓鍵盤監聽
void setKeyboardEnable(bool enable);
//回調函數
void onKeyPressed(cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event*);
//清空數組
void arrClearr();
//創建隨機數字
void randomCreateNum();
GameLayer();
~GameLayer();
CREATE_FUNC(GameLayer);
private:
//用於存放數字精靈
Number *number[4][4];
Dir dir;
int map[4][4] = {
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 }
};
int temp[4]{ 0 };
//判斷是否從鍵盤監聽到了方向鍵
bool isInput = false;
};
#endif // __GAMELAYER_SCENE_H__
GameLayer.cpp
#include "GameLayer.h"
#include <cstdlib>
#include <ctime>
using namespace cocos2d;
USING_NS_CC;
static const int ArrSize = 4;
/***************************2048核心算法**********************************/
/*
* 1.定義去零方法(針對一維數組):將0元素移至末尾
* 2.合併數據方法(針對一維數組)
* --去零:將0元素移至末尾
* --相鄰 相同則合併(將後一個元素累加到前一個元素上,後一個元素清0)
* --去零:將0元素移至末尾
* 3.上移
* --從上到下獲取列數據,形成一維數組
* --調用合併數據方法
* --將一維數組元素還原至原列
* 4.上移
* --從下到上獲取列數據,形成一維數組
* --調用合併數據方法
* --將一維數組元素還原至原列
* ......
*/
GameLayer::GameLayer():dir(up) {
}
GameLayer::~GameLayer() {
}
Scene* GameLayer::createScene() {
auto scene = Scene::create();
auto layer = GameLayer::create();
scene->addChild(layer);
return scene;
}
bool GameLayer::init() {
if (!Layer::init()) {
return false;
}
//產生隨機數種子
srand((int)time(0));
Size size = Director::getInstance()->getVisibleSize();
//創建背景
Sprite* bg = Sprite::create("image/map.png");
bg->setPosition(Vec2(size.width / 2, size.height / 2));
this->addChild(bg, -1);
//初始化數字
initNumber();
//開啓默認調度器
scheduleUpdate();
//開啓鍵盤監聽
setKeyboardEnable(true);
return true;
}
/*
默認調度器
*/
void GameLayer::update(float dt) {
//判斷如果從鍵盤上接收到了方向鍵
if (isInput) {
//正向獲取數據還是反向
bool inverted;
switch (dir) {
case left:
case up:
inverted = true;
break;
case down:
case right:
inverted = false;
break;
}
for (int i = 0; i < ArrSize; i++) {
//獲取數據
getDataForColORRow(map, i);
//合併
merge(inverted);
//還原
reduction(map, i);
//清空
arrClearr();
}
//創建隨機數字
randomCreateNum();
for (size_t i = 0; i < ArrSize; i++) {
for (size_t j = 0; j < ArrSize; j++) {
//設置數字精靈的圖片
number[i][j]->setImage(map[i][j]);
//設置數字精靈位置
number[i][j]->setPosition(Vec2(113 * j + 7, 113 * (ArrSize - i - 1) + 7));
}
}
isInput = false;
}
}
/*
初始化棋盤數字
*/
void GameLayer::initNumber() {
//開局隨機先創建兩個數字
randomCreateNum();
randomCreateNum();
//初始化棋盤數字並打印
for (size_t i = 0; i < ArrSize; i++) {
for (size_t j = 0; j < ArrSize; j++) {
number[i][j] = Number::create(map[i][j]);
number[i][j]->setAnchorPoint(Vec2(0, 0));
number[i][j]->setPosition(Vec2(113 * j + 7, 113 * (ArrSize - i - 1) + 7));
this->addChild(number[i][j], 1);
}
}
}
/*
獲取原行或原列數據
*/
void GameLayer::getDataForColORRow(int arr[][ArrSize], int getNum) {
for (int i = 0; i < ArrSize; i++) {
switch (dir) {
case left:
temp[i] = arr[getNum][i];
break;
case right:
temp[i] = arr[getNum][ArrSize - i - 1];
break;
case down:
temp[i] = arr[ArrSize - i - 1][getNum];
break;
case up:
temp[i] = arr[i][getNum];
break;
}
}
}
/*
合併方法
*/
void GameLayer::merge(bool inverted) {
//第一次去零
removeZero(inverted, false);
//進行合併,把後一個數字給設置爲0
for (int i = 0; i < ArrSize - 1; i++) {
if (temp[i] == temp[i + 1]) {
temp[i] += temp[i + 1];
temp[i + 1] = 0;
}
}
//第二次去零
removeZero(inverted, true);
}
/*
去0方法
*/
void GameLayer::removeZero(bool inverted, bool again) {
int arr[ArrSize]{ 0 };
int j = 0;
//先把非0數字給提取出來,放到一個新數組中
for (int i = 0; i < ArrSize; i++) {
if (temp[i] != 0) {
arr[j++] = temp[i];
temp[i] = 0;
}
}
for (int i = 0; i < j; i++) {
if (inverted) {
//如果是正向獲取來的數組,還原的時候也是正向
temp[i] = arr[i];
} else {
if (!again) {
//如果是反向且第二次去0,則不需要把數組倒置
temp[i] = arr[i];
} else {
//如果是反向獲取來的數組,還原的時候也是反向
temp[ArrSize - i - 1] = arr[i];
}
}
}
}
/*
將一維數組還原至原列或原行
*/
void GameLayer::reduction(int arr[][ArrSize], int reductionNum) {
for (int i = 0; i < ArrSize; i++) {
switch (dir) {
case left:
case right:
arr[reductionNum][i] = temp[i];
break;
case down:
case up:
arr[i][reductionNum] = temp[i];
break;
}
}
}
//是否開啓鍵盤監聽
void GameLayer::setKeyboardEnable(bool enable) {
if (enable) {//開啓
auto listener = EventListenerKeyboard::create();
listener->onKeyPressed = CC_CALLBACK_2(GameLayer::onKeyPressed, this);
//事件分發
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
} else {//關閉
_eventDispatcher->removeEventListenersForTarget(this);
}
}
/*
鍵盤監聽
*/
void GameLayer::onKeyPressed(cocos2d::EventKeyboard::KeyCode keyCode, cocos2d::Event*) {
switch (keyCode) {
case EventKeyboard::KeyCode::KEY_UP_ARROW:
dir = up;
isInput = true;
break;
case EventKeyboard::KeyCode::KEY_DOWN_ARROW:
dir = down;
isInput = true;
break;
case EventKeyboard::KeyCode::KEY_LEFT_ARROW:
dir = left;
isInput = true;
break;
case EventKeyboard::KeyCode::KEY_RIGHT_ARROW:
dir = right;
isInput = true;
break;
}
}
/*
數組清空
*/
void GameLayer::arrClearr() {
for (size_t i = 0; i < ArrSize; i++) {
temp[i] = 0;
}
}
/*
隨機創建數字
*/
void GameLayer::randomCreateNum() {
int x = rand() % ArrSize;
int y = rand() % ArrSize;
while (map[x][y] != 0) {
//判斷map[x][y]的這個元素是否爲0,如果不是0,則繼續隨機獲取座標
x = rand() % ArrSize;
y = rand() % ArrSize;
}
![](https://user-gold-cdn.xitu.io/2020/4/24/171ab6d72edb2f43?w=304&h=320&f=gif&s=235311)
map[x][y] = rand() % 2 == 0 ? 2 : 4;
}
最後附上我的github地址,供大家參考源碼
https://github.com/DemonHXD/2048