Вместо того, чтобы обработчик клавиатуры вызывал метод перемещения, который запускает свой собственный цикл таймера, у вас должна быть одна подпрограмма update , которая обновляет все для одного кадра анимации.Вы также должны максимально ускорить рендеринг, используя requestAnimationFrame
, и каждый рендер запрашивает следующий кадр анимации.(См. Пример по предоставленной ссылке.) Если вы хотите более медленную анимацию, вы можете выполнить поэтапное обновление сцены с помощью отдельного таймера.(Поверьте мне, однажды вы захотите анимацию с высокой частотой кадров, даже в пошаговой игре.)
Мне было скучно, поэтому я решил внести некоторые изменения в ваш код.
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
const length_width = 15;
let snakeCoord = [
{x:300,y:150},
{x:315,y:150},
{x:330,y:150},
{x:345,y:150},
{x:360,y:150},
{x:375,y:150}
];
let snake = {
dir: {dx: -1, dy: 0},
nextDir: [], // buffered direction changes
speed: 5, // steps per second
ratchet: 0
};
function drawSnakePart(snakePart) {
ctx.beginPath();
ctx.fillRect(snakePart.x, snakePart.y, length_width, length_width);
ctx.strokeRect(snakePart.x, snakePart.y, length_width, length_width);
ctx.closePath();
}
function drawSnake() {
snakeCoord.forEach(drawSnakePart);
}
function moveSnake() {
if (snake.nextDir[0]) {
// only change directions if it doesn't result in doubling back on yourself
if (snakeCoord[0].x + snake.nextDir[0].dx * length_width !== snakeCoord[1].x
&& snakeCoord[0].y + snake.nextDir[0].dy * length_width !== snakeCoord[1].y) {
snake.dir = snake.nextDir[0];
}
snake.nextDir.shift(1);
}
const head = {
x: snakeCoord[0].x + snake.dir.dx * length_width,
y: snakeCoord[0].y + snake.dir.dy * length_width
};
snakeCoord.unshift(head);
snakeCoord.pop();
}
function keyPress(e) {
let key = e.key;
if (key == "ArrowUp") {
setDirection(0,-1);
} else if (key == "ArrowDown") {
setDirection(0, 1);
} else if (key == "ArrowLeft") {
setDirection(-1, 0);
} else if (key == "ArrowRight") {
setDirection(1, 0);
}
e.preventDefault();
}
drawSnake();
let lastTime = new Date();
window.requestAnimationFrame(render);
function setDirection(dx, dy) {
snake.nextDir.push({dx, dy}); // overwrite any pending direction changes.
}
function update() {
let now = Date.now();
let elapsed = (now - lastTime) / 1000;
snake.ratchet += elapsed * snake.speed;
while (snake.ratchet >= 1) {
moveSnake();
snake.ratchet -= 1;
}
lastTime = now;
}
function render() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
update();
drawSnake();
window.requestAnimationFrame(render);
}
document.addEventListener("keydown", keyPress);
* {
overflow: hidden
}
<canvas width="500" height="500"></canvas>
Сделан этот цикл рендеринга с высокой частотой кадров.Использовал храповой механизм для частого перемещения змеи с определенной скоростью (см. Snake.speed).Добавлено свойство змеи, которое является ее направлением.(см. snake.dir).Буферизованные нажатия клавиш для запрошенных изменений направления (см. Snake.nextDir) Упрощенная логика предотвращения дублирования змеи на себя.Съешь одну смену направления за шаг.
Тебе все еще нужно сделать самоконтроль змей.(Предполагая, что это то, что вы делаете, с традиционной игрой в змею.)
В любом случае, я надеюсь, что это поможет вам или кому-то еще.