基于easyx库的GUI扫雷项目

文章目录

  • 基于easyx库的GUI扫雷项目
    • 0.观前提醒
    • 1.扫雷游戏项目效果展示
    • 2.扫雷游戏项目基本信息
    • 3.扫雷游戏项目设计思路
    • 4.扫雷游戏实现原理
      • 4.1 头文件解析
      • 4.2 素材解析
      • 4.3 变量与矩形布局解析
      • 4.4 随机雷的实现
      • 4.5 生成雷位置矩阵的实现
      • 4.6 生成雷数矩阵的实现
      • 4.7 绘制图像过程
      • 4.8 输赢检测规则
      • 4.9 检测相邻空元素的方法
      • 4.10 扩展空元素区域的方法
      • 4.11 绘制状态贴图的方法
      • 4.12 展示地雷的方法
      • 4.13 鼠标交互逻辑解析
      • 4.14 游戏获胜与失败判定原理
      • 4.15 自定义颜色制定

0.观前提醒

本程序为本人原创,没有参考任何其他资料或博客,图片素材基于原版扫雷图片

制作带GUI的扫雷游戏项目主要是为了练习自己的C语言基础和学习使用图形化库,我已经将此项目上传了,欢迎大家点击下面链接直接下载。写这篇博客一是为了记录自己曾经练习写代码的过程,二是分享给更多感兴趣的朋友。我将项目捏碎了给大家讲解了出来,相信只要具有一定的耐心,即使基础很差的朋友,也能随便写出来!

源码里的游戏本体有很多步骤可以放入函数进行执行的,这样代码看起来更加简洁。由于我写这个游戏的时候在高铁上,有些不方便,所以我并没有进行功能合并,有兴趣的朋友,自己new个函数将功能合并吧

源码地址:基于easyx图形库做的GUI版扫雷


1.扫雷游戏项目效果展示


2.扫雷游戏项目基本信息

项目名称:扫雷

开发语言:C语言

开发作者:牟建波

开发环境:Visual Studio2019、EasyX图形库、Windows

开发时间:2023-03-12


3.扫雷游戏项目设计思路


4.扫雷游戏实现原理

4.1 头文件解析

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <windows.h> //SetConsoleTextAttribute()函数头文件
#include <graphics.h> //easyx图形库
#include <time.h> //srand()函数头文件头文件使用解释:                                                                                                      1.使用#include<windows.h>头文件,是为了使用其中的SetConsoleTextAttribute()进行字体颜色修改,方便后期给雷标记颜色区分2.使用#include<graphics.h>头文件,是为了使用eaxys函数库,进行鼠标交互和图形绘制                                   3.使用#include<time.h>头文件,是为了使用srand()函数,通过时间播种生成随机数去对应雷

4.2 素材解析

  • 扫雷项目中的素材,全部来自官方原版游戏截图和自己画的,我把素材全放在pic文件夹下了,如下图2中各种表情、方块、数字、地雷

  • 在项目中,我们会使用easyx图形库对图像进行提取使用,所以尽量将素材和游戏放在同一目录下,避免一些奇怪的错误


4.3 变量与矩形布局解析

//三重矩阵布局:我的想法是用三个16*16的矩阵,分别用来表示雷区、地雷的数量、方块的状态,然后将他们进行一张棋盘重合//这样的布局,可以降低开发难度,调用时也更加不易弄晕矩阵
int Minefield_matrix[16][16] = { 0 };//雷区矩阵
int Mine_Count_matrix[16][16] = { 0 };//地雷计数矩阵
int Block_Status_matrix[16][16] = { 0 };//方块状态矩阵//公用循环变量:
//这里设定为全局变量,是因为后续会使用很多次,我懒得在里面加int,而且C89标准是不能在for里定义int i的
//所以为了照顾不同编译标准的读者,这里我将其定义为全局变量
int i = 0;
int j = 0;//中转地雷数组:用于临时中转存储地雷
int Transfer_Mine_matrix[40] = { 0 };//方块状态flag
int IsMine_flag = 0;//0表示非雷,1表示雷
//游戏胜利flag
int Success_flag = 0;//0表示失败,1表示成功

4.4 随机雷的实现

//生成随机雷//思路://1.雷随机生成 16*16=256 从左到右 1~256//2.随机生成40个[1,256]范围的随机数,通过对矩阵的位置,将对应的编号设置为雷//3.为防止生成的随机数有重复的,每生成一个随机数,就将这个数存入Transfer_Mine_matrix[40]这个中转地雷数组中//4.之后生成的随机数需与数组中的元素进行比较,若重复则不会保存在数组中srand((unsigned int)time(0));//通过时间播种生成随机数用于表示雷for (int Mine_Number = 0; Mine_Number < 40; Mine_Number++){int Correct_Number_mine = 0;//合格的雷的随机数字Correct_Number_mine = rand() % 256 + 1; //生成1~256范围内随机数if (Correct_Number_mine < 0 || Correct_Number_mine > 256)//随机数不符合要求{Mine_Number--;}else//随机数符合要求{for (i = 0; i < 40; i++){if (Transfer_Mine_matrix[i] == Correct_Number_mine)//如果合格的雷在中转地雷数组中存在{Mine_Number--;//不符合的雷去掉,雷数-1break;}if (Transfer_Mine_matrix[i] == 0)//如果合格的雷没有出现过,则存入中转地雷数组中 中转地雷数组初始化是0{Transfer_Mine_matrix[i] = Correct_Number_mine;break;}}}}//测试雷的位置for (int i = 0; i < 40; i++){printf("%d ", Transfer_Mine_matrix[i]);}

随机雷生成测试结果图:


4.5 生成雷位置矩阵的实现

//生成雷的矩阵
//思路:通过Transfer_Mine_matrix中转地雷数组中的随机数,通过对应关系写入雷区矩阵Minefield_matrix[16][16]中,雷的位置表示为1,非雷的位置表示为0for (int length = 0; length < 40; length++){int count = 0;//计数for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){count++;if (Transfer_Mine_matrix[length] == count){Minefield_matrix[i][j] = 1;IsMine_flag = 1;//1表示雷,0表示非雷break;}}if (IsMine_flag == 1){break;}}IsMine_flag = 0;//初始化,以免影响下次循环count = 0;//初始化,以免影响下次循环}//雷矩阵调试for (int i = 0; i < 16; i++){for (int j = 0; j < 16; j++){if (Minefield_matrix[i][j] == 1){colour(12);//是雷标记为红色}else{colour(15);//不是雷标记为白色}printf("%d ", Minefield_matrix[i][j]);//打印雷矩阵}colour(15);//让后续字体染色显示不为红色printf("\n");}system("pause");

生成雷位置矩阵调试结果图:


4.6 生成雷数矩阵的实现

雷数矩阵处理的想法:

  • 上图是我们对于一个矩阵四周遍历的常规过程,可以看出,我们进行遍历都是一边一边的进行遍历,由外向内进行遍历。当我们处理边界的是否就非常头疼了,这时我们可以使用循环不变量法
  • 循环不变量法:比如上图,就是我们在考虑边界问题的时候是非常头疼的,如果我们每边都考虑全部遍历完,就会出现很多边界考虑问题,但是如果我们把遍历方式都设置成一种,那么就让边界问题变成了一种情况,也就是上图的只遍历开始到最后一位的前一位,让下一次循环遍历剩下的元素,从而叫做循环不变量法
  • 但是我们在处理雷数矩阵的时候,要处理的时一个元素四周,所以直接使用循环不变量法不合适的。我们可以使用它的思想,进行雷数的计算
  • 这里我的想法是,先处理矩阵的四个角,然后处理去角的四边,那么我们就剩下了一个15*15的正方形了,处理正方形我们就有了一个通用的方法进行计算,如下图

//通过对matrix矩阵每个元素的周围八个元素计算雷数,并将雷数存入number[16][16]矩阵中
//number矩阵中1~8表示雷数,9表示该元素为雷
//通过对Minefield_matrix雷区矩阵每个元素的周围八个元素进行计算雷数,并将雷存入Mine_Count_matrix地雷计数矩阵中
//Mine_Count_martix地雷计数矩阵中,1~8表示周围雷数量,9表示该元素为雷
for (i = 0; i < 16; i++)
{int count = 0;for (j = 0; j < 16; j++){count = 0;if (Minefield_matrix[i][j] == 1)//如果是雷设为9{Mine_Count_matrix[i][j] = 9;continue;}else{//思路://1.先处理16*16矩阵的,四个边角,坐标为(0,0)、(0,15)、(15,0)、(15,15)//2.再处理矩形的四边,在处理过程把矩阵看成一个17*17的矩阵,这样是为了方便计数设计,数组溢出并不会有什么问题,因为我们没有使用它//3.最后我们会剩下一个规整15*15的正方形,然后处理每个方块的八个方向就可以了if (i == 0 && j == 0)//左上角{if (Minefield_matrix[0][1] == 1)//左上角方块右侧count++;if (Minefield_matrix[1][0] == 1)//左上角方块下侧count++;if (Minefield_matrix[1][1] == 1)//左上角方块斜右下侧count++;}else if (i == 0 && j == 15)//右上角{if (Minefield_matrix[0][14] == 1)//右上角方块左侧count++;if (Minefield_matrix[1][15] == 1)//右上角方块下侧count++;if (Minefield_matrix[1][14] == 1)//右上角方块斜左下侧count++;}else if (i == 15 && j == 0)//左下角{if (Minefield_matrix[15][1] == 1)//左下角方块右侧count++;if (Minefield_matrix[14][0] == 1)//左下角方块侧count++;if (Minefield_matrix[14][1] == 1)//左下角方块斜右上侧count++;}else if (i == 15 && j == 15)//右下角{if (Minefield_matrix[15][14] == 1)//右下角方块左侧count++;if (Minefield_matrix[14][15] == 1)//右下角方块上侧count++;if (Minefield_matrix[14][14] == 1)//右小角方块斜左上侧count++;}else if (i == 0)//处理顶部,第二个方块开始{if (Minefield_matrix[i][j - 1] == 1)//处理左侧count++;if (Minefield_matrix[i][j + 1] == 1)//处理右侧count++;if (Minefield_matrix[i + 1][j] == 1)//处理下侧count++;if (Minefield_matrix[i + 1][j - 1] == 1)//处理左下侧count++;if (Minefield_matrix[i + 1][j + 1] == 1)//处理右下侧count++;}else if (j == 15)//处理右部,第二个方块开始{if (Minefield_matrix[i - 1][j] == 1)//处理上侧count++;if (Minefield_matrix[i + 1][j] == 1)//处理右侧count++;if (Minefield_matrix[i][j - 1] == 1)//处理左侧count++;if (Minefield_matrix[i - 1][j - 1] == 1)//处理左上侧count++;if (Minefield_matrix[i + 1][j - 1] == 1)//处理左下侧count++;}else if (i == 15)//处理底部,第二个方块开始{if (Minefield_matrix[i][j - 1] == 1)//处理左侧count++;if (Minefield_matrix[i][j + 1] == 1)//处理右侧count++;if (Minefield_matrix[i - 1][j] == 1)//处理上侧count++;if (Minefield_matrix[i - 1][j - 1] == 1)//处理左上侧count++;if (Minefield_matrix[i - 1][j + 1] == 1)//处理右上侧count++;}else if (j == 0)//处理左部,第二个方块开始{if (Minefield_matrix[i - 1][j] == 1)//处理上侧count++;if (Minefield_matrix[i + 1][j] == 1)//处理下侧count++;if (Minefield_matrix[i][j + 1] == 1)//处理右侧count++;if (Minefield_matrix[i - 1][j + 1] == 1)//处理右上侧count++;if (Minefield_matrix[i + 1][j + 1] == 1)//处理右下侧count++;}else//处理剩下的规整15*15正方形的八个方向{if (Minefield_matrix[i - 1][j - 1] == 1)//左上侧count++;if (Minefield_matrix[i - 1][j] == 1)//上侧count++;if (Minefield_matrix[i - 1][j + 1] == 1)//右上侧count++;if (Minefield_matrix[i][j - 1] == 1)//左侧count++;if (Minefield_matrix[i][j + 1] == 1)//右侧count++;if (Minefield_matrix[i + 1][j - 1] == 1)//左下侧count++;if (Minefield_matrix[i + 1][j] == 1)//下侧count++;if (Minefield_matrix[i + 1][j + 1] == 1)//右下侧count++;}}Mine_Count_matrix[i][j] = count;}
}//数字阵调试for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (Mine_Count_matrix[i][j] == 9){colour(12);//red}else{colour(15);//white}printf("%d ", Mine_Count_matrix[i][j]);}colour(15);printf("\n");}

雷数矩阵数字阵调试结果图:


4.7 绘制图像过程

我们在绘制函数initgraph哪里打个断点,让我们继续往后执行,看下图像绘制的过程

  • 通过上图可以很直观的看到,这个图像的绘制时先导入图像,然后先绘制一个(735,883)的长方形,然后绘制中间的笑脸,最后根据我们设定的方块间隔,一块一块的绘制整个图像
//载入素材initgraph(735, 883);//测试(745,845)刚好放下16*16的矩形//IMAGE 定义一个图片名//游戏背景IMAGE cube;//方块图标IMAGE cube_trigger;//方块触发器:变色IMAGE background;//背景//游戏符号IMAGE mine_eliminate;//地雷图标IMAGE mine_trigger;//地雷触发器:变色IMAGE mine_flag;//插旗图标IMAGE question;//问号图标IMAGE question_trigger;//问号触发器:变色//游戏脸部IMAGE smile;//笑脸图标IMAGE smile_trigger;//笑脸触发器:变脸IMAGE caution;//谨慎图标IMAGE dead;//死亡图标IMAGE dead_trigger;//死亡触发器:变脸//游戏雷数:雷数0-8颜色,9表示本身为雷IMAGE mine_0;//雷数0图标IMAGE mine_1;//雷数1图标IMAGE mine_2;//雷数2图标IMAGE mine_3;//雷数3图标IMAGE mine_4;//雷数4图标IMAGE mine_5;//雷数5图标IMAGE mine_6;//雷数6图标IMAGE mine_7;//雷数7图标IMAGE mine_8;//雷数8图标IMAGE mine_9;//雷数9图标//loadimage 从图片中获取图像loadimage(&cube, "./pic/cube.jpg");//加载方块图标loadimage(&cube_trigger, "./pic/cube_trigger.jpg");//加载方块触发器图标loadimage(&background, "./pic/background.jpg");//加载背景图标loadimage(&mine_eliminate, "./pic/mine_eliminate.jpg");//加载地雷图标loadimage(&mine_trigger, "./pic/mine_trigger.jpg");//加载地雷触发器图标loadimage(&mine_flag, "./pic/mine_flag.jpg");//加载插旗图标loadimage(&question, "./pic/question.jpg");//加载问号图标loadimage(&question_trigger, "./pic/question_trigger.jpg");//加载问号触发器图标loadimage(&smile, "./pic/smile.jpg");//加载笑脸图标loadimage(&smile_trigger, "./pic/smile_trigger.jpg");//加载笑脸触发器图标loadimage(&caution, "./pic/caution.jpg");//加载谨慎图标loadimage(&dead, "./pic/dead.jpg");//加载死亡图标loadimage(&dead_trigger, "./pic/dead_trigger.jpg");//加载死亡触发器图标loadimage(&mine_0, "./pic/mine_0.jpg");//加载雷数0图标loadimage(&mine_1, "./pic/mine_1.jpg");//加载雷数1图标loadimage(&mine_2, "./pic/mine_2.jpg");//加载雷数2图标loadimage(&mine_3, "./pic/mine_3.jpg");//加载雷数3图标loadimage(&mine_4, "./pic/mine_4.jpg");//加载雷数4图标loadimage(&mine_5, "./pic/mine_5.jpg");//加载雷数5图标loadimage(&mine_6, "./pic/mine_6.jpg");//加载雷数6图标loadimage(&mine_7, "./pic/mine_7.jpg");//加载雷数7图标loadimage(&mine_8, "./pic/mine_8.jpg");//加载雷数8图标//贴图//putimage 绘制图片到屏幕,图片左上角坐标为(0,0)putimage(0, 0, &background);putimage(320, 17, &smile);//绘制扫雷的矩阵,循环中i、j同时对应Minefield_matrix雷区数组、Mine_Count_matrix地雷计数数组、 Block_Status_matrix方块状态矩阵//这样做的好处是方便后面的鼠标机交互坐标检测for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){putimage(15 + 45 * j, 118 + 45 * i, &cube);}}

4.8 输赢检测规则

//在游戏逻辑大循环前进行输赢检测,通过遍历Block_Status_matrix矩阵中的值进行计数,若元素值4的数量到达216即判定为胜利/*|-----------------------------------------------------------------------------------------------|| Block_Status_matrix矩阵内元素值的含义:                                                         ||     0 = 未触发的元素,相当于普通白方块                                                           ||                                                                                                ||      1 = 鼠标左键点击某元素后与其相邻的number矩阵中值为0的,但还未检测周边元素的元素                     ||                                                                                                ||      2 = 鼠标左键点击某元素后触发拓展后与number矩阵中值为0的元素相邻的number矩阵中值为1~8的元素             ||                                                                                             ||      3 = 鼠标左键点击某元素后与其相邻的number矩阵中值为0的,且已检测周边元素的元素                      ||                                                                                                ||      4 = 界面上已被贴图位置(空贴图,及1~8数字贴图)所对应的元素                                    ||                                                                                               ||      5 = 鼠标右键点击某元素后,该元素位置被贴为红旗的元素                                            ||                                                                                              ||      6 = 鼠标右键点击某元素后,该元素位置被贴为问号的元素                                            ||-----------------------------------------------------------------------------------------------|*/while (1){int Block_Status4_Number = 0;//Block_Status4_Number矩阵内元素值4的贴图数量for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (Block_Status_matrix[i][j] == 5)//如果贴上红旗if (Minefield_matrix[i][j] == 1)//如果红旗下是雷Block_Status4_Number++;}}if (Block_Status4_Number == 40)//40个雷都给正确排出来了{Success_flag = 1;//游戏成功旗帜break;}Block_Status4_Number = 0;//排除特殊情况,初始化为0开始正常情况//正常情况:界面贴图216游戏结束,还有40个是雷for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (Block_Status_matrix[i][j] == 4)Block_Status4_Number++;}}if (Block_Status4_Number == 216){Success_flag = 1;//游戏成功旗帜break;}Block_Status4_Number = 0;

4.9 检测相邻空元素的方法

//检测相邻空元素函数解释:
//1.此处运用递归方法,实现了对触发空元素的所有相邻空元素的检测,检测完成的空元素,status矩阵中的状态值会被调整为3;
//2.被检测出但还未检测其本身的元素状态值将会被设为1,当status矩阵中不存在值为1的元素时,即表示所有相邻空元素已检测完成,递归停止 //检测相邻空元素函数:Detects_adjacent_empty_elements(int, int, int[16][16], int[16][16])
void Detects_adjacent_empty_elements(int i, int j, int number[16][16], int status[16][16])
{//1.检测初始状态为0、没有被检测的0、没有被贴红旗的0、没有被贴为问号的0//2.将符合条件的元素状态变更为1,即此元素已经被检测过的0if (i != 0)if (number[i - 1][j] == 0 && status[i - 1][j] != 3 && status[i - 1][j] != 5 && status[i - 1][j] != 6)status[i - 1][j] = 1;if (i != 15)if (number[i + 1][j] == 0 && status[i + 1][j] != 3 && status[i + 1][j] != 5 && status[i + 1][j] != 6)status[i + 1][j] = 1;if (j != 0)if (number[i][j - 1] == 0 && status[i][j - 1] != 3 && status[i][j - 1] != 5 && status[i][j - 1] != 6)status[i][j - 1] = 1;if (j != 15)if (number[i][j + 1] == 0 && status[i][j + 1] != 3 && status[i][j + 1] != 5 && status[i][j + 1] != 6)status[i][j + 1] = 1;//将此元素调整为3,即表示:此元素周围元素都被检测了status[i][j] = 3;//调整状态为3for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (status[i][j] == 1)Detects_adjacent_empty_elements(i, j, number, status);}}
}

4.10 扩展空元素区域的方法

//扩展空元素区域函数解释:
//1.经上一步检测出的空元素后,要将其相邻的数值为1~8的元素也贴上图,因此将status矩阵中值为3的元素的周围的值
//2.不为3的元素的状态值变为2,即表示即将要被贴图的不为空的元素                                                           //扩展空元素区域函数:Expand_empty_element_area(int[16][16],int[16][16])
void Expand_empty_element_area(int status[16][16])
{int i = 0;int j = 0;for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (status[i][j] == 3){//1.检测状态不为3、不为5、不为6的元素//2.将符合条件的元素状态更改为2if (i != 0)if (status[i - 1][j] != 3 && status[i - 1][j] != 5 && status[i - 1][j] != 6)status[i - 1][j] = 2;if (i != 15)if (status[i + 1][j] != 3 && status[i + 1][j] != 5 && status[i + 1][j] != 6)status[i + 1][j] = 2;if (j != 0)if (status[i][j - 1] != 3 && status[i][j - 1] != 5 && status[i][j - 1] != 6)status[i][j - 1] = 2;if (j != 15)if (status[i][j + 1] != 3 && status[i][j + 1] != 5 && status[i][j + 1] != 6)status[i][j + 1] = 2;if (i != 0 && j != 0)if (status[i - 1][j - 1] != 3 && status[i - 1][j - 1] != 5 && status[i - 1][j - 1] != 6)status[i - 1][j - 1] = 2;if (i != 0 && j != 15)if (status[i - 1][j + 1] != 3 && status[i - 1][j + 1] != 5 && status[i - 1][j + 1] != 6)status[i - 1][j + 1] = 2;if (i != 15 && j != 0)if (status[i + 1][j - 1] != 3 && status[i + 1][j - 1] != 5 && status[i + 1][j - 1] != 6)status[i + 1][j - 1] = 2;if (i != 15 && j != 15)if (status[i + 1][j + 1] != 3 && status[i + 1][j + 1] != 5 && status[i + 1][j + 1] != 6)status[i + 1][j + 1] = 2;}}}
}

4.11 绘制状态贴图的方法

状态绘图函数解释:
1.经过上两步的检测,所有要被贴图的元素状态已被设为2和3,因此仅需遍历status矩阵
2.找到状态为2和3的元素,在对照number矩阵中的值贴上空以及1~8的图片素材                            //状态绘图函数:State_mapping(int[16][16], int[16][16], IMAGE, IMAGE, IMAGE, IMAGE, IMAGE, IMAGE, IMAGE, IMAGE, IMAGE)
void State_mapping(int Mine_Count_matrix[16][16], int Block_Status_matrix[16][16], IMAGE ZERO, IMAGE ONE, IMAGE TWO, IMAGE THREE, IMAGE FOUR, IMAGE FIVE, IMAGE SIX, IMAGE SEVEN, IMAGE EIGHT)
{int i = 0;int j = 0;for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){//满足状态2和3的元素,根据状态码进行贴图处理if (Block_Status_matrix[i][j] == 2 || Block_Status_matrix[i][j] == 3){switch (Mine_Count_matrix[i][j]){case 0: putimage(15 + 45 * j, 118 + 45 * i, &ZERO); break;case 1: putimage(15 + 45 * j, 118 + 45 * i, &ONE); break;case 2: putimage(15 + 45 * j, 118 + 45 * i, &TWO); break;case 3: putimage(15 + 45 * j, 118 + 45 * i, &THREE); break;case 4: putimage(15 + 45 * j, 118 + 45 * i, &FOUR); break;case 5: putimage(15 + 45 * j, 118 + 45 * i, &FIVE); break;case 6: putimage(15 + 45 * j, 118 + 45 * i, &SIX); break;case 7: putimage(15 + 45 * j, 118 + 45 * i, &SEVEN); break;case 8: putimage(15 + 45 * j, 118 + 45 * i, &EIGHT); break;}Block_Status_matrix[i][j] == 4;}}}
}

4.12 展示地雷的方法

//展示地雷函数解释:
//1.游戏失败后,需向玩家展示所有地雷的位置,根据雷位置所在元素进行贴图即可                                                   //展示地雷:Display_mine(int matrix[16][16], IMAGE mine_eliminate)
void Display_mine(int matrix[16][16], IMAGE mine_eliminate)
{int i = 0;int j = 0;for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){if (matrix[i][j] == 1){putimage(15 + 45 * j, 118 + 45 * i, &mine_eliminate);}}}
}

4.13 鼠标交互逻辑解析

//鼠标交互逻辑
MOUSEMSG mousemsg = GetMouseMsg();
//解释:
//1.MOUSEMSG:定义结构体鼠标
//2.GetMouseMsg():获取当前鼠标信息
for (i = 0; i < 16; i++)
{for (j = 0; j < 16; j++){if (15 + 45 * j <= mousemsg.x && mousemsg.x <= 15 + 45 * j + 42 && 118 + 45 * i <= mousemsg.y&& mousemsg.y <= 118 + 45 * i + 42 && (Block_Status_matrix[i][j] == 0|| Block_Status_matrix[i][j] == 5|| Block_Status_matrix[i][j] == 6)){if (mousemsg.uMsg == WM_LBUTTONDOWN && Block_Status_matrix[i][j] != 5)//如果左键点击,并且没贴红旗{putimage(320, 17, &caution);//界面上方笑脸图标切换为谨慎图标 if(Mine_Count_matrix[i][j] == 9)//如果点击的元素为雷{putimage(15 + 45 * j, 118 + 45 * i, &mine_trigger);//绘制雷的图片 Success_flag = 0;//游戏失败旗帜IsMine_flag = 1;//是雷旗帜break;}else if (Mine_Count_matrix[i][j] != 0)//如果点击的元素不为雷而为空{switch (Mine_Count_matrix[i][j])//按照相邻元素中雷的数量进行贴图{case 1: putimage(15 + 45 * j, 118 + 45 * i, &mine_1); break;case 2: putimage(15 + 45 * j, 118 + 45 * i, &mine_2); break;case 3: putimage(15 + 45 * j, 118 + 45 * i, &mine_3); break;case 4: putimage(15 + 45 * j, 118 + 45 * i, &mine_4); break;case 5: putimage(15 + 45 * j, 118 + 45 * i, &mine_5); break;case 6: putimage(15 + 45 * j, 118 + 45 * i, &mine_6); break;case 7: putimage(15 + 45 * j, 118 + 45 * i, &mine_7); break;case 8: putimage(15 + 45 * j, 118 + 45 * i, &mine_8); break;}Block_Status_matrix[i][j] = 4;//状态转换为已贴图}else//该元素为雷{Block_Status_matrix[i][j] = 1;Detects_adjacent_empty_elements(i, j, Mine_Count_matrix, Block_Status_matrix);//扫描周围的所有空元素Expand_empty_element_area(Block_Status_matrix);//对周围空元素进行扩展State_mapping(Mine_Count_matrix, Block_Status_matrix, mine_0, mine_1, mine_2, mine_3, mine_4, mine_5, mine_6, mine_7, mine_8);//绘制状态图片}Sleep(250);//睡眠0.25秒,让谨慎脸转换笑脸自然一点putimage(320, 17, &smile);//界面上方的笑脸}else if (mousemsg.uMsg == WM_RBUTTONDOWN)//如果右键点击{//将状态在未触发0、插旗状态5、问号状态6之间切换if (Block_Status_matrix[i][j] == 0)//不是雷{Block_Status_matrix[i][j] = 5;//贴插旗状态putimage(15 + 45 * j, 118 + 45 * i, &mine_flag);//贴上插旗图标 }else if (Block_Status_matrix[i][j] == 5)//如果已经是红旗状态了Block_Status_matrix[i][j] = 6;//状态改为问号else if (Block_Status_matrix[i][j] == 6)//如果已经是问号状态了Block_Status_matrix[i][j] = 0;//状态改为未触发状态}//将触发状态图标在未触发、问号之间切换else{//未触发图标if (Block_Status_matrix[i][j] == 0)putimage(15 + 45 * j, 118 + 45 * i, &cube_trigger);//问号图标else if (Block_Status_matrix[i][j] == 6)putimage(15 + 45 * j, 118 + 45 * i, &question_trigger);}}//将图标在未触发、问号之间切换else if (Block_Status_matrix[i][j] == 0 || Block_Status_matrix[i][j] == 6)//鼠标交互反应贴图{if (Block_Status_matrix[i][j] == 0)putimage(15 + 45 * j, 118 + 45 * i, &cube);elseputimage(15 + 45 * j, 118 + 45 * i, &question);}}if (IsMine_flag == 1)break;
}
if (IsMine_flag == 1)
{IsMine_flag = 0;break;
}
if (333 <= mousemsg.x && mousemsg.x <= 413 && 15 <= mousemsg.y && mousemsg.y <= 95)
{if (mousemsg.uMsg == WM_LBUTTONDOWN)//如果点击笑脸,重新开始游戏{putimage(320, 17, &smile_trigger);Sleep(200);goto start;//goto函数进行跳转}
}

4.14 游戏获胜与失败判定原理

 //游戏失败if (Success_flag == 0){settextcolor(RED);setbkmode(TRANSPARENT);settextstyle(100, 0, "黑体");outtextxy(110, 5, "GAME  OVER");putimage(320, 17, &dead);Sleep(10);Display_mine(Minefield_matrix, mine_eliminate);putimage(15 + 45 * j, 118 + 45 * i, &mine_trigger);while (1){MOUSEMSG mouse = GetMouseMsg();if (333 <= mouse.x && mouse.x <= 413 && 15 <= mouse.y && mouse.y <= 95)//点击哭脸可重新开始游戏{if (mouse.uMsg == WM_LBUTTONDOWN){putimage(320, 17, &dead_trigger);Sleep(200);goto start;//goto跳转}}}}//游戏成功if (Success_flag == 1){settextcolor(RED);setbkmode(TRANSPARENT);settextstyle(100, 0, "黑体");outtextxy(110, 5, "GAME  WIN!");putimage(320, 17, &smile);Sleep(10);Display_mine(Minefield_matrix, mine_eliminate);system("pause");return 0;}printf("\n");for (i = 0; i < 16; i++){for (j = 0; j < 16; j++){printf("%d ", Block_Status_matrix[i][j]);}printf("\n");}system("pause");
}

4.15 自定义颜色制定

//自定义文字颜色函数:colour(short x)
void colour(short x)
{SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);//用这个函数方便我们给雷标记颜色/*函数解释:SetConsoleTextAttribute()函数是一个API设置字体颜色和背景色的函数。参数表中使用两个属性(属性之间用,隔开),不同于system(color),SetConsoleTextAttribute()可以改变界面多种颜色,而system()只能修改为一种!函数原型:SetConsoleTextAttribute(_In_ HANDLE hConsoleOutput, _In_ WORD wAttributes);第一个属性获得句柄(即要设置颜色的地方),第二个属性设置颜色句柄:STD_INPUT_HANDLE     含义:标准输入的句柄句柄:STD_OUTPUT_HANDLE    含义:标准输出的句柄句柄:STD_ERROR_HANDLE     含义:标准错误的句柄颜色设置(三种方法):1.SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x07);第二个参数填十六进制数字2.SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);直接填十进制数字3.SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), BACKGROUND_BLACK | FOREGROUND_RED);BACKGROUND代表背景,FOREGROUND代表前景4.数字代表的什么请搜索一下,这里我用的是十进制数字,15代表白色,12代表红色,用红色来标记雷,白色标记非雷*/
}

基于easyx库的GUI扫雷项目相关推荐

  1. C/C++基于easyX库实现三星环绕小项目(C/C++)

    1,项目描述 功能1:使用图形化的方式描述地球围绕着太阳转动,月球围绕着地球转动 功能2:在转动的过程中当用户按下1,2,3,4,5,6,7时它可以变换出7种不同的颜色,当用户按下8时它可以变换从1- ...

  2. 绘制樱花树特效(基于EasyX库实现)

    1.程序运行环境(必读) Visual C++ 6.0.Visual Studio 2010 ~ Visual Studio 2022 等支持EasyX图形库的软件均可以使用,实现相应功能. ---- ...

  3. c语言基于easyx库的 飞机大战游戏(鼠标控制飞机移动,武器升级,boss发射散弹,boss血条等功能)

    课设题目 实现功能: 飞机鼠标控制–飞机武器升级–敌机发射子弹–boss发射散弹–boss血条记录–我方多条生命 图片资源和源码在下面 链接:https://pan.baidu.com/s/1uTQV ...

  4. C++的学习心得和知识总结(十八)|基于EasyX实现 2048游戏 项目(C/C++版)

    目录结构 注:提前言明 本文借鉴了以下博主.书籍或网站的内容,其列表如下: 1.网络热门游戏 2048,点击前往 2.EasyX官方链接,点击前往 3.中国色谱 颜色代码对照表(RGB多用于easyX ...

  5. C++的学习心得和知识总结(十六)|基于EasyX实现小甲鱼Python飞机大战项目(C++版)

    目录结构 注:提前言明 本文借鉴了以下博主.书籍或网站的内容,其列表如下: 1.小甲鱼Python项目 – 飞机大战 2.本文使用的掩码图生成工具 自动生成遮罩图的程序,点击前往 3.EasyX官方链 ...

  6. 【C++】基于Easyx的UI库(2022-8-21 V1)

    系列文章目录 [C++]基于Easyx的UI库(2022-8-14 V1) [C++]基于Easyx的UI库(2022-8-14 V2) 简介 实现语言:C++ 项目名称:UI.h 项目类型:控制台应 ...

  7. 【C++】基于Easyx的UI库(2022-8-14 V2)

    系列文章目录 [C++]基于Easyx的UI库(2022-8-14 V1) 简介 实现语言:C++ 项目名称:UI.h 项目类型:控制台应用 项目版本:2022-8-14 V2 开发环境:Visual ...

  8. 基于EasyX的扫雷游戏

    基于EasyX的扫雷游戏 一.预备知识 二.游戏逻辑 1.扫雷元素 2.扫雷规则 三.游戏设计 1.地图设计 2.点击设计 3.结束设计 4.整体设计 一.预备知识 1.使用EasyX必须要知道的一些 ...

  9. dio设置自定义post请求_基于dio库封装flutter项目的标准网络框架

    网络框架是每个应用的基石,封装一个好的网络框架不仅是项目的一个好的开始,并且直接影响到随后项目的稳定性和可扩展性.在移动开发的各个端都有非常赞的网络请求基础框架,比如Android的okhttp库.s ...

最新文章

  1. Java的几种常见接口用法
  2. SAP HUM嵌套HU场景下WM报表LS26里SU号码是外层HU号码
  3. oracle更改控制文件位置
  4. TABLES ABOUT CRM MARTETING
  5. 第一章 编程基础知识
  6. python函数封装计算n运算_在Python里面怎么可以运算出999999999**999999999,求思路?...
  7. 客户端连接mysql 自动断开_MySql连接空闲8小时自动断开的原因及连接池配置方法...
  8. 活动预告丨openGauss西安Meetup来啦!本周五见!
  9. 关于Patter类和Match类
  10. 第八章 程序的分支结构
  11. 自绘LISTVIEW的滚动条(Delphi实现)
  12. IBM 人工智能黑科技
  13. 笔记︱盘点实验科学的三种实验模型(A/B实验、因果推断、强化学习)
  14. 内耗自救指南|5招停止内耗让你逆风翻盘
  15. JS中的构造函数的含义和用法
  16. 大天使之剑服务器维护,《大天使之剑H5》9月4日服务器临时维护公告
  17. 赴美工作常识(Part 6 - 绿卡排队)
  18. 大数据all_in环境搭建
  19. “数字化”与“信息化”的区别是什么?
  20. Chapter2、HDR流水线

热门文章

  1. 并发编程思维导图分享(包含详细知识点)
  2. PTA 求该月天数分数 java解法 分类平均 C语言
  3. CAD 设置原点 UCS M
  4. JAVA增删改查代码
  5. Qt开发教程:配置qmake
  6. Android开发:实现简单的问卷调查
  7. 柯美6500服务器开机显示FF,柯美复印机CO3FF代码处理方法
  8. 电脑桌面图标背景出现蓝色解决方法
  9. 人工智能体系架构(入门必看)
  10. STM32程序正常烧录后不能运行的可能原因(关于新旧CUBE的型号问题)