使用cocos2d寫個簡單的2048小遊戲

項目展示

遊戲思路

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章