为了账号安全,请及时绑定邮箱和手机立即绑定

做一个扫雷游戏

论游戏中,最深入人心的游戏是什么?最经典的游戏是什么?理所当然,是《扫雷》。

项目地址:项目地址

游戏样例
游戏样例

这就是游戏的整体ui。

首先制作游戏的h5骨架:
可以使用一个div作为容器来放置每个方格,至于时间表,可以最为图片:

index.html

<!DOCTYPE html>
<html lang="cn">
<head>
    <meta charset="UTF-8">
    <title>扫雷</title>
    <link rel="stylesheet" type="text/css" href="css/index.css"/>
    <script type="text/javascript" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>
<div id="info">
    <span id="boomNum"><img class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="img/d0.bmp" id="b-h"/><img class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="img/d0.bmp" id="b-t"/><img class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="img/d0.bmp" id="b-o"/></span>
    <img class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="img/face_normal.bmp" class="face" id="start"/>
</div>
<div id="chess">
</div>
<script type="text/javascript" class="lazyload" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC" data-original="js/index.js"></script>
</body>
</html>

然后编写下css文件,设置html部件的样式
css/index.css

#chess{
    border: #000 solid 1px;
    margin: 10px auto;
}
.block{
    width: 28px;
    height: 28px;
    margin: 0;
    border: #000 solid 1px;
    background-color: #eee;
    float: left;
    font-weight: bold;
    text-align: center;
    line-height: 30px;
    font-size: 20px;
    cursor: pointer;
}
.block:hover{
    background-color: #ccc;
}
#info{
    margin: 0 auto;
    text-align: center;
}
.face{
    border: 2px solid #808080;
    border-left-color: #fff;
    border-top-color: #fff;
    height: 20px;
    width: 20px
}
.face:active{
    border-top:2px solid #808080;
    border-bottom:1px solid #808080;
    border-right:1px solid #808080;
    border-left:2px solid #808080;
}

接下来就开始实现游戏的逻辑实现,首先初始化一些基本变量与数组:

var chess = [];// 存储雷区的值
const num = 100;// 存储地雷数
var surplusNum = num;// 存储剩余地雷数
var styles = [
    "#00f",
    "#0f0",
    "#f00",
    "#08f",
    "#f30",
    "#0fa",
    "#943",
    "#eee"
];// 存储不同数字的颜色样式
var die = false;// 存储是否游戏结束
var chessJq = $("#chess");// 为雷区的jQuery对象
var chessLength = 25;// 为雷区的边长

为了能在游戏结束后能重复开始游戏,则我们将一些初始化代码放入init函数中,并在开始调用它:

init();
function init() {
    // 判断是否地雷数大于格子数
    if (num > Math.pow(chessLength,2))
        return;
    // 设置雷区长宽
    $("#chess").css({
        "width": chessLength * 30,
        "height": chessLength * 30
    });
    // 初始化雷区
    for (let i = 0 ; i < chessLength ; i++){
        chess[i] = [];
        for (let j = 0 ; j < chessLength ; j++){
            chess[i][j] = 0;
        }
    }
    for (let i = 0 ; i < num ; i++){
        while (true){
            var x = Math.floor(Math.random() * chess.length),y = Math.floor(Math.random() * chess.length);
            if (chess[y][x] == 0){
                chess[y][x] = -1;
                break;
            }
        }
    }
    // 初始化数字标记
    for (let i = 0 ; i < chess.length ; i++){
        for (let j = 0 ; j < chess.length ; j++){
            if (chess[i][j] == -1)
                continue;
            chess[i][j] += get(i - 1,j);
            chess[i][j] += get(i + 1,j);
            chess[i][j] += get(i,j - 1);
            chess[i][j] += get(i,j + 1);
            chess[i][j] += get(i - 1,j - 1);
            chess[i][j] += get(i + 1,j + 1);
            chess[i][j] += get(i + 1,j - 1);
            chess[i][j] += get(i - 1,j + 1);
        }
    }
    // 清空雷区
    chessJq.empty();
    for (let i = 0 ; i < chess.length ; i++){
        for (let j = 0 ; j < chess.length ; j++){
            // chessJq.append("<div class='block' id='" + i + "-" + j + "' style='color: " + styles[chess[i][j] - 1] + "'></div>");
            // if (chess[i][j] > 0){
            //     $("#" + i + "-" + j).html(chess[i][j]);
            // }
            chessJq.append("<div class='block' id='" + i + "-" + j + "'></div>");
        }
    }
    surplusNum = num;
    update();
}
function get(i,j) {
    if (i < 0 || i >= chess.length || j < 0 || j >= chess.length)
        return 0;
    if (chess[i][j] == -1)
        return 1;
    return 0;
}

然后,接着实现点击雷区方格露出数字、与右击标记的代码,对此,可以添加一个mousedown的事件。

$(".block").mousedown(function (e) {
    if (die)
        return;
    var me = $(this);
    if (e.which == 1){// left click
        open(me,true,0);
    } else {// right click
        if (me.html().search(/0/g) !== -1){
            me.html("<img src='img/flag.bmp'/>");
            surplusNum--;
            update();
        } else if (me.html().search(/flag/g) !== -1) {
            me.html("<img src='img/ask.bmp'/>");
            surplusNum++;
            update();
        } else {
            me.html("<img src='img/0.bmp'>");
        }
        if (surplusNum == 0){
            for (let i = 0 ; i < chess.length ; i++){
                for (let j = 0 ; j < chess.length ; j++){
                    if (chess[i][j] == -1 && $("#" + i + "-" + j).html().search("flag") == -1){
                        die = true;
                        break;
                    }
                }
            }
            if (die){
                $("#start").attr("src","img/face_fail.bmp");
                dieShow();
            } else {
                $("#start").attr("src","img/face-win.bmp");
            }
        }
    }
});

细心的同学们肯定发现了,接着,我们要开始实现open函数,该函数应该有两个模式,一个是程序自动翻出附近的数字,一个是打开用户操作的方格。可以使用传一个布尔值来判断,并使用递归实现自动翻出附近的数字。


function open(me,canBoom,level) {
    var id = me.attr("id");
    var tmp = id.split("-");
    var x = tmp[1] * 1;
    var y = tmp[0] * 1;
    if (level > 5){
        return;
    }
    if (me.html().search(/flag/g) != -1 || me.html().search(/ask/g) != -1)
        return;
    if (chess[y][x] == -1 && canBoom){
        me.html("<img src='img/blood.bmp'/>");
        die = true;
        $("#start").attr("src","img/face_fail.bmp");
        dieShow();
    } else if (chess[y][x] > 0){
        me.html(chess[y][x]);
        me.css({
            "color": styles[chess[y][x]],
            "background-image": "url(\"img/0.bmp\")"
        });
        let tmp = $("#" + y + "-" + x);
        if (tmp.html().search(/flag/g) != -1 || tmp.html().search(/ask/g) != -1)
            return;
        if (chess[y - 1][x] == -1 || chess[y + 1][x] == -1 || chess[y][x + 1] == -1 || chess[y][x - 1] == -1 ||
            chess[y - 1][x + 1] == -1 || chess[y + 1][x - 1] == -1 || chess[y + 1][x + 1] == -1 || chess[y - 1][x - 1] == -1
        ){
            return;
        }
        open($("#" + (y - 1) + "-" + (x)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x)),false,level + 1);
        open($("#" + (y) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y) + "-" + (x - 1)),false,level + 1);
        open($("#" + (y - 1) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x - 1)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y - 1) + "-" + (x - 1)),false,level + 1);
    } else {
        me.html("<img src='img/0.bmp'>");
        let tmp = $("#" + y + "-" + x);
        if (tmp.html().search(/flag/g) != -1 || tmp.html().search(/ask/g) != -1)
            return;
        if (chess[y - 1][x] == -1 || chess[y + 1][x] == -1 || chess[y][x + 1] == -1 || chess[y][x - 1] == -1 ||
            chess[y - 1][x + 1] == -1 || chess[y + 1][x - 1] == -1 || chess[y + 1][x + 1] == -1 || chess[y - 1][x - 1] == -1
        ){
            return;
        }
        open($("#" + (y - 1) + "-" + (x)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x)),false,level + 1);
        open($("#" + (y) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y) + "-" + (x - 1)),false,level + 1);
        open($("#" + (y - 1) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x - 1)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y - 1) + "-" + (x - 1)),false,level + 1);
    }
}

仔细阅读以上代码,可以发现还有两个函数未实现: update与dieShow。

先啃软柿子,编写update的代码:

function update() {
    surplusNum += "";
    if (surplusNum.length == 2)
        surplusNum = "0" + surplusNum;
    if (surplusNum.length == 1)
        surplusNum = "00" + surplusNum;
    if (surplusNum.length == 0)
        surplusNum = "000";
    $("#b-h").attr("src","img/d" + surplusNum.charAt(0) + ".bmp");
    $("#b-t").attr("src","img/d" + surplusNum.charAt(1) + ".bmp");
    $("#b-o").attr("src","img/d" + surplusNum.charAt(2) + ".bmp");
    surplusNum *= 1;
}

至于dieShow的代码,可以通过遍历存储雷区的数组编写:

function dieShow() {
    for (let i = 0 ; i < chess.length ; i++){
        for (let j = 0 ; j < chess.length ; j++){
            let tmp = $("#" + i + "-" + j);
            if (chess[i][j] == -1){// have boom
                if (tmp.html().search(/blood/g) != -1)
                    continue;
                tmp.html("<img src='img/boom.bmp'/>");
            } else if (tmp.html().search(/flag/g) != -1 && chess[i][j] != -1){// not have boom and user is marked
                tmp.html("<img src='img/error.bmp'/>");
            } else if (chess[i][j] > 0){
                me.html(chess[i][j]);
                tmp.css({
                    "color": styles[chess[i][j]],
                    "background-image": "url(\"img/0.bmp\")"
                });
            } else {
                tmp.html("<img src='img/0.bmp'/>");
            }
        }
    }
}

再编写点击按钮重来的代码

$("#start").click(function () {
    $(this).attr("src","img/face_normal.bmp");
    die = false;
    init();
});

这时测试,游戏就基本完成了...等等,为什么右击无法标记?

因为我们忘写了一个重要而简洁的事件响应函数

$(".block").bind("contextmenu", function(){
    return false;
});

最后js部分全部代码如下:

var chess = [];
const num = 100;
var surplusNum = num;
var styles = [
    "#00f",
    "#0f0",
    "#f00",
    "#08f",
    "#f30",
    "#0fa",
    "#943",
    "#eee"
];
var die = false;
var chessJq = $("#chess");
var chessLength = 25;
init();
function init() {
    if (num > Math.pow(chessLength,2))
        return;
    $("#chess").css({
        "width": chessLength * 30,
        "height": chessLength * 30
    });
    for (let i = 0 ; i < chessLength ; i++){
        chess[i] = [];
        for (let j = 0 ; j < chessLength ; j++){
            chess[i][j] = 0;
        }
    }
    for (let i = 0 ; i < num ; i++){
        while (true){
            var x = Math.floor(Math.random() * chess.length),y = Math.floor(Math.random() * chess.length);
            if (chess[y][x] == 0){
                chess[y][x] = -1;
                break;
            }
        }
    }
    for (let i = 0 ; i < chess.length ; i++){
        for (let j = 0 ; j < chess.length ; j++){
            if (chess[i][j] == -1)
                continue;
            chess[i][j] += get(i - 1,j);
            chess[i][j] += get(i + 1,j);
            chess[i][j] += get(i,j - 1);
            chess[i][j] += get(i,j + 1);
            chess[i][j] += get(i - 1,j - 1);
            chess[i][j] += get(i + 1,j + 1);
            chess[i][j] += get(i + 1,j - 1);
            chess[i][j] += get(i - 1,j + 1);
        }
    }
    chessJq.empty();
    for (let i = 0 ; i < chess.length ; i++){
        for (let j = 0 ; j < chess.length ; j++){
            // chessJq.append("<div class='block' id='" + i + "-" + j + "' style='color: " + styles[chess[i][j] - 1] + "'></div>");
            // if (chess[i][j] > 0){
            //     $("#" + i + "-" + j).html(chess[i][j]);
            // }
            chessJq.append("<div class='block' id='" + i + "-" + j + "'></div>");
        }
    }
    $(".block").mousedown(function (e) {
        if (die)
            return;
        var me = $(this);
        if (e.which == 1){// left click
            open(me,true,0);
        } else {// right click
            if (me.html().search(/0/g) !== -1){
                me.html("<img src='img/flag.bmp'/>");
                surplusNum--;
                update();
            } else if (me.html().search(/flag/g) !== -1) {
                me.html("<img src='img/ask.bmp'/>");
                surplusNum++;
                update();
            } else {
                me.html("<img src='img/0.bmp'>");
            }
            if (surplusNum == 0){
                for (let i = 0 ; i < chess.length ; i++){
                    for (let j = 0 ; j < chess.length ; j++){
                        if (chess[i][j] == -1 && $("#" + i + "-" + j).html().search("flag") == -1){
                            die = true;
                            break;
                        }
                    }
                }
                if (die){
                    $("#start").attr("src","img/face_fail.bmp");
                    dieShow();
                } else {
                    $("#start").attr("src","img/face-win.bmp");
                }
            }
        }
    });
    $(".block").bind("contextmenu", function(){
        return false;
    });
    surplusNum = num;
    update();
}
function dieShow() {
    for (let i = 0 ; i < chess.length ; i++){
        for (let j = 0 ; j < chess.length ; j++){
            let tmp = $("#" + i + "-" + j);
            if (chess[i][j] == -1){// have boom
                if (tmp.html().search(/blood/g) != -1)
                    continue;
                tmp.html("<img src='img/boom.bmp'/>");
            } else if (tmp.html().search(/flag/g) != -1 && chess[i][j] != -1){// not have boom and user is marked
                tmp.html("<img src='img/error.bmp'/>");
            } else if (chess[i][j] > 0){
                me.html(chess[i][j]);
                tmp.css({
                    "color": styles[chess[i][j]],
                    "background-image": "url(\"img/0.bmp\")"
                });
            } else {
                tmp.html("<img src='img/0.bmp'/>");
            }
        }
    }
}
function open(me,canBoom,level) {
    var id = me.attr("id");
    var tmp = id.split("-");
    var x = tmp[1] * 1;
    var y = tmp[0] * 1;
    if (level > 5){
        return;
    }
    if (me.html().search(/flag/g) != -1 || me.html().search(/ask/g) != -1)
        return;
    if (chess[y][x] == -1 && canBoom){
        me.html("<img src='img/blood.bmp'/>");
        die = true;
        $("#start").attr("src","img/face_fail.bmp");
        dieShow();
    } else if (chess[y][x] > 0){
        me.html(chess[y][x]);
        me.css({
            "color": styles[chess[y][x]],
            "background-image": "url(\"img/0.bmp\")"
        });
        let tmp = $("#" + y + "-" + x);
        if (tmp.html().search(/flag/g) != -1 || tmp.html().search(/ask/g) != -1)
            return;
        if (chess[y - 1][x] == -1 || chess[y + 1][x] == -1 || chess[y][x + 1] == -1 || chess[y][x - 1] == -1 ||
            chess[y - 1][x + 1] == -1 || chess[y + 1][x - 1] == -1 || chess[y + 1][x + 1] == -1 || chess[y - 1][x - 1] == -1
        ){
            return;
        }
        open($("#" + (y - 1) + "-" + (x)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x)),false,level + 1);
        open($("#" + (y) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y) + "-" + (x - 1)),false,level + 1);
        open($("#" + (y - 1) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x - 1)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y - 1) + "-" + (x - 1)),false,level + 1);
    } else {
        me.html("<img src='img/0.bmp'>");
        let tmp = $("#" + y + "-" + x);
        if (tmp.html().search(/flag/g) != -1 || tmp.html().search(/ask/g) != -1)
            return;
        if (chess[y - 1][x] == -1 || chess[y + 1][x] == -1 || chess[y][x + 1] == -1 || chess[y][x - 1] == -1 ||
            chess[y - 1][x + 1] == -1 || chess[y + 1][x - 1] == -1 || chess[y + 1][x + 1] == -1 || chess[y - 1][x - 1] == -1
        ){
            return;
        }
        open($("#" + (y - 1) + "-" + (x)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x)),false,level + 1);
        open($("#" + (y) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y) + "-" + (x - 1)),false,level + 1);
        open($("#" + (y - 1) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x - 1)),false,level + 1);
        open($("#" + (y + 1) + "-" + (x + 1)),false,level + 1);
        open($("#" + (y - 1) + "-" + (x - 1)),false,level + 1);
    }
}
function get(i,j) {
    if (i < 0 || i >= chess.length || j < 0 || j >= chess.length)
        return 0;
    if (chess[i][j] == -1)
        return 1;
    return 0;
}
function update() {
    surplusNum += "";
    if (surplusNum.length == 2)
        surplusNum = "0" + surplusNum;
    if (surplusNum.length == 1)
        surplusNum = "00" + surplusNum;
    if (surplusNum.length == 0)
        surplusNum = "000";
    $("#b-h").attr("src","img/d" + surplusNum.charAt(0) + ".bmp");
    $("#b-t").attr("src","img/d" + surplusNum.charAt(1) + ".bmp");
    $("#b-o").attr("src","img/d" + surplusNum.charAt(2) + ".bmp");
    surplusNum *= 1;
}
$("#start").click(function () {
    $(this).attr("src","img/face_normal.bmp");
    die = false;
    init();
});

[完]

点击查看更多内容
16人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
全栈工程师
手记
粉丝
54
获赞与收藏
183

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消