React组件中的选择性状态未更新



我正在尝试在React中创建一个基本的蛇游戏。我似乎误解了状态。现在玩家可以移动了,樱桃也被放置起来了,但当检查move((函数内部的碰撞时,我似乎无法更新gameOver状态。有什么明显的东西我遗漏了吗?感谢您花时间阅读和回复。

import { useEffect, useState } from "react";
const Snake = () => {
const timer = (ms) => new Promise((res) => setTimeout(res, ms));
let [gameStarted, setGameStarted] = useState(false);
let [gameOver, setGameOver] = useState(false);
let [playerLocation, setPlayerLocation] = useState({ x: 20, y: 20 });
let [cherryLocation, setCherryLocation] = useState({});
let direction = "W";
const tableWidth = 40;
const tableHeight = 40;
const cellSize = "10px";
const handleKeyPress = (e) => {
switch (e.code) {
case "ArrowLeft":
direction = "W";
break;
case "ArrowRight":
direction = "E";
break;
case "ArrowUp":
direction = "N";
break;
case "ArrowDown":
direction = "S";
break;
}
};
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
const placeCherry = () => {
return {
x: getRandomInt(0, tableWidth),
y: getRandomInt(0, tableHeight)
};
};
const checkCollision = () => {
if (playerLocation.x <= 0 || playerLocation.x >= tableWidth || playerLocation.y <= 0 || playerLocation.y >= tableHeight) {
return true
}
return false
};
const startGame = () => {
if (!gameStarted) {
setGameStarted(!gameStarted);
setGameOver(!gameOver);
update();
}
};
const move = () => {
let newX = playerLocation.x;
let newY = playerLocation.y;
switch (direction) {
case "N":
newY = playerLocation.y -= 1;
break;
case "S":
newY = playerLocation.y += 1;
break;
case "E":
newX = playerLocation.x += 1;
break;
case "W":
newX = playerLocation.x -= 1;
break;
}
if(checkCollision()) {
setGameOver(!gameOver);
}
setPlayerLocation((prevState) => ({
...prevState,
x: newX,
y: newY
}));
};
const update = async () => {
setCherryLocation(placeCherry());
while (!gameOver) {
console.log(gameOver);
await timer(100);
move();
}
alert("Game Over!");
};

const renderBoard = () => {
return (
<table style={{ border: "2px solid rgba(0, 0, 0, 0.05)" }}>
<tbody>
{[...Array(tableHeight)].map((y, i) => {
return (
<tr key={i}>
{[...Array(tableWidth)].map((x, j) => {
if (playerLocation.y === i && playerLocation.x === j) {
return (
<td
style={{
width: cellSize,
height: cellSize,
backgroundColor: "green"
}}
key={j}
>
{" "}
</td>
);
}
if (cherryLocation.y === i && cherryLocation.x === j) {
return (
<td
style={{
width: cellSize,
height: cellSize,
backgroundColor: "pink"
}}
key={j}
>
{" "}
</td>
);
}
return (
<td
style={{
width: cellSize,
height: cellSize,
backgroundColor: "black"
}}
key={j}
>
{" "}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
);
};
useEffect(() => {
window.addEventListener("keydown", handleKeyPress);
return () => {
window.removeEventListener("keydown", handleKeyPress);
};
}, []);
return (
<div>
<p>Snake</p>
<div style={{ display: "flex", justifyContent: "center" }}>
{renderBoard()}
</div>
<div>
<button
disabled={gameOver}
style={{ width: "250px", height: "50px", marginTop: "30px" }}
onClick={startGame}
>
Start
</button>
</div>
</div>
);
};
export default Snake;

异步函数内部没有更新gameOver状态,这导致了一个无限while循环。所以我试着用setInterval更新器函数替换计时器。

用以下内容替换现有功能以继续操作。

const timerIdRef = useRef();

useEffect(() => {
if (gameOver) {
alert("Game Over!");
}

if (!gameOver && !checkCollision()) {
setCherryLocation(placeCherry());
}

return () => clearInterval(timerIdRef.current);
}, [gameOver]);

const handleKeyPress = (e) => {
switch (e.code) {
case "ArrowLeft":
direction = "W";
update();
break;
case "ArrowRight":
direction = "E";
update();
break;
case "ArrowUp":
direction = "N";
update();
break;
case "ArrowDown":
direction = "S";
update();
break;
}
};

const startGame = () => {
if (!gameStarted) {
setGameStarted(true);
setGameOver(false);
update();
}
};

const move = () => {
let newX = playerLocation.x;
let newY = playerLocation.y;
switch (direction) {
case "N":
newY = playerLocation.y -= 1;
break;
case "S":
newY = playerLocation.y += 1;
break;
case "E":
newX = playerLocation.x += 1;
break;
case "W":
newX = playerLocation.x -= 1;
break;
}

if (checkCollision()) {
setGameOver(true);
}
setPlayerLocation({
x: newX,
y: newY
});
};

const update = () => {
clearInterval(timerIdRef.current);
if (!gameOver) {
timerIdRef.current = setInterval(() => move(), 100);
}
};

是的,状态更新似乎太复杂了。这里,假定setPlayerLocation()仅更新playerLocation变量。所以,你可以说,

setPlayerLocation({
x : theNewX,
y : theNewY
});

并且它应该更新playerLocation状态。

最新更新