IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 没事扫个雷!手把手教你编写扫雷小游戏 -> 正文阅读

[游戏开发]没事扫个雷!手把手教你编写扫雷小游戏

目录

一、游戏简介

二、游戏设计

2.1功能设计

2.2模型设计

三、游戏实现

3.1SaoLeiMain.c

3.2Menu.c

3.3Game.c

3.3.1 playGame(游戏执行模块)

3.3.2?saoLei(玩家扫雷模块)

3.3.3 boardInit(初始化数组模块)

3.3.4 setMine(埋雷模块)

3.3.5 displayBoard(显示模块)

3.3.6 getMineCount(数雷模块)

3.3.7 isNoLei(延展模块)

四、游戏测试

五、游戏代码

5.1?SaoLeiMain.c

5.2 Menu.c

5.3 Game.c

5.4 Menu.h

5.5 Game.h


一、游戏简介

《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。

相信大家都在自己的电脑上玩过这款游戏。又想尝试玩玩怎么办?不如自己动手,将这款经典游戏再次复现吧!

二、游戏设计

2.1功能设计

在完成扫雷游戏游玩的最基本功能的同时,也要设计出标记地雷的功能,且在选中一个时,若周围一圈无雷,需要自动点开周围一圈的区域,并如此延展下去,已达到节省游戏时间的目的。

2.2模型设计

三、游戏实现

3.1SaoLeiMain.c

(1)模块功能及思路:本模块为程序的入口,通过此模块调入菜单模块,执行开始游戏或退出游戏的功能。还涉及到rand()函数的配置,在之前的文章中也提到了这一操作。同时设计为循环的形式,以便玩家多次游玩。

(2)模块代码:

int main()
{
	int flag = 0;

	srand((unsigned int)time(NULL));
	do
	{
		switch (flag = choiceMenu())
		{
		case 1:playGame(); break;
		case 0:printf("退出游戏\n"); break;
		default:printf("输入错误,请重新选择!\n"); break;
		}
	} while (flag);

	return 0;
}

3.2Menu.c

(1)模块功能及思路:该函数负责打印游戏开始菜单,同时需要返回玩家的选项值。

(2)模块代码:

int choiceMenu()
{
	int input = 0;

	printf("********************************\n");
	printf("*********   1. play     ********\n");
	printf("*********   0. exit     ********\n");
	printf("********************************\n");
	printf("请输入选项>");
	scanf("%d", &input);

	return input;
}

3.3Game.c

3.3.1 playGame(游戏执行模块)

(1)模块功能及思路:想要高效地完成扫雷游戏,需要定义两个二维数组用以存放信息。其中一个数组用来存放布雷图,这是玩家看不到的。而另一个数组用来存放游戏棋盘,这是玩家所能看到的。在定义这两个二维数组时,可采用宏定义的手段,以便随时调整游戏难度。所有的宏定义均设置在Game.h文件下,除控制数组大小的宏定义外,还有控制地雷数目的宏定义。

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

#define EASY_COUNT 10

接下来就来看看本模块的主要执行流程:初始化布雷数组——>初始化棋盘数组——>设置地雷——>显示棋盘——>玩家扫雷

(2)模块代码:

void playGame()
{
	char mine[ROWS][COLS] = { 0 };//雷区图
	char show[ROWS][COLS] = { 0 };//玩家图

	system("cls");
	boardInit(mine, ROWS, COLS, '0');//数组初始化
	boardInit(show, ROWS, COLS, '*');//数组初始化
	setMine(mine, ROW, COL);//埋雷
	displayBoard(show, ROW, COL);//显示玩家图
	saoLei(mine, show, ROW, COL);//玩家扫雷
}

3.3.2?saoLei(玩家扫雷模块)

(1)模块功能及思路:玩家在执行扫雷程序时,首先会面临两个选择,一是排雷,二是标记雷。想要实现标记地雷非常简单,只需要根据输入的坐标对棋盘数组进行字符的替换即可,(注意,每次输入坐标时均要判断输入是否正确!)。

而如果玩家选择排雷,又会出现多种情况,一是选中地雷,游戏结束,玩家失败;二是没选中地雷这时就要进入延展模块。

以上过程为一个循环,终止此过程的条件是胜利或失败。那么如何判断忘记是否排雷完成呢?这里我们可以定义一个变量win,每当选中一个各种并且此格非雷,win就会加一。通过简单的数学计算,不难发现,当点完所有非雷格即win = 总格数 - 地雷数时,玩家胜利。因此循环条件为win != row * col - EASY_COUNT

(2)模块代码:

void saoLei(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	int input = 0;

	while (win != row * col - EASY_COUNT)
	{
		printf("排雷(1)或标记雷(2)>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入排雷坐标>");
			scanf("%d%d", &x, &y);//输入坐标
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				if (mine[x][y] == '1')
				{
					system("cls");
					displayBoard(mine, ROW, COL);
					printf("恭喜您被炸飞了\n");
                    win = row * col - EASY_COUNT;
					break;
				}
				else
				{
					isNoLei(mine, show, ROW, COL, x, y, &win);
				}
			}
			else
				printf("输入错误,请重新输入!\n");
			if (win == row * col - EASY_COUNT)
			{
				printf("恭喜你,排雷成功\n");
				displayBoard(mine, ROW, COL);
			}
			break;
		case 2:
			printf("请输入标记坐标>");
			scanf("%d%d", &x, &y);//输入坐标
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				show[x][y] = '!';
				system("cls");
				displayBoard(show, ROW, COL);
			}
			break;
		default:printf("输入错误,请重新选择\n");
		}
	}
}

3.3.3 boardInit(初始化数组模块)

(1)模块功能及思路:本模块的实现也非常简单,只是初始化二维数组的基本步骤。需要注意的是两个二维数组初始化的内容不同,因此需要定义一个参数获取要初始化的内容

(2)模块代码:

void boardInit(char arr[ROWS][COLS], int row, int col, char n)
{
	int i = 0;

	for (i = 0; i < row; i++)
	{
		int j = 0;

		for (j = 0; j < col; j++)
		{
			arr[i][j] = n;
		}
	}
}

3.3.4 setMine(埋雷模块)

(1)模块功能及思路:本模块复制布置地雷。由于地雷是随机放置的,因此只需要利用rand()函数获取随机坐标即可。这一操作在之前的三子棋文章中有具体提到。定义一个变量记录布雷数,每成功布置一次地雷(不能在一个区域上重复布置)雷数加一,知道等于宏定义设置的初值。

(2)模块代码:

void setMine(char arr[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = 0;

	do
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count++;
		}
	} while (count < EASY_COUNT);
}

3.3.5 displayBoard(显示模块)

(1)模块功能及思路:本模块的实现同样非常简单,也只是显示二维数组的一般步骤。在显示棋盘的同时,也可以显示一个横纵坐标,便于玩家选择格子。

(2)模块代码:

void displayBoard(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;

	printf("-------------------\n");
	for (i = 0; i <= 9; i++)
	{
		printf("%d ", i);
	}
	printf("\n-------------------\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d|", i);
		int j = 0;

		for (j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("-------------------\n");
}

3.3.6 getMineCount(数雷模块)

(1)模块功能及思路:本模块的功能为计算选中格周围一圈的地雷数目。这里使用了一种很简单的计算方法。由于在mine数组中,地雷用字符'1'表示,无雷用'0'表示,所以只需要将周围一圈字符的ASCII码值相加,再减去8个字符'0'的ASCII码值即可。

(2)模块代码:

int getMineCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] +
		mine[x][y + 1] +
		mine[x - 1][y + 1] - 8 * '0');
}

3.3.7 isNoLei(延展模块)

(1)模块功能及思路:本模块要在一个格子的周边无雷时,自动排查周围一圈的格子,并点开排查格中周围一圈无雷的格子。要想简单的完成这个功能,最好采用递归的方法,递归在解决一些复杂问题时,能够使问题简单化。

在本部分使用递归,需要防止一个格子被重复排查,这样会照成死递归,因此,在每次进入这个函数时,需要判断格子是否已被点开,只要判断show数组中该坐标的字符是否为'*'即可。

每次点开一个格子时,win需要加一,在之前的模块中提到过。因此,想要改变win的值,必须利用指针进行间接操作

(2)模块代码:

void isNoLei(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* win)
{
	if ((show[x][y] == '*')&&(x >= 1 && x <= row && y >= 1 && y <= col))
	{
		*win = *win + 1;
		int count = getMineCount(mine, x, y);

		if (count != 0)
		{
			show[x][y] = count + '0';
			system("cls");
			displayBoard(show, ROW, COL);
		}
		else
		{
			show[x][y] = count + '0';
			isNoLei(mine, show, ROW, COL, x - 1, y, win);
			isNoLei(mine, show, ROW, COL, x - 1, y - 1, win);
			isNoLei(mine, show, ROW, COL, x, y - 1, win);
			isNoLei(mine, show, ROW, COL, x + 1, y - 1, win);
			isNoLei(mine, show, ROW, COL, x + 1, y, win);
			isNoLei(mine, show, ROW, COL, x + 1, y + 1, win);
			isNoLei(mine, show, ROW, COL, x, y + 1, win);
			isNoLei(mine, show, ROW, COL, x - 1, y + 1, win);
		}
	}
}

四、游戏测试

?

?

?

?

五、游戏代码

5.1?SaoLeiMain.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "Menu.h"

int main()
{
	int flag = 0;

	srand((unsigned int)time(NULL));
	do
	{
		switch (flag = choiceMenu())
		{
		case 1:playGame(); break;
		case 0:printf("退出游戏\n"); break;
		default:printf("输入错误,请重新选择!\n"); break;
		}
	} while (flag);

	return 0;
}

5.2 Menu.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "Menu.h"

int choiceMenu()
{
	int input = 0;

	printf("********************************\n");
	printf("*********   1. play     ********\n");
	printf("*********   0. exit     ********\n");
	printf("********************************\n");
	printf("请输入选项>");
	scanf("%d", &input);

	return input;
}

5.3 Game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "Game.h"

void boardInit(char arr[ROWS][COLS], int row, int col, char n)
{
	int i = 0;

	for (i = 0; i < row; i++)
	{
		int j = 0;

		for (j = 0; j < col; j++)
		{
			arr[i][j] = n;
		}
	}
}

void setMine(char arr[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = 0;

	do
	{
		x = rand() % row + 1;
		y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count++;
		}
	} while (count < EASY_COUNT);
}

void displayBoard(char arr[ROWS][COLS], int row, int col)
{
	int i = 0;

	printf("-------------------\n");
	for (i = 0; i <= 9; i++)
	{
		printf("%d ", i);
	}
	printf("\n-------------------\n");
	for (i = 1; i <= row; i++)
	{
		printf("%d|", i);
		int j = 0;

		for (j = 1; j <= col; j++)
		{
			printf("%c ", arr[i][j]);
		}
		printf("\n");
	}
	printf("-------------------\n");
}

int getMineCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] +
		mine[x][y + 1] +
		mine[x - 1][y + 1] - 8 * '0');
}

void isNoLei(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* win)
{
	if ((show[x][y] == '*')&&(x >= 1 && x <= row && y >= 1 && y <= col))
	{
		*win = *win + 1;
		int count = getMineCount(mine, x, y);

		if (count != 0)
		{
			show[x][y] = count + '0';
			system("cls");
			displayBoard(show, ROW, COL);
		}
		else
		{
			show[x][y] = count + '0';
			isNoLei(mine, show, ROW, COL, x - 1, y, win);
			isNoLei(mine, show, ROW, COL, x - 1, y - 1, win);
			isNoLei(mine, show, ROW, COL, x, y - 1, win);
			isNoLei(mine, show, ROW, COL, x + 1, y - 1, win);
			isNoLei(mine, show, ROW, COL, x + 1, y, win);
			isNoLei(mine, show, ROW, COL, x + 1, y + 1, win);
			isNoLei(mine, show, ROW, COL, x, y + 1, win);
			isNoLei(mine, show, ROW, COL, x - 1, y + 1, win);
		}
	}
}

void saoLei(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	int input = 0;

	while (1)
	{
		printf("排雷(1)或标记雷(2)>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("请输入排雷坐标>");
			scanf("%d%d", &x, &y);//输入坐标
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				if (mine[x][y] == '1')
				{
					system("cls");
					displayBoard(mine, ROW, COL);
					printf("恭喜您被炸飞了\n");
                    win = row * col - EASY_COUNT;
					break;
				}
				else
				{
					isNoLei(mine, show, ROW, COL, x, y, &win);
				}
			}
			else
				printf("输入错误,请重新输入!\n");
			if (win == row * col - EASY_COUNT)
			{
				printf("恭喜你,排雷成功\n");
				displayBoard(mine, ROW, COL);
			}
			break;
		case 2:
			printf("请输入标记坐标>");
			scanf("%d%d", &x, &y);//输入坐标
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				show[x][y] = '!';
				system("cls");
				displayBoard(show, ROW, COL);
			}
			break;
		default:printf("输入错误,请重新选择\n");
		}
	}
}

void playGame()
{
	char mine[ROWS][COLS] = { 0 };//雷区图
	char show[ROWS][COLS] = { 0 };//玩家图

	system("cls");
	boardInit(mine, ROWS, COLS, '0');//数组初始化
	boardInit(show, ROWS, COLS, '*');//数组初始化
	setMine(mine, ROW, COL);//埋雷
	displayBoard(show, ROW, COL);//显示玩家图
	saoLei(mine, show, ROW, COL);//玩家扫雷
}

5.4 Menu.h

#pragma once

#include "Game.h"

int choiceMenu();

5.5 Game.h

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

#define EASY_COUNT 10

void playGame();

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-08-02 11:06:31  更:2021-08-02 11:07:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/4 10:46:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码