JavaScript中Canvas上平滑的按键动画

3

我是一名编程新手,正在尝试创建一些代码,以便通过按箭头键来移动画布上的一个正方形。我已经成功让这个正方形运动了,但它的运动不够流畅。我让它每次移动10像素,所以我明白为什么它感觉有些卡顿,因为在每个10帧差异的位置之间没有任何动画,但让它每次移动更小的增量会使它过于缓慢。我已完成的部分如下:

window.onload = function init() {
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    setInterval(gameLoop,50);
    window.addEventListener('keydown',whatKey,true);
}

avatarX = 400
avatarY = 300

function gameLoop() {
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");
    canvas.width = 800
    canvas.height = 600
    ctx.fillRect(avatarX,avatarY,50,50);
}

function whatKey(e) {
    switch(e.keyCode) {
    case 37:
        avatarX = avatarX - 10;
        break;
    case 39:
        avatarX = avatarX + 10;
        break;
    case 40:
        avatarY = avatarY + 10;
        break;
    case 38:
        avatarY = avatarY - 10;
        break;
    }
}

每次我按向右的箭头键时,我希望正方形以恒定速率在该方向上平滑移动。非常感谢您提前的任何帮助!
2个回答

14

添加了一些东西,第一个是 requestAnimationFrame这里解释了原因

然后我添加了 keyupkeydown 事件处理程序,它们将使用数组跟踪当前正在按下哪些键。 如果在数组中键为 true,则表示当前正在按下该键,如果为 false,则没有按下该键。 这种方法还允许您同时按住多个键。

然后我添加了速度变量,这些变量根据被按下的键增加或减少,并添加了一个 maxSpeed 变量,以便您不会超过某个速度。 可以删除 maxSpeed 变量和递增和递减的 velXvelY,只需要取消我留下的行的注释即可。 它只是在 10 时似乎太快了,所以我添加了渐进加速。

在线演示

由于画布有点苦涩,上下箭头会滚动帧,因此以上内容看起来很卡顿,使用全屏链接来完全测试移动。

全屏链接

(function () {
  var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
  window.requestAnimationFrame = requestAnimationFrame;
})();


window.onload = function init() {
  var canvas = document.getElementById("canvas");
  var ctx = canvas.getContext("2d");
  gameLoop();
}

window.addEventListener("keydown", function (e) {
  keys[e.keyCode] = true;
});
window.addEventListener("keyup", function (e) {
  keys[e.keyCode] = false;
});


var avatarX = 400,
  avatarY = 300,
  velX = 0,
  velY = 0,
  keys = [],
  maxSpeed = 10;

function gameLoop() {
  whatKey();
  var canvas = document.getElementById("canvas");
  var ctx = canvas.getContext("2d");
  canvas.width = 800;
  canvas.height = 600;

  avatarX += velX;
  avatarY += velY;

  ctx.fillRect(avatarX, avatarY, 50, 50);
  requestAnimationFrame(gameLoop);
}

function whatKey() {
  if (keys[37]) {
    //velX = -10;
    if (velX > -maxSpeed) {
      velX -= 0.5;
    }
  }

  if (keys[39]) {
    //velX = 10;
    if (velX < maxSpeed) {
      velX += 0.5;
    }
  }
  if (keys[40]) {
    //velY = 10;
    if (velY < maxSpeed) {
      velY += 0.5;
    }
  }
  if (keys[38]) {
    //velY = -10;
    if (velY > -maxSpeed) {
      velY -= 0.5;
    }
  }
}

@LeeTaylor 哈哈,我也是这么想的,花了一点时间才弄明白发生了什么,因为画布比框架大一点,可以查看全屏链接http://jsfiddle.net/loktar/DEaja/embedded/result/。 - Loktar
1
谢谢你的出色回答!我把我的React重构作为答案发布,以防其他人也会遇到这个问题。 - MonsterBasket

1

非常感谢@Loktar在十年前回答了这个问题。我目前正在使用React制作游戏,这个答案仍然适用。以下是我的React适配版本,供将来遇到此问题的人参考!

import { useEffect, useState } from "react";

function Player(){
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);
  let keys = [];
  let velocity = [0,0];
  const maxSpeed = 3;

  useEffect(() => {
    window.addEventListener("keydown", keyDown);
    window.addEventListener("keyup", keyUp );
    gameLoop();
    return () => {
      window.removeEventListener("keydown", keyDown);
      window.removeEventListener("keyup", keyUp);
    };
  }, [])

  function keyDown(e){
    keys[e.code] = true;
  }
  function keyUp(e){
    keys[e.code] = false;
  }

  function handleInput(){
    if(keys["KeyA"] && velocity[0] > -maxSpeed) velocity[0] -= 0.15;
    if(keys["KeyD"] && velocity[0] <  maxSpeed) velocity[0] += 0.15;
    if(keys["KeyS"] && velocity[1] <  maxSpeed) velocity[1] += 0.15;
    if(keys["KeyW"] && velocity[1] > -maxSpeed) velocity[1] -= 0.15;
    if(!keys["KeyA"] && !keys["KeyD"]){
      if((velocity[0] < 0.1 && velocity[0] > 0) || (velocity[0] > -0.1 && velocity[0] < 0)) velocity[0] = 0;
      if(velocity[0] < 0) velocity[0] += 0.1;
      if(velocity[0] > 0) velocity[0] -= 0.1;
    } 
    if(!keys["KeyW"] && !keys["KeyS"]){
      if((velocity[1] < 0.1 && velocity[1] > 0) || (velocity[1] > -0.1 && velocity[1] < 0)) velocity[1] = 0;
      if(velocity[1] < 0) velocity[1] += 0.1;
      if(velocity[1] > 0) velocity[1] -= 0.1;
    } 
  }

  function gameLoop() {
    handleInput();
    if(velocity[0]) setX(prev => prev + velocity[0])
    if(velocity[1]) setY(prev => prev + velocity[1])
    requestAnimationFrame(gameLoop);
  }

  const characterMover = {
    transform: `translate(${x}px, ${y}px)`
  }

  return <div id="player" style={characterMover}></div>
}

export default Player;

我仍然没有实现精灵动画,但是我保留了稍后调整的权利,这对我来说是一个很好的起点!


1
哦,你让我感觉好老啊 :) 但很高兴我的回答对你有帮助!! - Loktar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接