月饼都藏起来了,你能找到全部月饼吗?

7,320 阅读6分钟

我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!

前言

这个游戏大家都不陌生,以前电脑内置小游戏,是一个经典游戏,我以前电脑课上经常玩。制作时间比较短,主要完成了功能,设计呢。。。能玩就行。

游戏介绍

游戏名称: 找出月饼

游戏介绍:

  1. 游戏有三个等级难度,分别是:初级、中级、高级。
  2. 每个难度的地图大小不同,越大的地图,月饼越多,难度越大。
  3. 初级地图9X9大小,月饼数量:10个。中级地图16X16大小,月饼数量:40个。高级地图28X28,月饼数量:99个。
  4. 左键打开格子,右键插旗🚩。
  5. 打开的格子,会显示周围8格的月饼数量,点击月饼格子游戏结束。
  6. 把疑似月饼的格子插上旗子标注好,打开所有非月饼格子视为胜利。

素材:

旗子: a4f0q-shbgq.png

月饼: yb.png

游戏预览:

QQ录屏20220906104231.gif

游戏体验

游戏设计

游戏设计分为三个部分:

  1. 难度选择
  2. 游戏主体
  3. 游戏内部
  4. 剩余月饼

难度选择

使用button按钮标签创建三个按钮。

<div class="level">
    <button class="active">初级</button>
    <button>中级</button>
    <button>高级</button>
</div>

稍微设置一下按钮的CSS样式,再给按钮点击稍微变个色,不然完全看不下去。

.level button {
    padding: 5px 15px;
    background-color: #02a4ad;
    border: none;
    color: #fff;
    border-radius: 3px;
    outline: none;
    cursor: pointer;
}

.level button.active {
    background-color: #00abff;
}

image.png

游戏主体

游戏部分准备使用表格来制作,方便一些,样式做一个跟电脑内置扫雷一样的样子就OK啦.

<div class="gameBox">
    
</div>
table {
    border-spacing: 1px;
    background-color: #929196;
    margin: 0 auto;
    cursor: pointer;
}

td {
    padding: 0;
    width: 50px;
    height: 50px;
    background-color: #ccc;
    border: 2px solid;
    border-color: #fff #a1a1a1 #a1a1a1 #fff;
    text-align: center;
    line-height: 20px;
    font-weight: bold;
    font-size: 30px;

}

哦吼,CSS写了table和td的样式,但是HTML没有这两个标签。

等下看游戏功能部分就知道了。

游戏内部

这一快指的是使用JavaScript更改类名使游戏界面发生变化或者达到一些效果。

旗子:

.flag {
    background: #ccc url(../images/flag.webp) no-repeat center;
    background-size: cover;
}

月饼:

.mine {
    background: #d9d9d9 url(../images/yb.png) no-repeat center;
    background-size: cover;
}

一些周围月饼的数字,每个数字颜色都不相同

td.zero {
    background-color: #d9d9d9;
    border-color: #d9d9d9;
}

td.one {
    background-color: #d9d9d9;
    border-color: #d9d9d9;
    color: #0332fe;
}

td.two {
    background-color: #d9d9d9;
    border-color: #d9d9d9;
    color: #019f02;
}

td.three {
    background-color: #d9d9d9;
    border-color: #d9d9d9;
    color: #ff2600;
}

td.four {
    background-color: #d9d9d9;
    border-color: #d9d9d9;
    color: #93208f;
}

td.five {
    background-color: #d9d9d9;
    border-color: #d9d9d9;
    color: #ff7f29;
}

td.six {
    background-color: #d9d9d9;
    border-color: #d9d9d9;
    color: #ff3fff;
}

td.seven {
    background-color: #d9d9d9;
    border-color: #d9d9d9;
    color: #3fffbf;
}

td.eight {
    background-color: #d9d9d9;
    border-color: #d9d9d9;
    color: #22ee0f;
}

剩余月饼

还需要加一点基础教程,万一哪些人啥都不知道怎么办呢,对吧。

<div class="info">
    剩余月饼:<span class="mineNum"></span>
    <br>
    <span class="tips">左键开月饼,右键插旗,再次点击右键拔旗</span>
</div>
.info {
    margin-top: 10px;
    text-align: center;
}
.tips {
    color: red;
    font-size: 16px;
}

image.png

游戏功能

现在完成一些游戏的功能,大概分为这几部分:

  1. 基础设置
  2. 难度选择
  3. 随机月饼
  4. 创建格子
  5. 根据周围月饼数量变更数字及剩余月饼数量
  6. 点击格子和插旗
  7. 游戏结束

基础设置

玩DOM游戏有个很大的问题,就是网页上的文字和图片会被选中,很烦! 让图片文字不能选中

document.addEventListener("selectstart", function (e) {
    e.preventDefault();
})

游戏左键是打开格子,右键是插旗,但是和浏览器右键冲突了。

image.png

取消游戏盒子中浏览器右键菜单

$('.gameBox')[0].oncontextmenu = function () {
    return false;
}

难度选择

难度分为三级:初级、中级、高级。

  • 初级的地图大小为9X9,月饼数量:10个。
  • 中级的地图大小为16X16,月饼数量:40个。
  • 高级的地图大小为28X28,月饼数量:99个。
let map = [
    [9, 9, 10],
    [16, 16, 40],
    [28, 28, 99]
]

点击按钮选择难度

  • 先定义一个game等于空,不然在函数里创建,外部获取不到。
  • 获取三个按钮,给点击的按钮添加点击的类名,并且使用JQuery排它方法,移出其它兄弟元素的点击类名。
  • 创建game类,执行game类里面的init(初始化)函数。
let game = null;

let btn = $('.level>button');

btn.on('click', function () {
    $(this).addClass('active').siblings().removeClass();
    game = new Game(map[$(this).index()][0], map[$(this).index()][1], map[$(this).index()][2]);
    game.init();
})
  • 写一个名为Game的类。
  • tr是横,td是竖,num是月饼数量。
  • 定义两个数组,squares是用来存储格子的信息(通常以对象方式存储),而tds是用来存格子的dom(通常以标签方式存放)。

init函数在创建地图细讲。

class Game {
    constructor(tr, td, num) {
        this.tr = tr;
        this.td = td;
        this.num = num;
        this.squares = [];
        this.tds = [];
    }
    init() {
        
    }
}

创建地图

随机月饼

Tips: 像这些XXX()函数,是写在Game类之下。

  • 生成一个square的空数组,长度为格子的总数。
  • 使用for循环,让数组里的值等于他在长度的位置。
  • 使用sort+随机数的方法,让数组打乱。
  • 最后只保留当前难度的月饼数量
randomNum() {
    let square = new Array(this.tr * this.td);
    for (let i = 0; i < square.length; i++) {
        square[i] = i;
    }
    square.sort(function () {
        return 0.5 - Math.random();
    })
    return square.slice(0, this.num);
}

初始化地图

  • 定义一个randomNum等于随机月饼的位置。
  • 定义一个index用来这个地方是否有月饼。如果与数组对上来,说明这里有月饼。
  • 如果index对上了,那给存信息的squares这个格子的信息为 {type: 'mine', x: j, y: i}。
  • 如果没有对上,这个格子的gquares信息为 {type: 'number', x: j, y: i, value: 0}。
  • 执行更新数字和创建地图函数。
  • 最后让剩余月饼数显示出来。
init() {
    let randomNum = this.randomNum()
    let index = -1;
    
    for (let i = 0; i < this.tr; i++) {
        this.squares[i] = [];
        for (let j = 0; j < this.td; j++) {
            index++
            if (randomNum.indexOf(index) != -1) {
                this.squares[i][j] = {
                    type: 'mine',
                    x: j,
                    y: i
                }
            } else {
                this.squares[i][j] = {
                    type: 'number',
                    x: j,
                    y: i,
                    value: 0
                }
            }
        }
    }

    this.updateNum();
    this.createMap();
    
    this.mineNum = $('.mineNum');
    this.mineNum.text(this.num);
}

创建格子

  • 定义that等于this,为了可以在函数里面正常使用createMap函数的this。
  • 使用二维数组,将定义的td添加到tr里,再把tr添加到table表格中。
  • 把格子添加到tds数组里,这样可以直接操控每个单元格。
createMap() {
    let that = this;
    let table = $('<table></table>');

    for (let i = 0; i < this.tr; i++) {
        let tr = $('<tr></tr>');
        this.tds[i] = [];
        for (let j = 0; j < this.td; j++) {
            let td = $('<td></td>');
            this.tds[i][j] = td;
            td.on('mousedown', function (e) {
                that.play(e, this);
            })
            td[0].pos = [i, j];
            tr.append(td);
        }
        table.append(tr);
    }

    $('.gameBox').html('');
    $('.gameBox').append(table)
}

根据周围月饼数量变更数字及剩余月饼数量

  • 既然要找到周围的月饼,更改数量的话,那首先要获取周围8格的月饼数量。
  • 首先获取位置,xy。
  • 定义一个数组,返回多个坐标。
  • 判断:判断超出边界、判断循环到自己和判断周围有月饼。判断通过就返回成功的坐标。

找到格子周围的八个格子

getAround(square) {
    let x = square.x;
    let y = square.y;
    let result = [];
    for (let i = x - 1; i <= x + 1; i++) {
        for (let j = y - 1; j <= y + 1; j++) {
            if (
                i < 0 ||
                j < 0 ||
                i > this.td - 1 ||
                j > this.tr - 1 ||
                (i == x && j == y) ||
                this.squares[j][i].type == 'mine'
            ) { continue; }
            result.push([j, i])
        }
    }
    return result;
}

更新所有数字 判断周围八个格子有月饼的话,让格子的value加1,用来表示周围月饼数量。

updateNum() {
    for (let i = 0; i < this.tr; i++) {
        for (let j = 0; j < this.td; j++) {
            if (this.squares[i][j].type == 'number') {
                continue;
            }
            let num = this.getAround(this.squares[i][j]);
            for (var k = 0; k < num.length; k++) {
                this.squares[num[k][0]][num[k][1]].value += 1;
            }
        }
    }

点击格子和插旗

  • 在创建格子的时候,写了点击函数,当td点击时,执行play函数,传入element和当前按钮。
  • 判断element.which,左键为1,右键为3。
  • 使用传进来的值,获取点击的位置。
  • 判断点击的格子是数字就获取周围八个格子的月饼,并且把数量打印在格子里,根据数量添加对应的类名,每个类名都有自己的颜色。
  • 如果点击的是月饼,则游戏结束。
  • 右键点击时,如果是数字(已经打开了格子),那return出去,不能进行下一步。
  • 右键切换类名,这样可以实现插旗,拔旗功能。
  • 利用squares数组进行判断游戏是否赢,赢了弹出恭喜你!
play(e, obj) {
    let that = this;
    if (e.which == 1 && obj.className != 'flag') {
        let curSquare = this.squares[obj.pos[0]][obj.pos[1]];
        let c = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight'];
        if (curSquare.type == 'number') {
            $(obj).text(curSquare.value).removeClass().addClass(c[curSquare.value]);
            if (curSquare.value == 0) {
                $(obj).text('');
                function getAllZero(square) {
                    let around = that.getAround(square);
                    for (let i = 0; i < around.length; i++) {
                        let x = around[i][0];
                        let y = around[i][1];
                        if (!that.tds[x][y].hasClass('flag')) {
                            that.tds[x][y].addClass(c[that.squares[x][y].value]);
                            if (that.squares[x][y].value == 0) {
                                if (!that.tds[x][y].check) {
                                    that.tds[x][y].check = true;
                                    getAllZero(that.squares[x][y])
                                }
                            } else {
                                that.tds[x][y].text(that.squares[x][y].value)
                            }
                        }
                    }
                }
                getAllZero(curSquare)
            }
        } else {
            this.gameOver();
        }
    }
    // 点击右键
    if (e.which == 3) {
        if (obj.className && obj.className != 'flag') return;
        $(obj).toggleClass('flag');
        if ($(obj).hasClass('flag')) {
            this.mineNum.text(this.num -= 1);
        } else {
            this.mineNum.text(this.num += 1);
        }
        if (this.squares[obj.pos[0]][obj.pos[1]].type == 'mine') {
            this.win = true;
        } else {
            this.win = false;
        }
        if (this.num == 0) {
            if (this.win) {
                alert('恭喜你,找出了所有月饼,中秋快乐!');
            }
        }
    }
}

游戏结束

gameOver() {
    for (let i = 0; i < this.td; i++) {
        for (let j = 0; j < this.tr; j++) {
            if (this.squares[i][j].type == 'mine') {
                this.tds[i][j].addClass('mine');
                this.mineNum.text(this.num = 0);
            }
        }
    }
}

结尾

本人只是个技校生啦,学的很菜,这次看到掘金又出新活动了,加班加点写了一些东西,再原有代码上修改了部分东西。

由于疫情原因,这个中秋回不了家啦,有谁可以给我送盒月饼吗,掘金上的月饼我别想了,我抽不到的哈哈哈哈。 有钱的大哥想送月饼加我QQ:1839177674

祝大家中秋节快乐哟!