我正在参加「码上掘金挑战赛」详情请看:码上掘金挑战赛来了!
前言
这个游戏大家都不陌生,以前电脑内置小游戏,是一个经典游戏,我以前电脑课上经常玩。制作时间比较短,主要完成了功能,设计呢。。。能玩就行。
游戏介绍
游戏名称: 找出月饼
游戏介绍:
- 游戏有三个等级难度,分别是:初级、中级、高级。
- 每个难度的地图大小不同,越大的地图,月饼越多,难度越大。
- 初级地图9X9大小,月饼数量:10个。中级地图16X16大小,月饼数量:40个。高级地图28X28,月饼数量:99个。
- 左键打开格子,右键插旗🚩。
- 打开的格子,会显示周围8格的月饼数量,点击月饼格子游戏结束。
- 把疑似月饼的格子插上旗子标注好,打开所有非月饼格子视为胜利。
素材:
旗子:
月饼:
游戏预览:
游戏体验
游戏设计
游戏设计分为三个部分:
- 难度选择
- 游戏主体
- 游戏内部
- 剩余月饼
难度选择
使用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;
}
游戏主体
游戏部分准备使用表格来制作,方便一些,样式做一个跟电脑内置扫雷一样的样子就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;
}
游戏功能
现在完成一些游戏的功能,大概分为这几部分:
- 基础设置
- 难度选择
- 随机月饼
- 创建格子
- 根据周围月饼数量变更数字及剩余月饼数量
- 点击格子和插旗
- 游戏结束
基础设置
玩DOM游戏有个很大的问题,就是网页上的文字和图片会被选中,很烦! 让图片文字不能选中
document.addEventListener("selectstart", function (e) {
e.preventDefault();
})
游戏左键是打开格子,右键是插旗,但是和浏览器右键冲突了。
取消游戏盒子中浏览器右键菜单
$('.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
祝大家中秋节快乐哟!