游戏扫雷 鼠标操作 C语言

前言

  这篇文章是我学习C语言后实现自己给自己定的小目标的记录性博客,这是第一篇比较系统的、但更多的是从我自身的角度出发写的博客。

​  在决定开始做这个小游戏后,我在网上找了很多资料。在这个版本之前有一个夭折的版本,因为那个对于现在的我来说有些原理没懂、功能也比较复杂,所以选了一个比较简单的试试手。这个做完之后准备做一个图书管理系统啥的就开始学习其他的了。

  当然,这个虽然简单的版本,但我依然有一个问题没解决,就是给游戏加背景音乐。原理代码都对,运行起来也没问题,但就是没声音,自己也去百度了很久也没解决,如有大佬看了之后知道的话请留下宝贵建议,感谢!!!

游戏分析

1.预期效果

  开局----
在这里插入图片描述

进行----
在这里插入图片描述

结束----

在这里插入图片描述

具体内容

1.游戏原理

(1)通过玩家点击方框,判断是否踩雷。没有踩到雷游戏继续,踩到雷则游戏结束。

(2)l雷的数量随机生成,但有最小值和最大值。

(3)雷的分布要与生成的随机数相同,分布合理,不能矛盾。

2.游戏模式

(1)鼠标点击,玩家每点击一次就要判断游戏输赢,点到雷玩家输,雷扫完则玩家胜利。

3.游戏实现

(1)使用的是vs2019 C语言

(2)逻辑思维

游戏主体框架如下

在这里插入图片描述

4.具体过程–我直接开干

1.思路 --------一边写一边进行测试 按主体框架一步一步实现

(1)

过于具体的步骤就不写了
这是我在敲完代码后来写的,更多的都在注释里
而且能看这篇的应该都是比我牛的,我比较菜

在这里插入图片描述

头文件在最开始没有直接写完,需要什么加什么。

;不过easyx图形库是需要事先下好的,图形库的使用下载可以到官网了解。

(2)

宏定义 

在这里插入图片描述

通过宏定义我们可以更直观更方便一些数据的更改,不用到具体的语句中更改。

(3)

函数声明及定义
函数的功能 就是具体实现我们每一个需要 定义极为重要

在这里插入图片描述

函数定义

在这里插入图片描述

初始化函数
void GameInit()
{
    //所有数组元素赋值为0 包括辅助区,辅助区元素值必须为0
    for (int i=0; i<ROW+2;i++)
    {
        for (int j=0;j<COL+2;j++)
        {
			map[i][j]=0;
        }
    }
    
    //随机位置生成雷 赋值为-1 除辅助区以外的位置 
    int n = 0;
	while (n < NUM)
	{
		int r = rand() % ROW + 1; //行
		int c = rand() % COL + 1; //列

		if (map[r][c] == 0)
		{
			map[r][c] = -1; //该位置为雷
			n++;
		}
	}
    
    	//根据雷的分布 填充其他位置的数据
	// 遍历数组 找雷
	for (int m = 1; m <= ROW; m++)
	{
		for (int k = 1; k <= COL; k++)
		{
			if (map[m][k] != -1) //找到不为雷的元素
			{
				for (int i = m - 1; i <= m + 1; i++)
				{
					for (int j = k - 1; j <= k + 1; j++)
					{
						if (map[i][j] == -1) //找到一个雷
						{
							map[m][k]++;
						}
					}
				}
			}
		}
	}
    //打印初始页面  
	for (int m = 1; m <= ROW; m++)
	{
		for (int k = 1; k <= COL; k++)
		{
			map[m][k] += 20;   
		}
	}
    
}

遍历九宫格找雷,这里对于边界来说就需要辅助区,辅助区元素的值全部初始化为不为0。

在这里插入图片描述
在这里插入图片描述

打印游戏界面函数
void GameDraw() //打印
{

	for (int i = 0; i < ROW + 2; i++)
	{
		for (int j = 0; j < COL + 2; j++)
		{
			printf("%2d ", map[i][j]);
			if (map[i][j] == -1)
			{
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[9]); //雷
			}
			else if (map[i][j] >= 0 && map[i][j] <= 8)
			{
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[map[i][j]]);//周围雷的数量
			}
			else if (map[i][j] >= 19 && map[i][j] <= 28)
			{
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[10]); //空白图片
			}
			else if (map[i][j] >= 30)
			{
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[11]); //标记图片 旗子
			}
		}
		printf("\n");  //换行
	}
}

这里面的数字是图片素材的名字。
在这里插入图片描述

//  这些函数的具体定义可以自行查看	
// 图像处理函数

void loadimage(IMAGE *pDstImg, LPCTSTR pImgFile, int nWidth = 0, int nHeight = 0, bool bResize = false);					
// 从图片文件获取图像(bmp/gif/jpg/png/tif/emf/wmf/ico)

void loadimage(IMAGE *pDstImg, LPCTSTR pResType, LPCTSTR pResName, int nWidth = 0, int nHeight = 0, bool bResize = false);	
// 从资源文件获取图像(bmp/gif/jpg/png/tif/emf/wmf/ico)

void saveimage(LPCTSTR pImgFile, IMAGE* pImg = NULL);									// 保存图像(bmp/gif/jpg/png/tif)

void getimage(IMAGE *pDstImg, int srcX, int srcY, int srcWidth, int srcHeight);			// 从当前绘图设备获取图像

void putimage(int dstX, int dstY, const IMAGE *pSrcImg, DWORD dwRop = SRCCOPY);			// 绘制图像到屏幕

void putimage(int dstX, int dstY, int dstWidth, int dstHeight, const IMAGE *pSrcImg, int srcX, int srcY, DWORD dwRop = SRCCOPY);		
// 绘制图像到屏幕(指定宽高)

void rotateimage(IMAGE *dstimg, IMAGE *srcimg, double radian, COLORREF bkcolor = BLACK, bool autosize = false, bool highquality = true);
// 旋转图像

void Resize(IMAGE* pImg, int width, int height);	// 调整绘图设备的大小
DWORD* GetImageBuffer(IMAGE* pImg = NULL);			// 获取绘图设备的显存指针
IMAGE* GetWorkingImage();							// 获取当前绘图设备
void SetWorkingImage(IMAGE* pImg = NULL);			// 设置当前绘图设备
HDC GetImageHDC(IMAGE* pImg = NULL);				// 获取绘图设备句柄(HDC)

									
游戏函数
int PlayGame()
{
	MOUSEMSG msg = { 0 };   //定义鼠标信息
	int r, c;

	while (1)
	{
		msg = GetMouseMsg();
		switch (msg.uMsg)
		{  
		case WM_LBUTTONDOWN:  //翻开图片 空白  +1是因为有辅助区的存在
			r = msg.x / SIZE + 1;   
			c = msg.y / SIZE + 1;
			if (map[r][c] >= 19 && map[r][c] <= 28)
			{
				if (map[r][c] == 20)
				{
					Openzero(r, c); //递归  打开周围空白格子 即如果周围没有雷则打开
				}
				else
				{
					map[r][c] -= 20;
					count++;
				}
			}
			return map[r][c];
			break;
		case WM_RBUTTONDOWN: //标记空白图片 取消已标记图片
			r = msg.x / SIZE + 1;
			c = msg.y / SIZE + 1;
			if (map[r][c] >= 19 && map[r][c] <= 28)
			{
				map[r][c] += 50;
			}
			else if (map[r][c] >= 30)
			{
				map[r][c] -= 50;
			}
			return map[r][c];
			break;
		}
	}
}
//鼠标操作函数   其中有很多没有用到但可以了解 也可自行查看
// “鼠标操作”相关函数
// 鼠标消息
// 支持如下消息:
//		WM_MOUSEMOVE		鼠标移动
//		WM_MOUSEWHEEL		鼠标滚轮拨动
//		WM_LBUTTONDOWN		左键按下     用到的
//		WM_LBUTTONUP		左键弹起
//		WM_LBUTTONDBLCLK	左键双击
//		WM_MBUTTONDOWN		中键按下
//		WM_MBUTTONUP		中键弹起
//		WM_MBUTTONDBLCLK	中键双击
//		WM_RBUTTONDOWN		右键按下     用到的
//		WM_RBUTTONUP		右键弹起
//		WM_RBUTTONDBLCLK	右键双击
struct MOUSEMSG
{
	UINT uMsg;				// 当前鼠标消息     用到的
	bool mkCtrl		:1;		// Ctrl 键是否按下
	bool mkShift	:1;		// Shift 键是否按下
	bool mkLButton	:1;		// 鼠标左键是否按下
	bool mkMButton	:1;		// 鼠标中键是否按下
	bool mkRButton	:1;		// 鼠标右键是否按下
	short x;				// 当前鼠标 x 坐标
	short y;				// 当前鼠标 y 坐标
	short wheel;			// 鼠标滚轮滚动值 (120 的倍数)
};
_EASYX_DEPRECATE	bool MouseHit();			// 检查是否存在鼠标消息

_EASYX_DEPRECATE_WITHNEW(getmessage)  MOUSEMSG GetMouseMsg();		/* 获取一个鼠标消息。如果没有,就等待*/
_EASYX_DEPRECATE_WITHNEW(peekmessage)	bool PeekMouseMsg(MOUSEMSG *pMsg, bool bRemoveMsg = true);	// 获取一个鼠标消息,并立即返回
_EASYX_DEPRECATE_WITHNEW(flushmessage)	void FlushMouseMsgBuffer();	// 清空鼠标消息
typedef ExMessage EASYXMSG;	// 兼容旧的消息结构体名称
打开周围格子函数
void Openzero(int i, int j)
{
	//先打开这个0
	map[i][j] -= 20;
	count++;

	//辅助区
	for (int m = i - 1; m <= i + 1; m++)
	{
		for (int n = j - 1; n <= j; n++)
		{
			if (m >= 1 && m <= ROW && n >= 1 && n <= COL)  //判断是否在游戏区内
			{
				if (map[m][n] >= 19 && map[m][n] <= 28)   
				{
					if (map[m][n] != 20)
					{
						map[m][n] -= 20;
						count++;
					}
					else
					{
						Openzero(m, n);  //递归调用  目前我还不是很能理解 但能用
					}
				}
			}
		}
	}
}

总结

  最后小小总结一下 ,这个小游戏比起之前的学习它更需要我去主动找资料,需要用到什么来解决这个需求,主动的去学习,在这个过程中很痛苦但也很快乐,痛苦的是找资料解决问题时自己需要不断筛选优质解答并且去实践,快乐的是在这个过程中可以发现很多其他的东西,开阔视野。

​   这个小游戏的让我了解了鼠标操作、句柄、图形库的简单使用,让我对于一整个小项目(自己做着玩的)的流程有了一个简单了解,有了一个整体思维。所有的想法不能仅在脑子里面想,要去一步步的实现。想出来和做出来还是有很多不一样的地方。
  学无止境!!
  这篇文章的写作也让我用到了很多以前不知道的软件,截图软件是Snipaste,笔记软件是Typora,工具类是utools。

在这里插入代码片
//头文件
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<graphics.h>
#include<windows.h>
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
//宏定义
#define ROW 10  //行
#define COL 10  //列
#define NUM 10  //雷的数量
#define SIZE 50 //图片大小

//函数声明
int map[ROW + 2][COL + 2];  //游戏内棋盘
void GameInit();    //初始化函数
void GameDraw();    //绘制函数
IMAGE img[12];    //图片
void Openzero(int i, int j); //打开0周围的格子
int PlayGame(); //游戏点击
int count = 0;


//函数定义
void GameInit() //初始化
{
	//随机数种子
	srand((unsigned int)time(NULL));

	//初始赋值所有元素为0
	for (int i = 0; i < ROW + 2; i++)
	{
		for (int j = 0; j < COL + 2; j++)
		{
			map[i][j] = 0;
		}
	}

	//设置雷 -1  随机位置
	int n = 0;
	while (n < NUM)
	{
		int r = rand() % ROW + 1; //行
		int c = rand() % COL + 1; //列

		if (map[r][c] == 0)
		{
			map[r][c] = -1; //该位置为雷
			n++;
		}
	}

	//根据雷的分布 填充其他位置的数据
	// 遍历数组 找雷
	for (int m = 1; m <= ROW; m++)
	{
		for (int k = 1; k <= COL; k++)
		{
			if (map[m][k] != -1) //找到不为雷的元素
			{
				for (int i = m - 1; i <= m + 1; i++)
				{
					for (int j = k - 1; j <= k + 1; j++)
					{
						if (map[i][j] == -1) //找到一个雷
						{
							map[m][k]++;
						}
					}
				}
			}
		}
	}
	//打印初始页面  
	for (int m = 1; m <= ROW; m++)
	{
		for (int k = 1; k <= COL; k++)
		{
			map[m][k] += 20;
		}
	}

}

void GameDraw() //打印
{

	for (int i = 0; i < ROW + 2; i++)
	{
		for (int j = 0; j < COL + 2; j++)
		{
			printf("%2d ", map[i][j]);
			if (map[i][j] == -1)
			{
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[9]); //雷
			}
			else if (map[i][j] >= 0 && map[i][j] <= 8)
			{
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[map[i][j]]); //数字
			}
			else if (map[i][j] >= 19 && map[i][j] <= 28)
			{
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[10]); //空白图片
			}
			else if (map[i][j] >= 30)
			{
				putimage((i - 1) * SIZE, (j - 1) * SIZE, &img[11]); //标记图片
			}
		}
		printf("\n");
	}
}

void Openzero(int i, int j)
{
	//先打开这个0
	map[i][j] -= 20;
	count++;

	//辅助区
	for (int m = i - 1; m <= i + 1; m++)
	{
		for (int n = j - 1; n <= j; n++)
		{
			if (m >= 1 && m <= ROW && n >= 1 && n <= COL)
			{
				if (map[m][n] >= 19 && map[m][n] <= 28)
				{
					if (map[m][n] != 20)
					{
						map[m][n] -= 20;
						count++;
					}
					else
					{
						Openzero(m, n);
					}
				}
			}
		}
	}
}

int PlayGame()
{
	MOUSEMSG msg = { 0 };   //定义鼠标信息
	int r, c;

	while (1)
	{
		msg = GetMouseMsg();
		switch (msg.uMsg)
		{
		case WM_LBUTTONDOWN:  //翻开图片 空白
			r = msg.x / SIZE + 1;
			c = msg.y / SIZE + 1;
			if (map[r][c] >= 19 && map[r][c] <= 28)
			{
				if (map[r][c] == 20)
				{
					Openzero(r, c);
				}
				else
				{
					map[r][c] -= 20;
					count++;
				}
			}
			return map[r][c];
			break;
		case WM_RBUTTONDOWN: //标记空白图片 取消已标记图片
			r = msg.x / SIZE + 1;
			c = msg.y / SIZE + 1;
			if (map[r][c] >= 19 && map[r][c] <= 28)
			{
				map[r][c] += 50;
			}
			else if (map[r][c] >= 30)
			{
				map[r][c] -= 50;
			}
			return map[r][c];
			break;
		}
	}
}
int main()
{
	//打开窗口
	HWND hwnd = initgraph(ROW * SIZE, COL * SIZE, SHOWCONSOLE);
	//播放音乐
	mciSendString(L"open Tobu&Itro - Sunburst.mp3 alias music", NULL, 0, NULL);
	mciSendString(L"play music ", NULL, 0, NULL);
	//system("pause");
	loadimage(&img[0], L"0.jpg", SIZE, SIZE);
	loadimage(&img[1], L"1.jpg", SIZE, SIZE);
	loadimage(&img[2], L"2.jpg", SIZE, SIZE);
	loadimage(&img[3], L"3.jpg", SIZE, SIZE);
	loadimage(&img[4], L"4.jpg", SIZE, SIZE);
	loadimage(&img[5], L"5.jpg", SIZE, SIZE);
	loadimage(&img[6], L"6.jpg", SIZE, SIZE);
	loadimage(&img[7], L"7.jpg", SIZE, SIZE);
	loadimage(&img[8], L"8.jpg", SIZE, SIZE);
	loadimage(&img[9], L"9.jpg", SIZE, SIZE);
	loadimage(&img[10], L"10.jpg", SIZE, SIZE);
	loadimage(&img[11], L"11.jpg", SIZE, SIZE);

	GameInit();

	while (1)
	{
		GameDraw();
		if (PlayGame() == -1)
		{
			GameDraw();
			MessageBox(hwnd, L" 你 没 了!!!", L"", MB_OK);
			break;
		}
		if ((ROW * COL - NUM) == count)
		{
			GameDraw();
			MessageBox(hwnd, L" 你 赢 了!!!", L"", MB_OK);
			break;
		}

	}
	closegraph();

	return 0;
}
  • 20
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值