整个棋盘的大小通过读取可视窗口来计算,通过vue的onMountd钩子来调用棋盘初始化,根据玩家所选的难度生成不同的障碍物地形数量和npc数量以及npc的移动速度。
通过键盘事件监听正确的玩家按键来决定玩家下一步的方位,通过下一步的按键找到对应的格子通过格子定位到玩家的下一步正确位置,对边界进行判断无法移动到边界和障碍物地形。同时调用碰撞检测函数是否与npc发生碰撞。
开始以后,给予npc移动状态、随机时间、移动速度,使得npc们错位时间下移动随机位置,并调用判断周围是否有玩家函数,如果有玩家那么调用A*寻路算法追逐玩家。
当玩家抵达右下角后清除所有定时器,调用胜利弹窗
当玩家爱心数量为0时,调用失败弹窗
function init() {
//获取屏幕宽高
h = document.querySelector(".game-container").offsetHeight
w = document.querySelector(".game-container").offsetWidth
containerSytle.value.width = w + 'px'
containerSytle.value.height = h + 'px'
//盒子宽度设置好
box = document.querySelector(".game-container")
boxItem.value.width = w / 30 - 0.04 + 'px'
boxItem.value.height = h / 15 - 0.04 + 'px'
//起点终点
startRow = 0,
startCol = 0,
endRow = 14,
endCol = 29
//初始化容器
ground = [],
annimalList = []
canWInFlag = false;
moveTarget = new Array(300).fill(0)
//初始化玩家及其血条
const spanPlaer = document.createElement("span")
spanPlaer.id = "player"
spanPlaer.style.width = boxItem.value.width
spanPlaer.style.height = boxItem.value.height
const xt = document.createElement("span")
xt.className = "groth"
for (let i = 1; i <= 3; i++) {
const img = document.createElement("img")
img.src = './GameIcon/爱心.png'
xt.appendChild(img)
}
xt.style.bottom = offsetH - 10 + 'px'
spanPlaer.appendChild(xt)
box.appendChild(spanPlaer)
player = document.getElementById("player")
animalSpan = []
animalHas = []
moveIndex = {
left: 0,
top: 0,
};
offsetW = w / 30
offsetH = h / 15
board = Array.from({ length: 15 }).map(() =>
Array.from({ length: 30 }).fill(0)
)
vis = Array.from({ length: 15 }).map(() =>
Array.from({ length: 30 }).fill(0)
)
dangerAnimal = Array.from({ length: 15 }).map(() =>
Array.from({ length: 30 }).fill(0)
)
//初始化棋盘
for (let i = 1; i <= 450; i++) {
let div = document.createElement("div")
div.className = "game-item"
div.style.width = boxItem.value.width
div.style.height = boxItem.value.height
box.appendChild(div)
}
var dx = [-1, 0, 1, 0],
dy = [0, 1, 0, -1]
//游戏初始化
let w, h, ground, annimalList, moveTarget, moveIndex, player, animalSpan, board, vis, dangerAnimal, divList, offsetW, offsetH, startRow, startCol, endRow, endCol, canWInFlag, box, stopTimer, animalHas
//坐标生成函数
function indexsAppear() {
return [Math.floor(Math.random() * 15), Math.floor(Math.random() * 30)]
}
function toIndexs(row, col) {
return row * 30 + col
}
//地形生成
function groundAppear() {
for (let i = 1; i <= 100; i++) {
let divList = document.querySelectorAll(".game-item")
let arr = indexsAppear();
if (arr[0] == startRow && arr[1] == startCol || arr[0] == endRow && arr[1] == endCol) {
i--;
continue
}
let indexs = toIndexs(arr[0], arr[1])
ground.push({
row: arr[0],
col: arr[1]
})
board[arr[0]][arr[1]] = 999;
divList[indexs].id = "ground"
}
}
//随机动物阻挡格子生成
function danger(diff) {
let cnt = diff;
for (let i = 1; i <= cnt; i++) {
let indexs = indexsAppear()
//起点周围9个格子不允许有动物
if (board[indexs[0]][indexs[1]] === 999 || dangerAnimal[indexs[0]][indexs[1]] === 1 ||
indexs[0] == startRow && indexs[1] == startCol || indexs[0] == endRow && indexs[1] == endCol || indexs[0] <
3 && indexs[1] < 3) {
i--;
continue;
}
dangerAnimal[indexs[0]][indexs[1]] = 1;
annimalList.push({
row: indexs[0],
col: indexs[1]
})
//处理动物 这些动物经过定位在棋盘某个位置上
let row = indexs[0], col = indexs[1]
if (animalHas.indexOf(toIndexs(row, col)) !== -1) {
i--;
continue;
}
animalHas.push(toIndexs(row, col))
let span = document.createElement("span")
span.className = "animalDanger"
span.style.width = boxItem.value.width
span.style.height = boxItem.value.height
span.style.top = row * offsetH + 'px'
span.style.left = col * offsetW + 'px'
span.style.backgroundImage = `url(./GameIcon/${i - 1}.png)`
box.appendChild(span)
animalSpan.push(span)
}
}
//地形是否支持到达终点
function canWin(row, col) {
if (row === endRow && col === endCol) {
canWInFlag = true;
return;
}
for (let i = 0; i < 4; i++) {
let tx = row + dx[i]
let ty = col + dy[i]
if (tx < 0 || ty < 0 || tx >= 15 || ty >= 30 || vis[tx][ty] === 1 || board[tx][ty] === 999 || dangerAnimal[tx][
ty
] === 1) continue;
vis[tx][ty] = 1;
canWin(tx, ty)
}
}
//玩家的创建和移动
function playerMove(e) {
e.preventDefault()
let divList = document.querySelectorAll(".game-item")
switch (e.key) {
case 'ArrowLeft':
if (moveIndex.left - 1 < 0 || divList[toIndexs(moveIndex.top, moveIndex.left - 1)].id === "ground") return;
moveSy.currentTime = 0;
moveSy.play()
gameStart.step++;
moveIndex.left--
player.style.left = divList[moveIndex.left].offsetLeft + 'px'
break;
case 'ArrowRight':
if (moveIndex.left + 1 > 29 || divList[toIndexs(moveIndex.top, moveIndex.left + 1)].id === "ground") return;
moveSy.currentTime = 0;
moveSy.play()
moveIndex.left++
gameStart.step++;
player.style.left = divList[moveIndex.left].offsetLeft + 'px'
break;
case 'ArrowDown':
if (moveIndex.top + 1 > 14 || divList[toIndexs(moveIndex.top + 1, moveIndex.left)].id === "ground") return;
moveSy.currentTime = 0;
moveSy.play()
moveIndex.top++
gameStart.step++;
player.style.top = divList[moveIndex.top * 30].offsetTop + 'px'
break;
case 'ArrowUp':
if (moveIndex.top - 1 < 0 || divList[toIndexs(moveIndex.top - 1, moveIndex.left)].id === "ground") return;
moveSy.currentTime = 0;
moveSy.play()
gameStart.step++;
moveIndex.top--
player.style.top = divList[moveIndex.top * 30].offsetTop + 'px'
break;
}
}
//周围是否有玩家
function hasPlayer(sRow, sCol, eRow, eCol) {
for (let i = sRow; i <= eRow; i++) {
for (let j = sCol; j <= eCol; j++) {
if (i === moveIndex.top && j === moveIndex.left) {
gameStart.chase++
return false;
}
}
}
return true;
}
function startMove() {
for (let i = 0; i < annimalList.length; i++) {
let randomTime = Math.floor(Math.random() * difficultyInfo.value[difficulty.value].animalMoveSpeed +
Math.random() * difficultyInfo.value[difficulty.value].animalMoveSpeed + 100)
timerList.value.push(
setInterval(() => {
animalMove(i, difficultyInfo.value[difficulty.value].animalChase);
}, randomTime)
);
}
}
function animalMove(target, t) {
//感应玩家到四周追逐玩家 或者自由移动
let sRow = Math.max(0, annimalList[target].row - difficultyInfo.value[difficulty.value].around)
let sCol = Math.max(0, annimalList[target].col - difficultyInfo.value[difficulty.value].around)
let eRow = Math.min(14, annimalList[target].row + difficultyInfo.value[difficulty.value].around)
let eCol = Math.min(29, annimalList[target].col + difficultyInfo.value[difficulty.value].around)
if (!hasPlayer(sRow, sCol, eRow, eCol)) {
AStar(annimalList[target].row, annimalList[target].col, moveIndex.top, moveIndex.left, target, t);
} else {
let ts = Math.floor(Math.random() * 4)
let row = annimalList[target].row + dx[ts];
let col = annimalList[target].col + dy[ts];
if (row < 0 || col < 0 || row > 14 || col > 29 || board[row][col] === 999) {
return
}
animalSpan[target].style.left = col * offsetW + 'px'
animalSpan[target].style.top = row * offsetH + 'px'
annimalList[target].row = row
annimalList[target].col = col
}
}
//A*函数
function AStar(startx, starty, endx, endy, targetDanger, t) {
//曼哈顿距离
function mhd(startx, starty, targetx, targety) {
return Math.abs(startx - targetx) + Math.abs(targety - starty)
}
let dis = Array.from({ length: 15 }).map(() => Array.from({ length: 30 }).fill(Infinity))
let AVis = Array.from({ length: 15 }).map(() => Array.from({ length: 30 }).fill(0))
let prev = Array.from({ length: 15 }, () => Array(30).fill(null));
let heap = []
dis[startx][starty] = 0;
heap.push({ row: startx, col: starty, step: +mhd(startx, starty, endx, endy) });
while (heap.length > 0) {
heap.sort((a, b) => a.step - b.step)
let { row, col } = heap.shift()
if (row === endx && col === endy) break;
if (AVis[row][col] === 1) continue;
AVis[row][col] = 1;
for (let i = 0; i < 4; i++) {
let tx = row + dx[i];
let ty = col + dy[i];
if (tx < 0 || ty < 0 || tx > 14 || ty > 29 || board[tx][ty] === 999) continue;
if (dis[row][col] + 1 < dis[tx][ty]) {
dis[tx][ty] = dis[row][col] + 1
prev[tx][ty] = { row, col };
heap.push({ row: tx, col: ty, step: dis[row][col] + 1 + mhd(tx, ty, endx, endy) });
}
}
}
let path = [];
let currentNode = { row: endx, col: endy };
while (currentNode !== null) {
path.unshift(currentNode);
currentNode = prev[currentNode.row][currentNode.col];
}
if (path.length > 1) { // 确保找到了有效路径
let indexs = 0
let timer = setInterval(() => {
indexs++
animalSpan[targetDanger].style.left = path[indexs].col * offsetW + 'px'
animalSpan[targetDanger].style.top = path[indexs].row * offsetH + 'px'
annimalList[targetDanger].row = path[indexs].row
annimalList[targetDanger].col = path[indexs].col
if (indexs + 1 >= path.length) {
// moveTarget[targetDanger] = 0
clearInterval(timer)
return
}
}, t)
} else {
return;
}
}
function hit() {
if (!ifStart.value) return
for (let i = 0; i < animalSpan.length; i++) {
// 定义碰撞缓冲区的大小
if (
(animalSpan[i].offsetLeft + animalSpan[i].offsetWidth>= player.offsetLeft) &&
(animalSpan[i].offsetLeft<= player.offsetLeft + player.offsetWidth) &&
(animalSpan[i].offsetTop + animalSpan[i].offsetHeight >= player.offsetTop) &&
(animalSpan[i].offsetTop <= player.offsetTop + player.offsetHeight)
) {
gameStart.groth--;
let groth = document.querySelector(".groth")
pengzhuang.currentTime = 0;
pengzhuang.play()
if (groth.lastChild) groth.removeChild(groth.lastChild)
if (gameStart.groth < 1) {
document.getElementById("bai").play()
gameStart.gameState = false;
gameVictory.value = false;
document.documentElement.removeEventListener("keydown", playerMove)
clearInterval(stopTimer)
} else {
//进入短暂无敌状态
invincible.value = true
clearInterval(stopTimer)
setTimeout(() => {
invincible.value = false
}, 400)
}
}
}
{{ gameState.gameVictory === true ? "恭喜你,赢了!" : "很遗憾,输了!" }}
{{info[gameState.difficulty] }}
走了 {{ gameState.step }} 步到达终点,躲过 {{ gameState.chase }} 次动物的追击
打败了全世界的人!
至此整个项目的部分代码和逻辑就到这里结束啦,这样看是不是也不能很难呢,由于完整代码太长了,大家可以试着思路自己写以下,如果实在写不出来的话可以关注我在底下评论我把代码打包发给你哦。