使用cocos2d写个简单的2048小游戏

1 篇文章 0 订阅

项目展示

游戏思路

其实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

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值