Я пытался сделать простую змеиную игру с помощью REACT. Все было хорошо, пока мне не понадобилось использовать useEffect для перемещения «змейки» при нажатии клавиши. Когда я пытаюсь поместить moveSnake() в useEffect, это дает мне некоторую ошибку. Когда я перемещаю его и называю внешним эффектом, получается бесконечный цикл. Я использую функциональные компоненты. App.js запутался, потому что я был в стрессе, потому что он не работает, и перепробовал все варианты. Надеюсь, у тебя все получится.
https://github.com/Pijano97/snake-game код здесь, спасибо. Также, если кто-то не может получить к нему доступ, вот код. Компонент змеи просто визуализирует змеиные точки с картой. Компонент еды просто создает случайную еду на карте.
import { useEffect, useState } from "react";
import "./App.css";
import Snake from "./Snake";
import Food from "./Food";
function App() {
const getRandomFoodDot = () => {
let min = 1;
let max = 98;
let x = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
let y = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
return [x, y];
};
const [snakeDots, setSnakeDots] = useState([
[0, 0],
[2, 0],
]);
const [foodDots, setFoodDots] = useState(getRandomFoodDot());
const [direction, setDirection] = useState("");
const moveSnake = () => {
let dots = [...snakeDots];
let head = dots[dots.length - 1];
switch (direction) {
case "UP":
head = [head[0], head[1] - 2];
break;
case "DOWN":
head = [head[0], head[1] + 2];
break;
case "LEFT":
head = [head[0] - 2, head[1]];
break;
case "RIGHT":
head = [head[0] + 2, head[1]];
break;
default:
break;
}
// adding new head
dots.push(head);
// removing last dot
dots.shift();
setSnakeDots(dots);
};
moveSnake();
useEffect(() => {
const keyPressHandler = (e) => {
switch (e.keyCode) {
case 38:
setDirection("UP");
break;
case 40:
setDirection("DOWN");
break;
case 37:
setDirection("LEFT");
break;
case 39:
setDirection("RIGHT");
break;
default:
setDirection("");
break;
}
};
document.addEventListener("keydown", keyPressHandler);
return () => {
document.removeEventListener("keydown", keyPressHandler);
};
}, []);
console.info(direction);
return (
<div className = "app">
<div className = "snake">
<Snake snakeDots = {snakeDots} />
<Food foodDots = {foodDots} />
</div>
</div>
);
}
export default App;
Причина, по которой вы получаете эту ошибку, в которой говорится, что React ограничивает количество повторных рендеров до проверенных бесконечных повторных рендеров, заключается в том, что вы вызываете moveSnake() внутри вашего компонента App(), а moveSnake() вызывает setSnakeDots(), который обновляет состояние змеиных точек. Когда состояние змеиных точек обновляется, ваш компонент приложения перерисовывается, что вызывает вызов moveSnake(), который вызывает setSnakeDots(), который обновляет состояние ваших змеиных точек, что приводит к повторному рендерингу вашего компонента приложения. Вы застряли в inifinate петле здесь.
Что-то, что могло бы исправить это (хотя я тестировал это, и игра не проходит дальше одного шага), могло бы установить вашу функцию moveSnake() в таймере, например
function timer(){
setInterval(function() {
moveSnake();
timer()
}, 1000);
}
timer()
На самом деле, у меня была эта функция, потому что она нужна мне для "движения каждые 300 мс". Хорошего дня! Это был мой первый пост
Я не уверен, правильно ли я понял, но, вероятно, вы можете попробовать это:
import { useEffect, useState } from "react";
import "./App.css";
import Snake from "./Snake";
import Food from "./Food";
const getDirection = e => {
switch (e.keyCode) {
case 38:
return 'UP';
case 40:
return 'DOWN';
case 37:
return 'LEFT';
case 39:
return 'RIGHT';
default:
return '';
}
}
function App() {
const getRandomFoodDot = () => {
let min = 1;
let max = 98;
let x = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
let y = Math.floor((Math.random() * (max - min + 1) + min) / 2) * 2;
return [x, y];
};
const [snakeDots, setSnakeDots] = useState([
[0, 0],
[2, 0],
]);
const [foodDots, setFoodDots] = useState(getRandomFoodDot());
const [direction, setDirection] = useState({val: ''});
useEffect(() => {
let dots = [...snakeDots];
let head = dots[dots.length - 1];
switch (direction.val) {
case "UP":
head = [head[0], head[1] - 2];
break;
case "DOWN":
head = [head[0], head[1] + 2];
break;
case "LEFT":
head = [head[0] - 2, head[1]];
break;
case "RIGHT":
head = [head[0] + 2, head[1]];
break;
default:
break;
}
// adding new head
dots.push(head);
// removing last dot
dots.shift();
setSnakeDots(dots);
}, [direction]);
useEffect(() => {
const keyPressHandler = (e) => {
setDirection({ val: getDirection(e) });
};
document.addEventListener("keydown", keyPressHandler);
return () => {
document.removeEventListener("keydown", keyPressHandler);
};
}, []);
console.info(direction);
return (
<div className = "app">
<div className = "snake">
<Snake snakeDots = {snakeDots} />
<Food foodDots = {foodDots} />
</div>
</div>
);
}
export default App;
Я только что создал getDirection
функцию для получения направления от ключевого события и добавил еще одну useEffect
с логикой из функции moveSnake
Я не знал текущую функциональность вашего проекта, но приведенный выше код работает, и встряску можно двигать.
Это было здорово, просто добавили интервал в первый useEffect и условие для useEffect snakeDots.
Хорошо, мило. Всегда рад помочь ;)
Вы должны указать свою ошибку в вопросе