使用学过的算法做个游戏很酷的好吗
说在前面🎈相信大家对于连连看这款游戏都不陌生了吧?还记得在我小时候,有一段时间周边的人都被这游戏给吸引了,那时候我就在想,我点了两个图片,它怎么知道能不能连线,还有明明有很多条路线可以走,为什么就要走那一条?直到后来我学习了BFS算法体验地址大家可以先到体验地址试玩一下,欢迎大家的意见反馈。JYeontuGame在线体验地址:http://jyeontu.xyz/JYeontuGame/#/项目
说在前面
🎈相信大家对于连连看这款游戏都不陌生了吧?还记得在我小时候,有一段时间周边的人都被这游戏给吸引了,那时候我就在想,我点了两个图片,它怎么知道能不能连线,还有明明有很多条路线可以走,为什么就要走那一条?直到后来我学习了BFS算法
体验地址
大家可以先到体验地址试玩一下,欢迎大家的意见反馈。
JYeontuGame在线体验地址:http://jyeontu.xyz/JYeontuGame/#/
项目介绍
本游戏是基于vue2.0的一个项目,服务端使用node简单的做了两个接口,数据库则是使用的mysql。
功能实现
1、初始化页面
使用js动态生成连连看看板格子。
//初始化页面
initPage() {
const row = this.row,
column = this.column;
const content = document.getElementById("game-content");
content.innerHTML = "";
for (let i = 0; i <= parseInt(column) + 1; i++) {
const columnDom = document.createElement("div");
columnDom.classList.add("column");
columnDom.id = `column-${i}`;
for (let j = 0; j <= parseInt(row) + 1; j++) {
const rowDom = document.createElement("div");
rowDom.classList.add("row");
rowDom.id = `row-${i}-${j}`;
const img = document.createElement("img");
img.id = `img-${i}-${j}`;
img.classList.add("img-block");
if (i == 0 || j == 0 || i == column + 1 || j == row + 1) {
img &&
img.setAttribute(
"src",
require("./img/remove.png")
);
} else {
img &&
img.setAttribute(
"src",
this.blockList[(i - 1) * row + j - 1]
);
img.onclick = () => {
this.imgClick(i, j);
};
}
rowDom.appendChild(img);
columnDom.appendChild(rowDom);
}
content.appendChild(columnDom);
}
for (let i = 1; i <= column; i++) {
for (let j = 1; j <= row; j++) {
const img = document.getElementById(i + "-" + j);
img &&
img.setAttribute(
"src",
this.blockList[(i - 1) * row + j - 1]
);
}
}
},
2、图片数组初始化
根据行列数初始化图片数组。
//初始化数据
initData() {
const row = this.row;
const column = this.column;
const imgList = this.imgList;
let blockList = this.blockList;
this.blockMap = new Array(column + 2);
for (let i = 0; i < this.blockMap.length; i++) {
let temp = [];
for (let j = 0; j < row + 2; j++) {
if (i == 0 || j == 0 || i == column + 1 || j == row + 1)
temp.push(true);
else temp.push(false);
}
this.blockMap[i] = temp;
}
let nums = row * column;
if (nums % 2 == 1) {
alert("个数不能为单数");
return;
}
while (nums / 2 > blockList.length) {
const dif = nums / 2 - blockList.length;
blockList.push(...imgList.slice(0, dif));
}
blockList.push(...blockList);
blockList = this.randomSort(blockList);
},
//数组打乱
randomSort(arr) {
return arr.sort((a, b) => {
return Math.random() - 0.5;
});
},
3、BFS判断图片是否可以消除
判断两点之间是否存在可通行路径的方法有两种,dfs和bfs,我们需要找到两点之间的最短路径,所以这里使用bfs算法来寻找路径。
关于bfs和dfs算法我之前也有发过一篇文章,这里我就不再过多描述这个算法的实现了,有兴趣的同学可以去一文带你了解dfs和bfs算法了解一下。
//BFS找出路径
getPath(startX, startY, targetX, targetY) {
let dx = [0, 1, 0, -1],
dy = [1, 0, -1, 0];
let queue = [[startX, startY]];
let flag = new Array(this.blockMap.length); //标记走过的路径
let step = new Array(this.blockMap.length); //存储走过的步数
for (let i = 0; i < flag.length; i++) {
flag[i] = new Array(this.blockMap[i].length).fill(false);
step[i] = new Array(this.blockMap[i].length).fill(Infinity);
}
step[startX][startY] = 0;
flag[startX][startY] = true;
while (queue.length) {
let p = queue.shift();
let x = p[0],
y = p[1];
if (x == targetX && y == targetY) break;
for (let i = 0; i < 4; i++) {
let nx = x + dx[i],
ny = y + dy[i];
if (
nx < 0 ||
nx >= this.blockMap.length ||
ny >= this.blockMap[0].length ||
ny < 0 ||
(
((nx != targetX || ny != targetY) &&
!this.blockMap[nx][ny]) ||
flag[nx][ny] == true
)
) {
continue;
}
flag[nx][ny] = true;
step[nx][ny] = step[x][y] + 1;
queue.push([nx, ny]);
if (nx == targetX && ny == targetY) {
return this.getStep(step, startX, startY, targetX, targetY);
}
}
}
return false;
},
4、找出最短路径
在上一步骤中我们通过BFS到达了终点,且对沿途经过的路径走进行了步数的标记,所以我们只需要从终点往回走即可找出最短路径的坐标集合。
//找出最短路径
getStep(step, startX, startY, targetX, targetY) {
let steps = [];
let dx = [0, 1, 0, -1],
dy = [1, 0, -1, 0];
steps.unshift([targetX, targetY]);
while (targetX != startX || targetY != startY) {
for (let i = 0; i < 4; i++) {
let x = targetX + dx[i],
y = targetY + dy[i];
if (
x < 0 ||
x >= step.length ||
y < 0 ||
y >= step[0].length
)
continue;
if (step[x][y] == step[targetX][targetY] - 1) {
targetX = x;
targetY = y;
steps.unshift([x, y]);
}
}
}
let lines = this.getLine(steps);
},
5、连点成线
在上一步骤中我们获取到了最短路径经过的坐标集合,现在我们需要将同一条轴上的点整合成线,如下图:
需要将上图的点集转换成下图的线集
我们可以这样做:
遇到’x坐标’相等的做个x标记,并将其作为线的起始端点,直到遇到’x坐标’不等的位置,其即为线的结束端点,这样我们可以将同一水平直线上的点集转换成线。
遇到’y坐标’相等的做个y标记,并将其作为线的起始端点,直到遇到’y坐标’不等的位置,其即为线的结束端点,这样我们可以将同竖直直线上的点集转换成线。
具体代码实现如下:
//获取连线集合
getLine(steps) {
let lines = [];
let temp = {
startX: steps[0][0],
startY: steps[0][1]
};
let flag = "";
for (let i = 1; i < steps.length; i++) {
if (
(steps[i][0] != steps[i - 1][0] && flag == "x") ||
(steps[i][1] != steps[i - 1][1] && flag == "y")
) {
temp.endX = steps[i - 1][0];
temp.endY = steps[i - 1][1];
flag = "";
lines.push({ ...temp });
temp = {
startX: steps[i - 1][0],
startY: steps[i - 1][1]
};
}
if (steps[i][0] == temp.startX) flag = "x";
if (steps[i][1] == temp.startY) flag = "y";
}
let len = steps.length - 1;
temp.endX = steps[len][0];
temp.endY = steps[len][1];
lines.push({ ...temp });
return lines;
},
6、绘制连线
在上一步骤中的到线段集合后,我们需要在页面上将其绘制出来,我们可以使用一个div标签来绘制每一条线段。
- (1)获取两个端点在页面上的具体位置
我们可以根据端点坐标取得处于端点的两张图片位置。
const img1 = document.getElementById(`row-${p1[0]}-${p1[1]}`);
const img2 = document.getElementById(`row-${p2[0]}-${p2[1]}`);
- (2)设置线段的长度
两个端点的距离即为线段的长度。
const div = document.createElement("div");
div.style.top = img1.offsetTop + 20 + "px";
let flag = "";
if (img1.offsetTop > img2.offsetTop) {
flag = "h";
div.style.transform = "rotateZ(180deg)";
div.style.transformOrigin = "top";
}
div.style.left = img1.offsetLeft + 20 + "px";
if (img1.offsetLeft > img2.offsetLeft) {
flag = "w";
div.style.transform = "rotateZ(180deg)";
div.style.transformOrigin = "left";
}
const width = Math.abs(img1.offsetLeft - img2.offsetLeft);
const height = Math.abs(img1.offsetTop - img2.offsetTop);
//绘制连线
drawLine(p1, p2) {
const content = document.getElementById("game-content");
const div = document.createElement("div");
const img1 = document.getElementById(`row-${p1[0]}-${p1[1]}`);
const img2 = document.getElementById(`row-${p2[0]}-${p2[1]}`);
div.style.top = img1.offsetTop + 20 + "px";
let flag = "";
if (img1.offsetTop > img2.offsetTop) {
flag = "h";
div.style.transform = "rotateZ(180deg)";
div.style.transformOrigin = "top";
}
div.style.left = img1.offsetLeft + 20 + "px";
if (img1.offsetLeft > img2.offsetLeft) {
flag = "w";
div.style.transform = "rotateZ(180deg)";
div.style.transformOrigin = "left";
}
const width = Math.abs(img1.offsetLeft - img2.offsetLeft);
const height = Math.abs(img1.offsetTop - img2.offsetTop);
if (width == 0) div.style.transition = `height ${this.speed}s`;
else div.style.transition = `width ${this.speed}s`;
div.classList.add("line-style");
content.appendChild(div);
setTimeout(() => {
let h = 0,
w = 0;
if (flag == "h") h = 4;
else if (flag == "w") w = 4;
div.style.width = width - w + "px";
div.style.height = height - h + "px";
}, 0);
this.lineLists.push(div);
},
完整代码
Gitee:https://gitee.com/zheng_yongtao/jyeontu_game.git
GitHub:https://github.com/xitu/game-garden
说在后面
🎉这里是JYeontu,现在是一名前端工程师,有空会刷刷算法题,平时喜欢打羽毛球🏸 ,平时也喜欢写些东西,既为自己记录📋,也希望可以对大家有那么一丢丢的帮助,写的不好望多多谅解🙇,写错的地方望指出,定会认真改进😊,在此谢谢大家的支持,我们下文再见🙌。
更多推荐
所有评论(0)