以下是我需要完成的 Pacman 游戏的样板。我在检测玩家何时会与墙壁碰撞时遇到问题。问题是我不知道玩家相对于迷宫的位置。就像如果玩家按下按键时从迷宫数组中移动一个图块一样,我可以计算出位置并检查相邻图块是否发生碰撞。但由于玩家在按键时以“1px”增量移动,因此我无法跟踪它当前所在的图块。
我尝试过的:
我创建了两个变量来存储玩家当前的行和列。每当玩家移动时,我都会使用 getBoundingClientRect() 方法检查玩家是否完全位于块内,当玩家完全位于块内时,我会更新行和列跟踪器。但我遇到了瓶颈,因为玩家也可以在两个图块之间移动。
请给我一些建议。
let upPressed = false;
let downPressed = false;
let leftPressed = false;
let rightPressed = false;
const main = document.querySelector("main");
//Player = 2, Wall = 1, Enemy = 3, Point = 0
let maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 2, 0, 1, 0, 0, 0, 0, 3, 1],
[1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 1, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1, 1, 1],
[1, 0, 0, 1, 0, 3, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[1, 3, 1, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
];
//Populates the maze in the HTML
for (let y of maze) {
for (let x of y) {
let block = document.createElement("div");
block.classList.add("block");
switch (x) {
case 1:
block.classList.add("wall");
break;
case 2:
block.id = "player";
let mouth = document.createElement("div");
mouth.classList.add("mouth");
block.appendChild(mouth);
break;
case 3:
block.classList.add("enemy");
break;
default:
block.classList.add("point");
block.style.height = "1vh";
block.style.width = "1vh";
}
main.appendChild(block);
}
}
//Player movement
const lbutton = document.querySelector("#lbttn");
const rbutton = document.querySelector("#rbttn");
const ubutton = document.querySelector("#ubttn");
const dbutton = document.querySelector("#dbttn");
function keyUp(event) {
if (event.key === "ArrowUp") {
upPressed = false;
ubutton.style.borderStyle = "outset";
} else if (event.key === "ArrowDown") {
downPressed = false;
dbutton.style.borderStyle = "outset";
} else if (event.key === "ArrowLeft") {
leftPressed = false;
lbutton.style.borderStyle = "outset";
} else if (event.key === "ArrowRight") {
rightPressed = false;
rbutton.style.borderStyle = "outset";
}
}
function keyDown(event) {
if (event.key === "ArrowUp") {
upPressed = true;
ubutton.style.borderStyle = "inset";
} else if (event.key === "ArrowDown") {
downPressed = true;
dbutton.style.borderStyle = "inset";
} else if (event.key === "ArrowLeft") {
leftPressed = true;
lbutton.style.borderStyle = "inset";
} else if (event.key === "ArrowRight") {
rightPressed = true;
rbutton.style.borderStyle = "inset";
}
}
const player = document.querySelector("#player");
const playerMouth = player.querySelector(".mouth");
let playerTop = 0;
let playerLeft = 0;
setInterval(function () {
if (downPressed) {
playerTop++;
player.style.top = playerTop + "px";
playerMouth.classList = "down";
} else if (upPressed) {
playerTop--;
player.style.top = playerTop + "px";
playerMouth.classList = "up";
} else if (leftPressed) {
playerLeft--;
player.style.left = playerLeft + "px";
playerMouth.classList = "left";
} else if (rightPressed) {
playerLeft++;
player.style.left = playerLeft + "px";
playerMouth.classList = "right";
}
}, 10);
function startGame() {
document.addEventListener("keydown", keyDown);
document.addEventListener("keyup", keyUp);
startBtn.style.display = "none";
}
const startBtn = document.querySelector(".start");
startBtn.addEventListener("click", startGame);
你遇到的问题很常见于用更细粒度的移动来处理基于网格的碰撞。你绝对走对了路,用网格的行和列来跟踪玩家的位置!问题在于你只在玩家完全位于一个图块内时才更新行和列,而这个条件不一定成立。
以下是如何改进吃豆人碰撞检测的方法:
- 根据玩家的速度/大小计算“下一个”位置: 与其直接在每个帧更新玩家位置,不如先计算玩家 将要 移动到的位置。
- 使用“下一个”位置检查与网格的碰撞:
- 根据玩家的“下一个”位置,确定它将占据哪些网格图块(由于玩家的大小,可能不止一个)。
- 检查这些图块中是否有任何图块代表墙壁。
- 如果发生碰撞,则停止移动: 如果玩家的“下一个”位置会导致与墙壁碰撞,则不要更新玩家的位置。否则,允许玩家移动到计算出的“下一个”位置。
应用于你的代码示例:
const playerSize = 20; // 假设玩家大小为 20px x 20px
const gridSize = 20; // 假设每个网格图块大小为 20px x 20px
setInterval(function () {
let nextPlayerTop = playerTop;
let nextPlayerLeft = playerLeft;
if (downPressed) {
nextPlayerTop++;
} else if (upPressed) {
nextPlayerTop--;
} else if (leftPressed) {
nextPlayerLeft--;
} else if (rightPressed) {
nextPlayerLeft++;
}
// 计算玩家将占据的网格图块
let nextGridRow = Math.floor(nextPlayerTop / gridSize);
let nextGridCol = Math.floor(nextPlayerLeft / gridSize);
// 边界检查:确保网格行和列在迷宫数组的范围内
if (
nextGridRow >= 0 &&
nextGridRow < maze.length &&
nextGridCol >= 0 &&
nextGridCol < maze[0].length
) {
// 检查与墙壁的碰撞
if (maze[nextGridRow][nextGridCol] !== 1) {
// 如果没有碰撞,则更新玩家位置
playerTop = nextPlayerTop;
playerLeft = nextPlayerLeft;
player.style.top = playerTop + "px";
player.style.left = playerLeft + "px";
}
}
// ...更新玩家嘴巴方向的代码...
}, 10);
在这个改进后的代码中,我们首先计算玩家的“下一个”位置。然后,我们使用这个“下一个”位置来确定玩家将占据哪些网格图块。最后,我们只在确认“下一个”位置没有导致与墙壁碰撞的情况下更新玩家的位置。
这应该可以解决你遇到的碰撞检测问题,并提供更健壮的吃豆人移动体验!
标签:javascript,python,web,game-development From: 78781345