前言
这篇文章是我学习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;
}