文章目录
- 一、准备工作
- 1.图片
- 2.html
- 2.css
- 3.js
- 二、初始化数据
- 1. 配置文件
- 2.工具文件
- 3.逻辑文件
- 1.main函数
- 2.init函数
- 1.随机生成雷
- 2.css添加
- 三、完整代码
- 1.html
- 2.js
- 3.css
一、准备工作
1.图片
需要找三张图片 旗子的图片 炸弹的图片 爆炸的图片
2.html
html文件夹新建一个html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扫雷游戏</title>
<link rel="stylesheet" href="../css/index.css">
</head>
<body>
<!-- 游戏最外层容器 -->
<div class="container">
<!-- 游戏标题 -->
<h1>扫雷</h1>
<!-- 游戏难度 -->
<div class="level">
<button class="active">初级</button>
<button>中级</button>
<button>高级</button>
</div>
<!-- 总雷数和插旗树 -->
<div class="info">总雷数:<span class="mineNum">10</span></div>
<div class="info">插旗数:<span class="flagNum">0</span></div>
<!-- 扫雷区域 通过js动态添加-->
<div class="mineArea"></div>
</div>
<script src="../js/config.js"></script>
<script src="../js/index.js"></script>
</body>
</html>
目前写好html后的样式
2.css
css文件夹下新建一个css文件
- 初始化
- 内外边框为0
- 改变盒子模型为边框盒子
*{ /* 初始化内外边距 */ margin: 0; padding: 0; /* 改成边框盒 会把padding什么的算进去 */ box-sizing: border-box; }
- 标题:
- 文字居中
- 字体大小46px
- 粗100
- 上下边距30 居中 左右20 居中
/* 标题 */ h1{ text-align: center; font-size:46px; font-weight: 100; margin:30px auto 20px auto }
- 游戏难度选择
- 字体居中
- 下边距10px
/* 游戏难度选择 */ .level{ text-align: center; margin-bottom: 10px; }
- 游戏难度选择按钮:
- 内边距上下5px 左右15px
- 背景颜色#02a4ad
- 字体颜色白色
- 取消轮廓(outline)
- 取消外边框(border)
- 圆角3px
- 上下边距0 左右边距5px
- 移动到按钮时鼠标改为手指
.level button{ padding: 5px 15px; background: #02a4ad; color: white; outline: none; border:none; border-radius: 3px; cursor:pointer }
- 当前按钮样式
- 背景颜色 #00abff
.level button.active{ background-color: #00abff; }
- 提示信息相关样式
- 文字居中
- 粗200
- 上下10px 左右居中
- 完成后的样式截图:
3.js
新建一个js文件夹
- 创建index.js 写逻辑
- 创建config.js写配置
二、初始化数据
1. 配置文件
由于整个游戏是可以选择难度的,所以我们需要一些配置信息,我们单独创建了一个config.js,代码如下
// 游戏配置文件
//不同级别的配置
var config = {
easy: {
row: 10,
col: 10,
mineNum: 10,
}, //简单模式
noraml: {
row: 15,
col: 15,
mineNum: 30,
}, //普通模式
hard: {
row: 20,
col: 20,
mineNum: 60,
}, //困难模式
custom: {
row: '',
col: '',
mineNum: '',
}, //自定义模式
}
// 当前游戏难度 一开始默认为简单模式
var curLevel=config.easy
2.工具文件
新建一个tool.js
// 工具函数
// 1. 获取元素
function $(selector){
return document.querySelector(selector)
}
//2.获取所有元素
function $$(selector){
return document.querySelectorAll(selector)
}
3.逻辑文件
1.main函数
// 游戏主要逻辑
// 主函数
function main(){
//1.进行游戏的初始化
init()
//2.绑定事件
bindEvent()
}
main()
// 初始化
function init(){
}
//绑定事件
function bindEvent(){
}
2.init函数
- 思考:初始化要做什么事情?
- 随机生成所选配置对应数量的雷
//index。js最上面的地方写
//用于存储生成的地雷的数组
var mineArray = null
//雷区的容器
var mineArea = $(".mineArea")
// 初始化
function init() {
//1.随机生成所选配置对应的数量的雷
mineArray = initMine()
//2.根据雷的数量,生成对应的表格
var table = document.createElement('table')
//初始化格子的下标
var index = 0
for (var i = 0; i < curLevel.row; i++) {
var tr = document.createElement('tr')
for (var j = 0; j < curLevel.col; j++) {
var td = document.createElement('td')
tr.appendChild(td)
}
table.appendChild(tr)
}
mineArea.appendChild(table);
}
1.随机生成雷
// 随机生成所选配置对应的数量的雷
function initMine() {
//1.创建一个数组,用于存储生成的地雷,生成对应长度的数组
var arr = new Array(curLevel.row * curLevel.col)
//2.往这个数组里面填充值
for (var i = 0; i < arr.length; i++) {
arr[i] = i
}
//3.接下来打乱这个数组
arr.sort(() => 0.5 - Math.random())
//4.只保留对应雷数量的数组长度
return arr.slice(0, curLevel.mineNum)
}
2.css添加
/* 表格相关样式 */
table{
background:#929196;
margin: 0 auto;
border-spacing:1px;
}
table td{
width: 24px;
height: 24px;
background:#ccc;
border: 2px solid;
border-color: #fff #a1a1a1 #a1a1a1 #fff;
text-align:center ;
line-height: 24px;
font-weight: bold;
cursor:pointer
}
三、完整代码
1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扫雷游戏</title>
<link rel="stylesheet" href="../css/index.css">
</head>
<body>
<!-- 游戏最外层容器 -->
<div class="container">
<!-- 游戏标题 -->
<h1>扫雷</h1>
<!-- 游戏难度 -->
<div class="level">
<button class="active">初级</button>
<button>中级</button>
<button>高级</button>
<button>自定义</button>
</div>
<!-- 总雷数和插旗树 -->
<div class="info">总雷数:<span class="mineNum">10</span></div>
<div class="info">插旗数:<span class="flagNum">0</span></div>
<!-- 扫雷区域 通过js动态添加-->
<div class="mineArea"></div>
</div>
<script src="../js/tool.js"></script>
<script src="../js/config.js"></script>
<script src="../js/index.js"></script>
</body>
</html>
2.js
config.js
// 游戏配置文件
//不同级别的配置
var config = {
easy: {
row: 10,
col: 10,
mineNum: 10,
}, //简单模式
normal: {
row: 15,
col: 15,
mineNum: 30,
}, //普通模式
hard: {
row: 20,
col: 20,
mineNum: 60,
}, //困难模式
custom: {
row: '',
col: '',
mineNum: '',
}, //自定义模式
}
// 当前游戏难度 一开始默认为简单模式
var curLevel=config.easy
index.js
// 游戏主要逻辑
//用于存储生成的地雷的数组
var mineArray = null
//雷区的容器
var mineArea = $('.mineArea')
//用于存储整张地图每个格子额外的一些信息
var tableData = []
//用于存储用户插旗的DOM元素
var flagArray = []
var mineNumber = $('.mineNum')
// 主函数
var btns = document.getElementsByTagName('button')
function main() {
//1.进行游戏的初始化
init()
//2.绑定事件
bindEvent()
}
main()
function clearScene(){
mineArea.innerHTML = ''
flagArray=[];
$('.flagNum').innerHTML=0;//重置插旗
mineNumber.innerHTML=curLevel.mineNum
}
// 初始化
function init() {
//清空场景,或者叫做重置信息
clearScene();
//1.随机生成所选配置对应的数量的雷
mineArray = initMine()
// console.log(mineArray)
//2.根据雷的数量,生成对应的表格
var table = document.createElement('table')
//初始化格子的下标
var index = 0
for (var i = 0; i < curLevel.row; i++) {
var tr = document.createElement('tr')
tableData[i] = []
for (var j = 0; j < curLevel.col; j++) {
var td = document.createElement('td')
var div = document.createElement('div')
//每一个小格子都会对应一个js对象
//该对象存储了额外的一些信息
tableData[i][j] = {
row: i, //该格子的行
col: j, //该格子的列
type: 'number', //格子的属性 数字number 雷mine
value: 0, //周围雷的数量
index, //格子的下标
checked: false, //是否被检验过,后面会用到
}
//为每一个div添加一个下标,方便用户点击的时候获取相应格子的下标
div.dataset.id = index
//当前格子是可以插旗的
// div.classList.add('canFlag')
div.setAttribute('class', 'canFlag')
//查看当前格子的下标是否在雷的数组里面
if (mineArray.includes(tableData[i][j].index)) {
tableData[i][j].type = 'mine'
div.classList.add('mine')
}
td.appendChild(div)
tr.appendChild(td)
//下标自增
index++
}
table.appendChild(tr)
}
mineArea.appendChild(table)
// console.log(table)
// console.log(tableData)
mineArea.onmousedown = function (e) {
if (e.button === 0) {
//左键
//进行区域搜索操作
searchArea(e.target)
}
if (e.button === 2) {
//右键
//插旗子
flag(e.target)
}
}
}
// 随机生成所选配置对应的数量的雷
function initMine() {
//1.创建一个数组,用于存储生成的地雷,生成对应长度的数组
var arr = new Array(curLevel.row * curLevel.col)
//2.往这个数组里面填充值
for (var i = 0; i < arr.length; i++) {
arr[i] = i
}
//3.接下来打乱这个数组
arr.sort(() => 0.5 - Math.random())
//4.只保留对应雷数量的数组长度
return arr.slice(0, curLevel.mineNum)
}
//显示答案
function showAnswer() {
var isAllRight = true
//1.显示所有的雷
//2.有些雷可能插了旗子 需要判断插旗是否正确
//正确添加绿色背景
//错误添加红色背景
var mineArr = $$('td>div.mine')
for (var i = 0; i < mineArr.length; i++) {
mineArr[i].style.opacity = 1
}
//遍历用户的插旗
for (var i = 0; i < flagArray.length; i++) {
if (flagArray[i].classList.contains('mine')) {
//说明插旗插队了
flagArray[i].classList.add('right')
} else {
flagArray[i].classList.add('error')
isAllRight = false
}
}
if (!isAllRight || flagArray.length !== curLevel.mineNum) {
gameOver(false)
}
//取消事件
mineArea.onmousedown = null
}
//找到对应DOM在tableData里面的js对象
function getTableItem(cell) {
var index = cell.dataset.id
// console.log(index)
// console.log(tableData)
var flatTableData = tableData.flat()
return flatTableData.filter((item) => item.index == index)[0]
// console.log(i)
}
//返回该对象对应四周的边界
function getBound(obj) {
// console.log(obj)
//确定上下边界
var rowTop = obj.row - 1 < 0 ? 0 : obj.row - 1
var rowBottom = obj.row + 1 === curLevel.row ? curLevel.row - 1 : obj.row + 1
//确定左右边界
var colLeft = obj.col - 1 < 0 ? 0 : obj.col - 1
var colRight = obj.col + 1 === curLevel.col ? curLevel.col - 1 : obj.col + 1
return {
rowTop,
rowBottom,
colLeft,
colRight,
}
}
//返回周围一圈雷的数量
function findMineNum(obj) {
var count = 0 //地雷计数器
var { rowTop, rowBottom, colLeft, colRight } = getBound(obj)
// console.log(getBound(obj))
for (var i = rowTop; i <= rowBottom; i++) {
for (var j = colLeft; j <= colRight; j++) {
if (tableData[i][j].type === 'mine') {
count++
}
}
}
return count
}
//根据tableData种的js对象返回对应的div
function getDOM(obj) {
var divArray = $$('td>div')
//返回对应下标的div
return divArray[obj.index]
}
//搜索该单元格周围的九宫格区域
function getAround(cell) {
if (!cell.classList.contains('flag')) {
//当前的单元格没有被插旗,我们才进行以下的操作
//搜索该单元格周围的九宫格区域
cell.parentNode.style.border = 'none'
if (cell.getAttribute('class') !== 'mineArea') {
cell.parentNode.style.background = '#d9d9d9'
}
cell.classList.remove('canFlag')
//1.获取到该DOM元素在tableData里面所对应的对象
var tableItem = getTableItem(cell)
if (!tableItem) {
return
}
//代表当前单元格已经被核对过了
tableItem.checked = true
//得到了对应DOM对象所对应的js对象
//那我们就可以查看周围一圈是否有雷
var mineNum = findMineNum(tableItem)
// console.log(mineNum)
if (!mineNum) {
//进入此if周围没有雷
//需要继续搜素
var { rowTop, rowBottom, colLeft, colRight } = getBound(tableItem)
for (var i = rowTop; i <= rowBottom; i++) {
for (var j = colLeft; j <= colRight; j++) {
if (!tableData[i][j].checked) {
getAround(getDOM(tableData[i][j]))
}
}
}
} else {
//当前的格子要显示对应雷的数量
var cl = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight']
cell.classList.add(cl[mineNum])
cell.innerHTML = mineNum
}
}
}
//鼠标左键点击
function searchArea(cell) {
//1.当前单元格子是雷 游戏结束
if (cell.classList.contains('mine')) {
//游戏结束
cell.classList.add('boom')
showAnswer() //显示答案
return
// gameOver()
}
//2.当前单元格不是雷
//2.1旁段周围一圈有没有雷 如果有雷,显示雷的数量
//2.2旁段周围一圈没有雷,显示空白,然后继续搜索周围一圈
getAround(cell)
}
//判断玩家插旗是否全部正确
function isWin() {
for (var i = 0; i < flagArray.length; i++) {
if (!flagArray[i].classList.contains('mine')) {
return false
}
}
return true
}
//判断是否获胜
function gameOver(isWin) {
//分为两种情况 胜利和失败
var mess = ''
if (isWin) {
mess = '游戏胜利,小呆找出了所有雷~'
} else {
mess = '粗心了哦小呆!'
}
window.alert(mess)
}
//插旗
function flag(cell) {
if (cell.classList.contains('canFlag')) {
//只有点击的dom元素包含canFlag这个类名才可以添加旗子
if (!flagArray.includes(cell)) {
//进行插旗操作
flagArray.push(cell)
cell.classList.add('flag')
//还需要进行插旗数的判断
if (flagArray.length === curLevel.mineNum) {
//判断玩家是否胜利
if (isWin()) {
gameOver(true)
}
//无论游戏胜利还是失败我们都应该进入showAnswer 显示最终答案
showAnswer()
}
} else {
//进行取消插旗操作
var index = flagArray.indexOf(cell)
flagArray.splice(index, 1)
cell.classList.remove('flag')
}
$('.flagNum').innerHTML = flagArray.length
}
}
//绑定事件
function bindEvent() {
//阻止默认的鼠标右键行为
mineArea.oncontextmenu = function (e) {
e.preventDefault()
}
//1.点击格子插旗子
//游戏难度选择
$('.level').onclick = function (e) {
for (var i = 0; i < btns.length; i++) {
btns[i].classList.remove('active')
}
e.target.classList.add('active')
switch (e.target.innerHTML) {
case '初级': {
curLevel = config.easy
break
}
case '中级': {
curLevel = config.normal
break
}
case '高级': {
curLevel = config.hard
break
}
case '自定义': {
}
}
init()
}
}
tool.js
// 工具函数
// 1. 获取元素
function $(selector) {
return document.querySelector(selector)
}
//2.获取所有元素
function $$(selector) {
return document.querySelectorAll(selector)
}
3.css
*{
/* 初始化内外边距 */
margin: 0;
padding: 0;
/* 改成边框盒 会把padding什么的算进去 */
box-sizing: border-box;
}
/* .container{
border: #f5b0d0 solid ;
} */
/* 标题 */
h1{
text-align: center;
font-size:46px;
font-weight: 100;
margin:30px auto 20px auto
}
/* 游戏难度选择 */
.level{
text-align: center;
margin-bottom: 10px;
}
.level button{
padding: 5px 15px;
background: #02a4ad;
color: white;
outline: none;
border:none;
border-radius: 3px;
cursor:pointer
}
.level button.active{
background-color: #00abff;
}
.info{
text-align: center;
margin:10px auto;
font-weight: 200;
}
/* 表格相关样式 */
table{
background:#929196;
margin: 0 auto;
border-spacing:1px;
}
table td{
width: 24px;
height: 24px;
background:rgb(245, 213, 228);
border: 2px solid;
border-color: rgb(252, 215, 233) #f5b0d0 #f5b0d0 rgb(252, 215, 233);
text-align:center ;
line-height: 24px;
font-weight: bold;
cursor:pointer
}
td>div{
height: 100%;
width: 100%;
}
/* 显示地雷 */
td>div.mine{
background: rgb(245, 213, 228) url("../img/炸弹.png") center/cover no-repeat;
opacity: 0;
}
/* 显示小旗子 */
td>div.flag{
background: rgb(245, 213, 228) url("../img/旗子.png") center/cover no-repeat;
opacity: 1;
}
td>div.error{
background-color:red
/* background: rgb(245, 213, 228) url("../img/爆炸.png") center/cover no-repeat; */
}
td>div.boom{
background: url("../img/爆炸.png") center/cover no-repeat;
}
td>div.right{
background-color: green;
}
td>div.zero{
border-color:#d9d9d9;
background-color:#d9d9d9;
}
td>div.one{
border-color:#d9d9d9;
background:#d9d9d9;
color:rgb(136, 173, 236);
}
td>div.two{
border-color:#d9d9d9;
background:#d9d9d9;
color:#9BCD9B;
}
td>div.three{
border-color:#d9d9d9;
background:#d9d9d9;
color:#ee8797;
}
td>div.four{
border-color:#d9d9d9;
background:#d9d9d9;
color:red;
}
td>div.five{
border-color:#d9d9d9;
background:#d9d9d9;
color:orange;
}
td>div.six{
border-color:#d9d9d9;
background:#d9d9d9;
color:yellow;
}
td>div.seven{
border-color:#d9d9d9;
background:#d9d9d9;
color:gray;
}
td>div.eight{
border-color:#d9d9d9;
background:#d9d9d9;
color:black;
}