项目展示
游戏思路
其实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