c++实现2048小游戏

2023-05-16

C++实现2048

2048小游戏界面展示效果图:
在这里插入图片描述
在这里插入图片描述

#include <easyx.h>//需要包含这个库
#include <string.h>
#include <map>
#include <sstream>
#include <time.h>
#include <conio.h>
#include <graphics.h>
#pragma comment( lib, "MSIMG32.LIB")


// 方块的状态
enum State
{
	EXIST,		// 存在
	DESTORY		// 销毁
};


// 二维向量,用于表示位置或者大小
struct Vector2
{
	float x;
	float y;
};


// 符号函数
int sgn(float d)
{
	if (d < 0)	return -1;
	if (d > 0)	return  1;
	return 0;
}


class Block
{
private:
	State	currentState;		// 当前的状态
	State	targetState;		// 移动后的状态
	Vector2	size;
	Vector2	currentPos;			// 当前位置
	Vector2	targetPos;			// 目标位置
	IMAGE	*img;
	IMAGE	*newImg;
	float	deltaPos;			// 每秒移动多少位置
	float	deltaSize;			// 每秒变大多少
	float	animationSpeed;		// 动画速度


public:
	Block(const Vector2 &pos, IMAGE *img)
	{
		currentPos = targetPos = pos;
		currentState = targetState = EXIST;
		size = { 50, 50 };
		this->img = this->newImg = img;

		deltaPos = 100;
		deltaSize = 40;
		animationSpeed = 20.0f;
	}


	void update(float deltaTime)
	{
		// 改变方块大小(图片刚生成时的由小到大的动画)
		if (size.x < img->getwidth())
		{
			size.x = size.y = size.x + deltaSize * deltaTime * animationSpeed / 2;
			if (size.x > img->getwidth())
			{
				size.x = size.y = (float)img->getwidth();
			}
		}

		// 更新方块位置
		if (currentPos.x != targetPos.x || currentPos.y != targetPos.y)
		{
			int directionX = sgn(targetPos.x - currentPos.x);
			int directionY = sgn(targetPos.y - currentPos.y);

			currentPos.x += deltaPos * directionX * deltaTime * animationSpeed;
			// 相距小于 5 视为在同一位置
			if (fabs(currentPos.x - targetPos.x) < 5)
			{
				currentPos.x = targetPos.x;
			}

			currentPos.y += deltaPos * directionY * deltaTime * animationSpeed;
			if (fabs(currentPos.y - targetPos.y) < 5)
			{
				currentPos.y = targetPos.y;
			}
		}
		if (currentPos.x == targetPos.x &&currentPos.y == targetPos.y)
		{
			currentState = targetState;
			img = newImg;
		}
	}


	void draw()
	{
		TransparentBlt(GetImageHDC(NULL), int(currentPos.x + (90 - size.x) / 2), int(currentPos.y + (90 - size.y) / 2),
			(int)size.x, (int)size.y, GetImageHDC(img), 0, 0, img->getwidth(), img->getheight(), BLACK);
	}


	// 把方块从当前位置移动到目标位置,移动后改变状态
	void MoveTo(const Vector2 &pos, IMAGE *newImg, State state = EXIST)
	{
		targetPos = pos;
		targetState = state;
		this->newImg = newImg;
	}


	State getState()
	{
		return currentState;
	}
};



int		map[4][4];				// 4 * 4 地图
Block	*blockMap[4][4];		// 方块索引
int		score;					// 得分
int		maxScore;				// 最高得分
int		currentMaxBlock;		// 当前最大方块
int		maxBlock;				// 历史最大方块
int		gameLoop;				// 游戏循环
float	keyTime = 0;			// 按键间隔
std::map<int, IMAGE> image;		// 存储所有数字图像
bool	gameOver = false;		// 游戏是否结束
float	overTime;				// 游戏结束后不会立刻退出循环,而是等待 0.5s 更新动画


// 判断是否有可移动的方式,有返回 1 ,没有返回 0
// 检测思路:如果碰到为 0 的格子,或者两个相邻的格子数字相等,则返回 1
int Judge()
{
	// 横向检测
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			if (map[i][j] == 0 || map[i][j] == map[i][j + 1] || map[i][j + 1] == 0)
				return 1;
		}
	}

	// 纵向检测
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			if (map[j][i] == 0 || map[j][i] == map[j + 1][i] || map[j + 1][i] == 0)
				return 1;
		}
	}

	return 0;
}


// 上移
void Up()
{
	int moveFlag = 0;	// 记录是否进行过移动
	int mergeFlag = 0;	// 记录是否合并过

	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			int k, z;

			// 找到一个不为 0 的方块向上移动,并判断是否可以和下边的方块合并
			for (k = j; k < 4; k++)
				if (map[k][i] != 0)
					break;

			// 寻找右边不为 0 的方块
			for (z = k + 1; z < 4; z++)
				if (map[z][i] != 0)
					break;

			// 当前行有非 0 方块
			if (k < 4)
			{
				if (z < 4 && map[k][i] == map[z][i])
				{
					// 可以合并
					int value = map[k][i] + map[z][i];
					map[k][i] = map[z][i] = 0;
					map[j][i] = value;

					// 开启动画
					Block *temp = blockMap[k][i];
					blockMap[k][i] = NULL;
					blockMap[j][i] = temp;
					blockMap[j][i]->MoveTo({ 25.0f + 100 * i, 225.0f + 100 * j }, &image[map[j][i]]);
					blockMap[z][i]->MoveTo({ 25.0f + 100 * i, 225.0f + 100 * (j + 1) }, &image[map[z][i]], DESTORY);

					// 更新分数
					score += map[j][i];
					if (score > maxScore) maxScore = score;

					// 更新方块
					if (value > currentMaxBlock) currentMaxBlock = value;
					if (currentMaxBlock > maxBlock) maxBlock = currentMaxBlock;

					mergeFlag = 1;
				}
				else
				{
					// 不可以合并
					int value = map[k][i];
					map[k][i] = 0;
					map[j][i] = value;

					// 判断是否可以移动
					if (k != j)
					{
						moveFlag = 1;

						// 开启动画
						Block *temp = blockMap[k][i];
						blockMap[k][i] = NULL;
						blockMap[j][i] = temp;
						blockMap[j][i]->MoveTo({ 25.0f + 100 * i, 225.0f + 100 * j }, &image[map[j][i]]);
					}
				}
			}
			else		// 判断下一行
			{
				break;
			}
		}
	}

	// 如果发生了移动或合并,随机生成一个 2 或 4
	if (moveFlag || mergeFlag)
	{
		int index;	// 随机位置的索引

		// 直到随机到一个空位置退出循环
		do
		{
			index = rand() % 4;
		} while (map[3][index] != 0);

		// 80% 生成 2 , 20% 生成 4
		int num = rand() % 10;
		if (num < 8)
		{
			map[3][index] = 2;
			blockMap[3][index] = new Block({ 25.0f + 100 * index, 225.0f + 100 * 3 }, &image[2]);
		}
		else
		{
			map[3][index] = 4;
			blockMap[3][index] = new Block({ 25.0f + 100 * index, 225.0f + 100 * 3 }, &image[4]);
		}
	}
}


// 下移
void Down()
{
	int moveFlag = 0;	// 记录是否进行过移动
	int mergeFlag = 0;	// 记录是否合并过

	for (int i = 0; i < 4; i++)
	{
		for (int j = 3; j > 0; j--)
		{
			int k, z;

			// 找到一个不为 0 的方块向下移动,并判断是否可以和上边的方块合并
			for (k = j; k >= 0; k--)
				if (map[k][i] != 0)
					break;

			// 寻找右边不为 0 的方块
			for (z = k - 1; z >= 0; z--)
				if (map[z][i] != 0)
					break;

			// 当前行有非 0 方块
			if (k >= 0)
			{
				if (z >= 0 && map[k][i] == map[z][i])
				{
					// 可以合并
					int value = map[k][i] + map[z][i];
					map[k][i] = map[z][i] = 0;
					map[j][i] = value;

					// 开启动画
					Block *temp = blockMap[k][i];
					blockMap[k][i] = NULL;
					blockMap[j][i] = temp;
					blockMap[j][i]->MoveTo({ 25.0f + 100 * i, 225.0f + 100 * j }, &image[map[j][i]]);
					blockMap[z][i]->MoveTo({ 25.0f + 100 * i, 225.0f + 100 * (j - 1) }, &image[map[z][i]], DESTORY);

					// 更新分数
					score += map[j][i];
					if (score > maxScore) maxScore = score;

					// 更新方块
					if (value > currentMaxBlock) currentMaxBlock = value;
					if (currentMaxBlock > maxBlock) maxBlock = currentMaxBlock;

					mergeFlag = 1;
				}
				else
				{
					// 不可以合并
					int value = map[k][i];
					map[k][i] = 0;
					map[j][i] = value;

					// 判断是否可以移动
					if (k != j)
					{
						moveFlag = 1;
						// 开启动画
						Block *temp = blockMap[k][i];
						blockMap[k][i] = NULL;
						blockMap[j][i] = temp;
						blockMap[j][i]->MoveTo({ 25.0f + 100 * i, 225.0f + 100 * j }, &image[map[j][i]]);
					}
				}
			}
			else		// 判断下一行
			{
				break;
			}
		}
	}

	// 如果发生了移动或合并,随机生成一个 2 或 4
	if (moveFlag || mergeFlag)
	{
		int index;	// 随机位置的索引

		// 直到随机到一个为 0 的位置退出循环
		do
		{
			index = rand() % 4;
		} while (map[0][index] != 0);

		// 80% 生成 2 , 20% 生成 4
		int num = rand() % 10;
		if (num < 8)
		{
			map[0][index] = 2;
			blockMap[0][index] = new Block({ 25.0f + 100 * index, 225.0f + 100 * 0 }, &image[2]);
		}
		else
		{
			map[0][index] = 4;
			blockMap[0][index] = new Block({ 25.0f + 100 * index, 225.0f + 100 * 0 }, &image[4]);
		}
	}
}


// 左移
void Left()
{
	int moveFlag = 0;	// 记录是否进行过移动
	int mergeFlag = 0;	// 记录是否合并过

	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			int k, z;

			// 找到一个不为 0 的方块向左移动,并判断是否可以和右边的方块合并
			for (k = j; k < 4; k++)
				if (map[i][k] != 0)
					break;

			// 寻找右边不为 0 的方块
			for (z = k + 1; z < 4; z++)
				if (map[i][z] != 0)
					break;

			// 当前行有非 0 方块
			if (k < 4)
			{
				if (z < 4 && map[i][k] == map[i][z])
				{
					// 可以合并
					int value = map[i][k] + map[i][z];
					map[i][k] = map[i][z] = 0;
					map[i][j] = value;

					// 开启动画
					Block *temp = blockMap[i][k];
					blockMap[i][k] = NULL;
					blockMap[i][j] = temp;
					blockMap[i][j]->MoveTo({ 25.0f + 100 * j, 225.0f + 100 * i }, &image[value]);
					blockMap[i][z]->MoveTo({ 25.0f + 100 * (j + 1), 225.0f + 100 * i }, &image[map[z][i]], DESTORY);

					// 更新分数
					score += map[i][j];
					if (score > maxScore) maxScore = score;

					// 更新方块
					if (value > currentMaxBlock) currentMaxBlock = value;
					if (currentMaxBlock > maxBlock) maxBlock = currentMaxBlock;

					mergeFlag = 1;
				}
				else
				{
					// 不可以合并
					int value = map[i][k];
					map[i][k] = 0;
					map[i][j] = value;

					// 判断是否可以移动
					if (k != j)
					{
						moveFlag = 1;
						// 开启动画
						Block *temp = blockMap[i][k];
						blockMap[i][k] = NULL;
						blockMap[i][j] = temp;
						blockMap[i][j]->MoveTo({ 25.0f + 100 * j, 225.0f + 100 * i }, &image[value]);
					}
				}
			}
			else		// 判断下一行
			{
				break;
			}
		}
	}

	// 如果发生了移动或合并,随机生成一个 2 或 4
	if (moveFlag || mergeFlag)
	{

		int index;	// 随机位置的索引

		// 直到随机到一个为 0 的位置退出循环
		do
		{
			index = rand() % 4;
		} while (map[index][3] != 0);

		// 80% 生成 2 , 20% 生成 4
		int num = rand() % 10;
		if (num < 8)
		{
			map[index][3] = 2;
			blockMap[index][3] = new Block({ 25.0f + 100 * 3, 225.0f + 100 * index }, &image[2]);
		}
		else
		{
			map[index][3] = 4;
			blockMap[index][3] = new Block({ 25.0f + 100 * 3, 225.0f + 100 * index }, &image[4]);
		}
	}
}


// 右移
void Right()
{
	int moveFlag = 0;	// 记录是否进行过移动
	int mergeFlag = 0;	// 记录是否合并过

	for (int i = 0; i < 4; i++)
	{
		for (int j = 3; j > 0; j--)
		{
			int k, z;

			// 找到一个不为 0 的方块向右移动,并判断是否可以和左边的方块合并
			for (k = j; k >= 0; k--)
				if (map[i][k] != 0)
					break;

			// 寻找右边不为 0 的方块
			for (z = k - 1; z >= 0; z--)
				if (map[i][z] != 0)
					break;

			// 当前行有非 0 方块
			if (k >= 0)
			{
				if (z >= 0 && map[i][k] == map[i][z])
				{
					// 可以合并
					int value = map[i][k] + map[i][z];
					map[i][k] = map[i][z] = 0;
					map[i][j] = value;

					// 开启动画
					Block *temp = blockMap[i][k];
					blockMap[i][k] = NULL;
					blockMap[i][j] = temp;
					blockMap[i][j]->MoveTo({ 25.0f + 100 * j, 225.0f + 100 * i }, &image[value]);
					blockMap[i][z]->MoveTo({ 25.0f + 100 * (j - 1), 225.0f + 100 * i }, &image[map[z][i]], DESTORY);

					// 更新分数
					score += map[i][j];
					if (score > maxScore) maxScore = score;

					// 更新方块
					if (value > currentMaxBlock) currentMaxBlock = value;
					if (currentMaxBlock > maxBlock) maxBlock = currentMaxBlock;

					mergeFlag = 1;
				}
				else
				{
					// 不可以合并
					int value = map[i][k];
					map[i][k] = 0;
					map[i][j] = value;

					// 判断是否可以移动
					if (k != j)
					{
						moveFlag = 1;
						// 开启动画
						Block *temp = blockMap[i][k];
						blockMap[i][k] = NULL;
						blockMap[i][j] = temp;
						blockMap[i][j]->MoveTo({ 25.0f + 100 * j, 225.0f + 100 * i }, &image[value]);
					}
				}
			}
			else		// 判断下一行
			{
				break;
			}
		}
	}

	// 如果发生了移动或合并,随机生成一个 2 或 4
	if (moveFlag || mergeFlag)
	{
		int index;	// 随机位置的索引
		do
		{
			index = rand() % 4;
		} while (map[index][0] != 0);

		// 80% 生成 2 , 20% 生成 4
		int num = rand() % 10;
		if (num < 8)
		{
			map[index][0] = 2;
			blockMap[index][0] = new Block({ 25.0f + 100 * 0, 225.0f + 100 * index }, &image[2]);
		}
		else
		{
			map[index][0] = 4;
			blockMap[index][0] = new Block({ 25.0f + 100 * 0, 225.0f + 100 * index }, &image[4]);
		}
	}
}


void Update(float deltaTime)
{
	// 更新方块
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			if (blockMap[i][j] != NULL)
			{
				blockMap[i][j]->update(deltaTime);
				if (blockMap[i][j]->getState() == DESTORY)
				{
					delete blockMap[i][j];
					blockMap[i][j] = NULL;
				}
			}
		}
	}

	if (gameOver)
	{
		overTime -= deltaTime;
		if (overTime <= 0)
			gameLoop = 0;
	}

	keyTime += deltaTime;
	// 0.2s 可以按键一次
	if (keyTime < 0.2f || gameOver)
		return;

	if ((GetAsyncKeyState(VK_UP) & 0x8000) || (GetAsyncKeyState('W') & 0x8000))			// 上
	{
		Up();
		if (!Judge())
		{
			gameOver = true;
		}
		keyTime = 0;
	}
	else if ((GetAsyncKeyState(VK_DOWN) & 0x8000) || (GetAsyncKeyState('S') & 0x8000))	// 下
	{
		Down();
		if (!Judge())
		{
			gameOver = true;
		}
		keyTime = 0;
	}
	else if ((GetAsyncKeyState(VK_LEFT) & 0x8000) || (GetAsyncKeyState('A') & 0x8000))	// 左
	{
		Left();
		if (!Judge())
		{
			gameOver = true;
		}
		keyTime = 0;
	}
	else if ((GetAsyncKeyState(VK_RIGHT) & 0x8000) || (GetAsyncKeyState('D') & 0x8000))	// 右
	{
		Right();
		if (!Judge())
		{
			gameOver = true;
		}
		keyTime = 0;
	}
}


// 设置文字样式和颜色
void settext(int height, int weight, UINT color)
{
	settextstyle(height, 0, _T("Arial"), 0, 0, weight, false, false, false, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
		CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH);
	settextcolor(color);
}


// 在指定矩形区域内居中输出字符串
void printtext(LPCTSTR s, int left, int top, int right, int width)
{
	RECT r = { left, top, right, width };
	drawtext(s, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}


// 绘制界面
void Draw()
{
	// 历史最大方块
	TransparentBlt(GetImageHDC(NULL), 12, 30, 90, 90, GetImageHDC(&image[maxBlock]), 0, 0, 90, 90, 0x9eaebb);

	setfillcolor(0x9eaebb);
	// 绘制当前分数
	solidroundrect(112, 30, 264, 119, 10, 10);
	settext(28, 800, 0xdbe6ee);
	printtext(_T("SCORE"), 112, 40, 264, 69);
	std::stringstream ss;
	ss << score;
	settext(44, 800, WHITE);

	printtext(ss.str().c_str(), 112, 70, 264, 114);
	ss.str(_T(""));

	// 绘制最高分数
	solidroundrect(275, 30, 427, 119, 10, 10);
	settext(28, 800, 0xdbe6ee);
	printtext(_T("BEST"), 275, 40, 427, 69);
	ss << maxScore;
	settext(44, 800, WHITE);
	printtext(ss.str().c_str(), 275, 70, 427, 114);
	ss.str(_T(""));

	// 绘制提示信息
	settextcolor(BLACK);
	ss << "Join the numbers and get to the " << currentMaxBlock * 2 << " tile!";
	settext(24, 800, 0x707b83);
	printtext(ss.str().c_str(), 0, 120, 439, 211);

	// 绘制方块底板
	solidroundrect(12, 212, 427, 627, 10, 10);

	// 绘制方块
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			putimage(25 + 100 * j, 225 + 100 * i, &image[0]);
		}
	}
	for (int i = 0; i < 4; i++)
	{
		for (int j = 0; j < 4; j++)
		{
			if (blockMap[i][j] != NULL)
				blockMap[i][j]->draw();
		}
	}
}


// 初始化游戏
void Init()
{
	srand((unsigned int)time(NULL));		// 初始化随机数种子

	memset(map, 0, 4 * 4 * sizeof(int));	// 把地图初始化为 0
	memset(blockMap, 0, 4 * 4 * sizeof(Block*));

	score = 0;
	gameLoop = 1;
	gameOver = false;
	overTime = 0.5f;
	currentMaxBlock = 2;
	map[0][0] = 2;
	map[0][1] = 2;
	blockMap[0][0] = new Block({ 25, 225 }, &image[2]);
	blockMap[0][1] = new Block({ 125, 225 }, &image[2]);

	setbkcolor(WHITE);
	setbkmode(TRANSPARENT);
}


// 游戏结束界面 返回 1 表示继续游戏 返回 0 表示结束游戏
int OverInterface()
{
	// 保存最高纪录
	std::stringstream ss;
	ss << maxScore;
	WritePrivateProfileString(_T("2048"), _T("MaxScore"), ss.str().c_str(), _T(".\\data.ini"));
	ss.str(_T(""));
	ss << maxBlock;
	WritePrivateProfileString(_T("2048"), _T("MaxBlock"), ss.str().c_str(), _T(".\\data.ini"));

	setbkmode(TRANSPARENT);
	setbkcolor(0x8eecff);
	cleardevice();

	// Game Over
	settext(60, 1000, 0x696f78);
	printtext(_T("Game Over!"), 0, 0, 439, 199);

	// 绘制最大方块
	TransparentBlt(GetImageHDC(NULL), 175, 150, 90, 90, GetImageHDC(&image[currentMaxBlock]), 0, 0, 90, 90, 0x9eaebb);

	// ReStart
	setfillcolor(0x9dadba);
	solidroundrect(120, 310, 319, 389, 10, 10);
	settext(36, 1000, WHITE);
	printtext(_T("ReStart"), 120, 310, 319, 389);
	// Exit
	solidroundrect(120, 460, 319, 539, 10, 10);
	printtext(_T("Exit"), 120, 460, 319, 539);

	FlushBatchDraw();

	flushmessage( -1);
	ExMessage *msg = new ExMessage();
	while (1)
	{

		while (peekmessage(msg,  -1, true))
		{
			getmessage(msg,-1);
			if (msg->lbutton)
			{
				int x = msg->x;
				int y = msg->y;
				if (x >= 120 && x <= 319 && y >= 310 && y <= 389)
					return 1;
				if (x >= 120 && x <= 319 && y >= 460 && y <= 539)
					return 0;
			}
		}
		Sleep(100);
	}
	return 1;
}


// 释放内存
void FreeMem()
{
	for (int i = 0; i < 4; i++)
		for (int j = 0; j < 4; j++)
			if (blockMap[i][j] != NULL)
				delete blockMap[i][j];
}


// 用于生成方块图片
//		img:		方块图片指针
//		num:		方块上的数字
//		imgColor:	方块颜色
//		fontSize:	字体大小
//		fontColor:	字体颜色	
void CreateImage(IMAGE *img, LPCTSTR num, COLORREF imgColor, int fontSize, COLORREF fontColor)
{
	SetWorkingImage(img);
	setbkmode(TRANSPARENT);
	setbkcolor(0x9eaebb);
	settext(fontSize, 1000, fontColor);
	setfillcolor(imgColor);
	settextcolor(fontColor);

	cleardevice();

	solidroundrect(0, 0, img->getwidth() - 1, img->getheight() - 1, 10, 10);

	RECT r = { 0, 0, img->getwidth() - 1, img->getheight() - 1 };
	drawtext(num, &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}

// 绘制图片缓存
void Load()
{
	IMAGE temp(90, 90);

	CreateImage(&temp, _T(""), 0xb5becc, 72, WHITE);		image[0] = temp;
	CreateImage(&temp, _T("2"), 0xdbe6ee, 72, 0x707b83);		image[2] = temp;
	CreateImage(&temp, _T("4"), 0xc7e1ed, 72, 0x707b83);		image[4] = temp;
	CreateImage(&temp, _T("8"), 0x78b2f4, 72, WHITE);		image[8] = temp;
	CreateImage(&temp, _T("16"), 0x538ded, 72, WHITE);		image[16] = temp;
	CreateImage(&temp, _T("32"), 0x607df6, 72, WHITE);		image[32] = temp;
	CreateImage(&temp, _T("64"), 0x3958e9, 72, WHITE);		image[64] = temp;
	CreateImage(&temp, _T("128"), 0x6bd9f5, 56, WHITE);		image[128] = temp;
	CreateImage(&temp, _T("256"), 0x4bd0f2, 56, WHITE);		image[256] = temp;
	CreateImage(&temp, _T("512"), 0x2ac0e4, 56, WHITE);		image[512] = temp;
	CreateImage(&temp, _T("1024"), 0x13b8e3, 40, WHITE);		image[1024] = temp;
	CreateImage(&temp, _T("2048"), 0x00c5eb, 40, WHITE);		image[2048] = temp;
	CreateImage(&temp, _T("4096"), 0x3958e9, 40, WHITE);		image[4096] = temp;
	CreateImage(&temp, _T("8192"), 0x3958e9, 40, WHITE);		image[8192] = temp;

	SetWorkingImage(NULL);
}


// 主函数
int main()
{
	float deltaTime = 0;	// 每帧耗时

	initgraph(440, 650);
	Load();
	BeginBatchDraw();

	maxScore = 0;

	// 读取最高分
	maxScore = GetPrivateProfileInt(_T("2048"), _T("MaxScore"), 0, _T(".\\data.ini"));
	// 读取最大方块
	maxBlock = GetPrivateProfileInt(_T("2048"), _T("MaxBlock"), 2, _T(".\\data.ini"));

	while (1)
	{
		Init();

		while (gameLoop)
		{
			clock_t start = clock();

			cleardevice();
			Update(deltaTime);
			Draw();
			FlushBatchDraw();
			Sleep(1);

			clock_t end = clock();
			deltaTime = (end - start) / 1000.0f;
		}

		FreeMem();

		if (OverInterface() == 0)
			break;

		flushmessage(-1);
	}

	closegraph();
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++实现2048小游戏 的相关文章

  • 最全计算机图形学面试资料整理

    计算机图形学面试资料整理 1 什么是渲染管线2 有那几个坐标系 xff08 空间 xff09 xff1f 如何在空间之间进行转换 xff1f 3 三个重要的空间变换矩阵4 视口变换是什么 xff1f 5 顶点缓冲对象 xff08 Verte
  • OpenGL状态机

    OpenGL状态机 1 什么是状态机 xff1f 2 OpenGL状态机 1 什么是状态机 xff1f 先来解释什么是 状态 State 现实事物是有不同状态的 xff0c 例如一个自动门 xff0c 就有 open 和 closed 两种
  • OpenGL窗口实现

    OpenGL窗口实现 1 渲染循环包括的代码 xff1a 2 完整代码及解释3 实现的结果 1 渲染循环包括的代码 xff1a span class token comment 渲染循环 span span class token keyw
  • ubuntu安装nvidia显卡驱动

    系统环境 ubuntu 16 04 一 设定ubuntu的root密码 1 ubuntu默认root密码 在安装ubuntu操作系统时 xff0c 需要配置用于登录系统的用户名和密码 xff0c 却没有提示配置root密码 ubuntu默认
  • OpenGL绘制三角形

    OpenGL绘制三角形 1 可编程渲染管线2 标准化设备坐标 Normalized Device Coordinates 3 三角形顶点数据输入4 顶点着色器 Vertex Shader 编译顶点着色器 5 片段着色器 Fragment S
  • 多个着色器与多个VAO,VBO绘制三角形

    多个着色器与多个VAO VBO绘制三角形 设置不同片段着色器绘制两个三角形设置多个不同片段着色器绘制三个三角形 写在最前面 xff1a 如果对一个三角形的绘制过程还不明白建议先去看看 x1f449 这篇文章 设置不同片段着色器绘制两个三角形
  • OpenGL可编程着色器

    OpenGL着色器 1 什么是着色器2 着色器语言 xff08 GLSL xff09 2 1 数据类型2 2 着色器的输入与输出 3 Uniform3 1 绘制一个插值渐变三角形3 2 插值渐变三角形实现代码 xff1a 4 我们自己的着色
  • OpenGL纹理贴图

    OpenGL纹理 1 基本概念2 纹理环绕方式3 纹理过滤3 1 GL NEAREST3 2 GL LINEAR3 3 多级渐远纹理 Mipmap 4 纹理加载与创建4 1 纹理加载4 2 纹理创建 4 3 纹理的使用 1 基本概念 纹理是
  • OpenGL纹理叠加

    OpenGL纹理叠加 1 纹理贴图的实现2 纹理单元 在 OpenGL纹理贴图的基础上继续深入学习纹理相关内容 xff0c 本篇文章的主要目的是为了实现OpenGL纹理叠加 xff0c 需要引入 纹理单元的相关概念 1 纹理贴图的实现 按照
  • OpenGL中的变换

    OpenGL中的变换 1 矩阵变换1 1 缩放1 2 平移1 3 旋转1 4 矩阵组合 2 案例实现2 1 缩放 xff1a 2 1 1 等比缩放2 1 2 不等比缩放2 1 3 镜像X方向2 1 4 镜像Y方向 2 2 平移2 2 1 动
  • OpenGL坐标变换

    OpenGL坐标变换 1 坐标系统间的变换1 1 局部空间1 2 世界空间1 3 观察空间裁剪空间1 4 屏幕空间1 5 变换矩阵的组合 2 实现3 渲染部分代码 1 坐标系统间的变换 在渲染管线中 xff0c 物体的顶点在最终转化为屏幕坐
  • OpenGL立方体纹理贴图

    OpenGL正方体纹理贴图 0 写在最前面1 正方体顶点属性构建2 绑定多个VAO VBO3 创建多个纹理4 渲染循环5 实现代码6 多个立方体纹理贴图 0 写在最前面 要实现本篇的内容 xff0c 建议先学习下以下内容 x1f449 多个
  • OpenGL摄像机(Look At矩阵)

    OpenGL摄像机 1 摄像机 观察空间2 Look At3 lookAt 矩阵案例 1 摄像机 观察空间 观察矩阵将所有的世界坐标变换为相对于摄像机位置与方向的观察坐标 当定义一个摄像机时需要它在世界空间中的位置 观察的方向 一个指向它右
  • OpenGL摄像机键盘交互

    摄像机交互 对于摄像机基础实现内容较为生疏的可以参考该文章 x1f449 摄像机lookAt矩阵实现 考虑键盘交互实现opengl摄像机绕着目标物体自由移动 xff0c 首先需要设置一个摄像机系统 xff0c 定义lookAt函数变量 gl
  • Docker迁移以及环境变量问题

    问题一描述 将docker容器通过docker export命令打包 xff0c 传输到另外的服务器 xff0c 再通过docker import命令导入后 xff0c 发现原来docker容器中的环境变量失效了 解决方案 1 无效方案 直
  • OpenGL鼠标视角交互

    OpenGL鼠标视角交互 1 欧拉角2 鼠标输入 1 欧拉角 欧拉角 Euler Angle 是可以表示3D空间中任何旋转的3个值 xff0c 一共有3种欧拉角 xff1a 俯仰角 Pitch 偏航角 Yaw 和滚转角 Roll xff0c
  • 二维数组快速排序sort

    二维数组快速排序sort 1 使用比较函数cmp2 使用lambda表达式 使用c 43 43 的快排函数需要包含头文件 include lt algorithm gt 1 使用比较函数cmp span class token macro
  • OpenGL光照之颜色

    OpenGL光照之颜色 颜色什么是颜色如何表示物体颜色 光照场景的搭建 颜色 什么是颜色 颜色是通过眼 脑和我们的生活经验所产生的一种对光的视觉效应 xff0c 我们肉眼所见到的光线 xff0c 是由频率范围很窄的电磁波产生的 xff0c
  • OpenGL冯氏光照

    OpenGL冯氏光照 1 环境光照 Ambient lighting 2 漫反射光照 Diffuse lighting 3 镜面反射光照 Specular lighting 冯氏光照模型的主要结构由3个分量组成 xff1a 环境 Ambie
  • Magics软件中实现蒙皮偏置

    Magics软件中实现蒙皮偏置 首先导入目标模型 对目标模型进行修复检查 一般比较简单的模型可以直接使用修复即可自动实现修复 xff0c 比较复杂的情况可以使用工具栏里面的操作实现人工修复 xff0c 修复完成后模型的诊断应该都是绿色打钩

随机推荐

  • Geomagic网格重划分与模型光顺

    Geomagic网格重划分与模型光顺 1 网格重划分2 模型光顺 首先先导入模型 xff0c 可以看到模型还是相对较为粗糙 xff0c 面片的法矢方向差别很大 xff0c 因此需要重新划分下网格 1 网格重划分 网格重划分的步骤为 xff1
  • 点集的读入与输出操作

    在MFC框架里实现从文件中读取点集 1 点集读入操作实现代码 xff1a 2 点集输出操作实现代码 xff1a 1 点集读入操作实现代码 xff1a 读入点集数据 ID x y z span class token keyword void
  • OpenGL物体材质

    OpenGL物体材质 定义材质的意义如何实现物体材质物体材质的设置光的属性 定义材质的意义 在现实世界里 xff0c 每个物体会对光产生不同的反应 xff0c 每个物体对镜面高光也有不同的反应 有些物体反射光的时候不会有太多的散射 xff0
  • OpenGL光照贴图

    光照贴图 1 光照贴图的意义2 漫反射贴图3 镜面光贴图 1 光照贴图的意义 现实世界中的物体通常并不只包含有一种材质 xff0c 而是由多种材质所组成 只有光照颜色的材质系统是不够的 xff0c 它只是一个最简单的模型 xff0c 难以模
  • OpenGL光源

    OpenGL光源 0 预备知识点1 平行光2 点光源2 1 简化版点光源2 2 点光源 4 聚光灯4 1 柔化边缘 平滑 0 预备知识点 在学习本节之前 xff0c 需要先掌握一些基础光照的知识 推荐学习以下两篇问章 xff1a 计算机图形
  • GPU并行效率问题——通过MPS提升GPU计算收益

    现象描述 使用V100 32G型号的GPU运行计算程序时 xff0c 发现程序每5秒能够完成一次任务 xff0c 耗费显存6G 鉴于V100 GPU拥有32G的显存 xff0c 还有很多空闲 xff0c 决定同时运行多个计算程序 xff0c
  • 对象数组初始化

    对象数组初始化问题 为什么使用对象数组遇到的问题解决方法 为什么使用对象数组 为了满足用户创建同一个类的多个对象 遇到的问题 应使用 初始化聚合对象不存在默认构造函数 span class token macro property span
  • MFC Check-box与Button结合

    在MFC中经常需要一个按钮能够实现多种功能 xff0c 这个时候可以考虑通过Check box的勾选操作来与其配合实现目标效果 首先在对话框里添加Check box xff0c 并且修改Check box的ID 双击添加好的Check bo
  • MFC+Opencv以图拼图

    MFC 43 Opencv拼图 MFC对话框添加按钮图片准备Opencv配置代码实现 本篇文章主要通过将一系列图片作为拼图的基础 xff0c 实现目标图形的拼图 xff0c 我们先给个结果图可以更直观了解生成的结果是如何的 MFC对话框添加
  • 判断点集与多边形的位置关系

    判断点集与多边形的位置关系 点集与凸多边形点集与一般性多边形 点集与凸多边形 点集或者是点与多边形位置关系无非就三种 点在多边形的内部点在多边形的外部点在多边形上 一般如果该多边形是凸的那么我们可以直接用面积法或者向量法来解决位置关系 xf
  • 模型轮廓提取

    模型轮廓提取 模型边界 孔洞 xff09 识别算法 对于三角网格模型 xff0c 如果存在某一条边只属于一个三角面片 xff0c 那么这个边为模型边界 xff0c 称这条边为模型的边界边 如果一条边同时属于两个三角形 xff0c 则为内部边
  • Opencv推荐学习网站

    中文学习网站网址 xff1a http www opencv org cn http wiki opencv org cn index php E9 A6 96 E9 A1 B5
  • 摄像头动态捕捉小程序

    Opencv摄像头实时捕捉小程序 驱动摄像头加入Canny边缘检测加入Sobel边缘检测 先上一个好玩的动图看看结果 xff1a 驱动摄像头 首先先驱动摄像头 实现的代码如下 xff1a span class token macro pro
  • Opencv图像美颜滤镜

    图像美颜 span class token keyword void span span class token function BeautyFace span span class token punctuation span span
  • Simplify3D打印参数设置

    Simplify3D打印参数设置 关键是打印进程的设定 xff0c 这边以TPU为例子 xff0c 其他材料可以查阅相关参数进行喷头温度 xff0c 打印速度的修改 xff0c 其他的基本一致 设置完成后进行模型切片 xff0c 完成后点击
  • CSDN如何转载一篇博客呢

    看到一篇挺好的文章想要转载怎么做呢 xff1f 方法一 xff1a 傻瓜式复制 不推荐 方法二 后台操作 推荐 方法一 xff1a 傻瓜式复制 不推荐 一个个图文抠 xff0c 效率低下 xff0c 且版面格式会发生变化 方法二 后台操作
  • Exchange 2013升级以及域名绑定等若干问题

    环境简介 Exchange 2013服务器位于ad域中 xff0c 系统为Windows server 2012 R2 xff0c 其内部域名为 xff1a mail ad com 一 Exchange客户端无法在浏览器中正常运行 在域中部
  • 无线攻击 --Wifite(批量破解无线工具)

    文章目录 一 用法概述二 命令格式三 用法示例3 1 破解某个AP 一 用法概述 Wifite使用命令行界面连续攻击多个WPA WPS加密的网络 xff0c 不需要记住参数即可使用它 xff1a 按信号强度排序 xff08 db单位 xff
  • 图像轮廓提取算法(Opencv基于C++实现)

    Opencv图像轮廓提取 0 实现结果如下 xff1a 1 打开图像代码2 轮廓提取函数3 代码实现 本文主要实现了图像的轮廓提取 xff0c 首先先给出直观的轮廓实现结果 xff1a 0 实现结果如下 xff1a 1 打开图像代码 注意图
  • c++实现2048小游戏

    C 43 43 实现2048 2048小游戏界面展示效果图 xff1a span class token macro property span class token directive hash span span class toke