C语言简易小游戏实现—2048

马肤
这是懒羊羊

一、概述

2048小游戏是一款休闲益智类小游戏,游戏在一个4×4的方格上进行,在这里会不断产生方块,方块的初始值为2,玩家通过合并、消除方块使得方块的数值越来越大,最终合成2048的方块来获取胜利,游戏的核心功能逻辑简单易实现,通过C语言的函数功能,同时配合easyx图形库美化游戏界面,一个简单的2048小游戏即可实现。

二、各部分功能实现

对主要功能进行分析后,将其分成了六个函数如下:

1. 随机数产生函数

用于产生整数2或4,这里设置产生2的概率为90%,产生4的概率为10%

void createNum()
{
	while (1)
	{
		int ret = 0;
		int x = rand() % MAX_SIZE;   //创建一个随机数对MAX_SIZE 取余结果坐标为0,1,2,3
		int y = rand() % MAX_SIZE;   //创建一个随机数对MAX_SIZE 取余结果坐标为0,1,2,3
		if (map[x][y] == 0) {     //查看随机的坐标是否等于0
			if (rand() % 10 == 1)    //判断随机数取10余数是否为1,来限制4出现的几率为10%
			{
				map[x][y] = 4;
			}
			else
			{
				map[x][y] = 2;    //否则2出现的几率为90%  
			}
			break;
		}
	}
}
2. 游戏初始化函数

初始化游戏,重置游戏方格区域的数据,并在两个随机位置产生2或4,概率同上,由此不难发现,该函数只需要在重置方格数据和生成两个随机位置后,调用以上随机数产生函数即可

void gameInit()
{
	for (int i = 0; i  
3. 游戏界面函数(核心)

由于应用了easyx图形库,因此该函数的主要功能就是使用easy图形库的各种命令来优化图形界面,例如打印界面,设置各个区域以及不同数值方块相对应的颜色,打印得分区域和新游戏按钮等

void gameView()
{
	//打印方格、设置颜色、打印方格值
	for (int i = 0; i  
4. 移动函数(核心)

游戏的核心逻辑部分,接收到玩家的键盘指令后,需要做出相对应的移动逻辑,并且根据方格区域不同的情况要做出不同的反应。例如所要移动的方向上无相同值方块可合并所有方块都到达边界无法前进,此时做出的回应是不执行任何命令,等待下一个键盘指令;而前方若有可合并方块,则需要改变前方可合并方块的数值,使其翻倍,并使得距离所要移动方向更远的方块消失,而若方块朝向索要移动的方向上无其他方块,则需使其向着该方向移动至边界,而若同一行内有三或四个相同数值的方块,则需要先从靠近所移动方向的一侧开始遍历合并,并且每个方块每回合只能合并一次,合并后还需要继续前进直到遇到其他方块或边界,为此,将移动部分拆分为四个函数,根据不同情况产生不同的指令,其根本逻辑都是一样的,只是跟据移动方向的不同会改变遍历的初始位置和方向,此处只展示上移函数,其余移动函数请见完整代码部分

void moveUp()
{
	bool isMoved = 0;	//是否移动成功
	for (int i = 0; i  
5. 胜负判定函数

根据当前方格区域的方块数值分布情况来确定胜利或失败,若生成了2048的方块,则游戏胜利,返回数值1;若方格填满且所有方块无法继续合并免责判定游戏失败,返回数值2;否则判定胜负未定,返回数值3;若胜负已定,则会调用胜利/失败界面函数

int isWin()
{
	int ret = 2; //定义判别变量,初始状态为失败
	for (int i = 0; i  
6. 胜利/失败界面函数

根据胜负判定函数的返回值来打印游戏胜利/失败界面

void winOrLoseView(int det)
{
	//接收isWin返回的参数,若为1打印胜利字样,为2打印失败字样
	HWND window = GetHWnd();          //获取窗口句柄
	if (det == 1)     //胜利判定
	{
		MessageBox(window, "You won the game!", "提示", MB_OK);  //打印提示
		gameInit();  //新开始游戏
	}
	else if (det == 2)
	{   //失败判定
		MessageBox(window, "Game over!", "提示", MB_OK);  //打印提示
		gameInit();  //新开始游戏
	}
}
7. 定义主函数
int main(void)
{
	//创建游戏窗口,尺寸为500 * 700
	initgraph(GAME_ZONE + 200, GAME_ZONE);    //创建游戏窗口
	//设置背景(方格间距)颜色  
	setbkmode(TRANSPARENT);      //设置窗口为透明色
	setbkcolor(INTERVRL_COLOR);   //设置背景颜色
	cleardevice();     //清除背景
	//打印游戏右侧区域并设置颜色
	setfillcolor(RIGHT_RECTANGLE);    //设置矩形填充颜色
	solidrectangle(GAME_ZONE, 0, GAME_ZONE + 200, GAME_ZONE);     //创建游戏额外区域的矩形
	//打印并设置积分板颜色,并打印对应字体
	setfillcolor(SCORE_COLOR);             //设置游戏界面右侧的颜色
	solidroundrect(530, 40, 670, 100, 5, 5); //得分板的左上和右下坐标分别为(530,40), (670,100)   创建得分板
	settextcolor(RGB(238, 228, 218));        //设置字体颜色
	settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //设置文字大小和字体
	outtextxy(565, 40, "SCORE");      //出入文字
	//打印New Gme按钮以及字体并设置字体颜色和格式
	setfillcolor(NEWGAME_COLOR);          //设置新游戏按钮区域背景颜色
	solidroundrect(525, 160, 675, 210, 5, 5); //New Game区域的左上和右下坐标分别为(525,160), (675,210)       
	int btw = textwidth("New Game");     //文字宽距离按钮居中更多距离
	int bth = textheight("New Game");    //文字高距离按钮居中更多距离
	settextcolor(WHITE);   //设置文字颜色为白色
	settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0);    //设置文字的字体和大小
	outtextxy(525 + (150 - btw) / 2, 160 + (50 - bth) / 2, "New Game");  //将文字居中放在矩形中
	gameInit();
	gameView();    //调用函数
	ExMessage m; //定义接收键鼠输入的变量
	//开始游戏,置于恒定循环内
	BeginBatchDraw(); //开始批量绘图,防止游戏过程中闪屏
	while (1)
	{
		gameView();
		//每轮开始时,进行一次胜负判定,若胜负未定则继续游戏,否则调用胜利/失败界面函数
		int det = isWin();
		if (det != 0) winOrLoseView(det);
		//接收键鼠输入
		m = getmessage(EX_MOUSE | EX_KEY);
		switch (m.message)
		{
		case WM_KEYDOWN: //如果键盘输入wasd或上下左右键,则做出相对应移动指令
			if (m.vkcode == VK_UP || m.vkcode == 'w' || m.vkcode == 'W') moveUp();
			if (m.vkcode == VK_DOWN || m.vkcode == 's' || m.vkcode == 'S')moveDown();
			if (m.vkcode == VK_LEFT || m.vkcode == 'a' || m.vkcode == 'A')moveLeft();
			if (m.vkcode == VK_RIGHT || m.vkcode == 'd' || m.vkcode == 'D')moveRight();
			break;
		case WM_LBUTTONDOWN: //若点击了New game按钮所在区域,则调用gameInit函数,即初始化游戏数据并打印游戏界面
			if (m.x >= 525 && m.x = 160 && m.y  

三、完整代码

#include 
#include  //easyx图形库头文件
#include 
#include 
#define GRID_W 100 //格子宽度(正方形边长)
#define INTERVAL 20 //格子间距
#define MAX_SIZE 4 //每行每列各自的最大数量
#define GAME_ZONE MAX_SIZE * GRID_W + (MAX_SIZE + 1) * INTERVAL //游戏格子区域的边长(实际为500)
//枚举出游戏中用到的颜色
enum color
{
	BLANK_GRID = RGB(215, 203, 190), //空格子颜色
	INTERVRL_COLOR = RGB(187, 173, 160), //间距颜色,在函数中设定为背景颜色
	RIGHT_RECTANGLE = RGB(250, 248, 219), //窗口右侧矩形区域颜色,即除格子和间隔以外的颜色
	SCORE_COLOR = RGB(187, 173, 160), //计分板颜色
	NEWGAME_COLOR = RGB(143, 122, 102), //新游戏按钮颜色
	TWO_ONE = RGB(238, 228, 218), //方格值为2的颜色
	TWO_TWO = RGB(237, 224, 200), //方格值为4的颜色
	TWO_THREE = RGB(242, 177, 121), //方格值为8的颜色
	TWO_FOUR = RGB(245, 149, 99), //方格值为16的颜色
	TWO_FIVE = RGB(246, 124, 95), //方格值为32的颜色
	TWO_SIX = RGB(246, 94, 59), //方格值为64的颜色
	TWO_SEVEN = RGB(237, 207, 114), //方格值为128的颜色
	TWO_EIGHT = RGB(237, 204, 97), //方格值为256的颜色
	TWO_NINE = RGB(237, 200, 80), //方格值为512的颜色
	TWO_TEN = RGB(237, 197, 63), //方格值为1024的颜色
	TWO_ELEVEN = RGB(237, 194, 46), //方格值为2048的颜色
};
color arr[12] = { BLANK_GRID, TWO_ONE, TWO_TWO, TWO_THREE, TWO_FOUR, TWO_FIVE, TWO_SIX, TWO_SEVEN, TWO_EIGHT, TWO_NINE, TWO_TEN, TWO_ELEVEN, }; //枚举颜色数组
int map[MAX_SIZE][MAX_SIZE]; //定义一个数组,用于存储游戏区域数据,全局变量自动初始化为零
int score; //定义全局变量得分值,会被打印在得分板上
//随机数产生函数
void createNum();
//游戏初始化函数
void gameInit();
//游戏界面函数
void gameView();
//游戏移动函数
void moveUp(); //上移
void moveDown(); //下移
void moveLeft(); //左移
void moveRight(); //右移
//胜负判定函数
int isWin();
//胜利/失败界面函数
void winOrLoseView(int det);
//定义主函数
int main(void)
{
	//创建游戏窗口,尺寸为500 * 700
	initgraph(GAME_ZONE + 200, GAME_ZONE);    //创建游戏窗口
	//设置背景(方格间距)颜色  
	setbkmode(TRANSPARENT);      //设置窗口为透明色
	setbkcolor(INTERVRL_COLOR);   //设置背景颜色
	cleardevice();     //清除背景
	//打印游戏右侧区域并设置颜色
	setfillcolor(RIGHT_RECTANGLE);    //设置矩形填充颜色
	solidrectangle(GAME_ZONE, 0, GAME_ZONE + 200, GAME_ZONE);     //创建游戏额外区域的矩形
	//打印并设置积分板颜色,并打印对应字体
	setfillcolor(SCORE_COLOR);             //设置游戏界面右侧的颜色
	solidroundrect(530, 40, 670, 100, 5, 5); //得分板的左上和右下坐标分别为(530,40), (670,100)   创建得分板
	settextcolor(RGB(238, 228, 218));        //设置字体颜色
	settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0); //设置文字大小和字体
	outtextxy(565, 40, "SCORE");      //出入文字
	//打印New Gme按钮以及字体并设置字体颜色和格式
	setfillcolor(NEWGAME_COLOR);          //设置新游戏按钮区域背景颜色
	solidroundrect(525, 160, 675, 210, 5, 5); //New Game区域的左上和右下坐标分别为(525,160), (675,210)       
	int btw = textwidth("New Game");     //文字宽距离按钮居中更多距离
	int bth = textheight("New Game");    //文字高距离按钮居中更多距离
	settextcolor(WHITE);   //设置文字颜色为白色
	settextstyle(30, 0, "Microsoft YaHei UI", 0, 0, 1000, 0, 0, 0);    //设置文字的字体和大小
	outtextxy(525 + (150 - btw) / 2, 160 + (50 - bth) / 2, "New Game");  //将文字居中放在矩形中
	gameInit();
	gameView();    //调用函数
	ExMessage m; //定义接收键鼠输入的变量
	//开始游戏,用一个恒定循环定义
	BeginBatchDraw(); //开始批量绘图,防止游戏过程中闪屏
	while (1)
	{
		gameView();
		//每轮开始时,进行一次胜负判定,若胜负未定则继续游戏,否则调用胜利/失败界面函数
		int det = isWin();
		if (det != 0) winOrLoseView(det);
		//接收键鼠输入
		m = getmessage(EX_MOUSE | EX_KEY);
		switch (m.message)
		{
		case WM_KEYDOWN: //如果键盘输入wasd或上下左右键,则做出相对应移动指令
			if (m.vkcode == VK_UP || m.vkcode == 'w' || m.vkcode == 'W') moveUp();
			if (m.vkcode == VK_DOWN || m.vkcode == 's' || m.vkcode == 'S')moveDown();
			if (m.vkcode == VK_LEFT || m.vkcode == 'a' || m.vkcode == 'A')moveLeft();
			if (m.vkcode == VK_RIGHT || m.vkcode == 'd' || m.vkcode == 'D')moveRight();
			break;
		case WM_LBUTTONDOWN: //若点击了New game按钮所在区域,则调用gameInit函数,即初始化游戏数据并打印游戏界面
			if (m.x >= 525 && m.x = 160 && m.y = 0; next--)//遍历行
		{
			if (map[next][i] != 0)
			{
				if (map[now][i] == 0)
				{
					map[now][i] = map[next][i];
					map[next][i] = 0;
					isMoved = 1;
				}
				else if (map[now][i] == map[next][i])
				{
					map[now][i] *= 2;
					score += map[now][i];
					map[next][i] = 0;
					isMoved = 1;
					now--;
				}
				else {
					map[now - 1][i] = map[next][i];
					if (now - 1 != next)
					{
						map[next][i] = 0;
						isMoved = 1;
					}
					now--;
				}
			}
		}
	}
	if (isMoved)
	{
		createNum();
	}
	gameView();  //打印一次游戏界面
}
void moveLeft()
{
	//代码实现
	bool isMoved = 0;	//是否移动成功
	for (int i = 0; i = 0; next--)	//遍历列
		{
			//判断next处是不是0
			if (map[i][next] != 0)
			{
				if (map[i][now] == 0)
				{
					map[i][now] = map[i][next];
					map[i][next] = 0;
					isMoved = 1;
				}
				else if (map[i][now] == map[i][next])
				{
					map[i][now] *= 2;
					score += map[i][now];
					map[i][next] = 0;
					now--;
					isMoved = 1;
				}
				else
				{
					map[i][now - 1] = map[i][next];
					if (now - 1 != next)
					{
						map[i][next] = 0;
						isMoved = 1;
					}
					now--;
				}
			}
		}
	}
	if (isMoved)
	{
		createNum();
	}
	gameView(); //打印一次游戏界面
}
int isWin()
{
	//代码实现
	int ret = 2; //定义判别变量,初始状态为游戏失败
	for (int i = 0; i  

成品示例:

四、总结

可以看出,整个游戏从构思倒实现还是比较简单的,总代码量也只有450行左右,只是一个功能比较简略、仅仅实现了核心功能和一点点拓展功能的版本,实际上该游戏的可扩展之处还有很多,例如音乐、主菜单、游戏设置、图形界面字体的优化、游戏的特效以及设置更多游戏模式等,这代表着实现从深度到广度的延伸,使得游戏更具有可玩性,广大开发者可自行构思、创造。


文章版权声明:除非注明,否则均为VPS857原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复:表情:
评论列表 (暂无评论,0人围观)

还没有评论,来说两句吧...

目录[+]

取消
微信二维码
微信二维码
支付宝二维码