我正在制作一个网络游戏(不是用webGL,而是用html元素),我需要用WaSD键移动角色.

  1. 使用事件侦听器keydown和keydup.

  2. 使用设置间隔(20ms)和事件侦听器.

keyDict = {}
document.addEventListener('keydown', key => keyDict[key.code] = true);
document.addEventListener('keyup', key => keyDict[key.code] = false);

moveID = setInterval(move, 20)
function move()
{
   if(!finished)
   {
      newDirs = [0,0]
      //Left
      if(keyDict.ArrowLeft == true) 
      {
         newDirs[0] -= 1;
      }
      //Right
      if(keyDict.ArrowRight == true) 
      {
         newDirs[0] += 1;
      }
      //Up
      if(keyDict.ArrowUp == true) 
      {
         newDirs[1] -= 1;
      }
      //Down
      if(keyDict.ArrowDown == true) 
      {
         newDirs[1] += 1;
      }
      map.updateDir(newDirs); 
   }
}
  1. 使用requestAnimationFrame和事件侦听器.
keyDict = {}
document.addEventListener('keydown', key => keyDict[key.code] = true);
document.addEventListener('keyup', key => keyDict[key.code] = false);

requestAnimationsFrame(move)
function move()
{
    same code...
    requestAnimationFrame(move)
}

我想让它react 灵敏,非常流畅,我知道有办法,但不知道怎么做.例如鼠标,你的笔记本电脑不会因为滚动而工作(例如,用鼠标移动谷歌 map 是平滑的,并且不需要太多cpu).

推荐答案

Don't use the 20ms interval.
Move the player inside the requestAnimationFrame depending on which key Event.code is pressed and holds a truthy value inside of your keyDict object:

const keyDict = {};
const Player = {
  el: document.querySelector("#player"),
  x: 200,
  y: 100,
  speed: 2,
  move() {
    this.el.style.transform = `translate(${this.x}px, ${this.y}px)`;
  }
};

const updateKeyDict = (ev) => {
    const k = ev.code;
    if (/^Arrow\w+/.test(k)) { // If is arrow
      ev.preventDefault();
      keyDict[k] = ev.type === "keydown"; // set boolean true / false
    }
};

const update = () => {
  // Determine move distance to account diagonal move: 1/Math.sqrt(2) = ~0.707
  let dist =
    keyDict.ArrowUp   && (keyDict.ArrowLeft || keyDict.ArrowRight) ||
    keyDict.ArrowDown && (keyDict.ArrowLeft || keyDict.ArrowRight) ? 0.707 : 1;
    
  dist *= Player.speed;
  
  if (keyDict.ArrowLeft)  Player.x -= dist;
  if (keyDict.ArrowUp)    Player.y -= dist;
  if (keyDict.ArrowRight) Player.x += dist;
  if (keyDict.ArrowDown)  Player.y += dist;
  Player.move();
}

document.addEventListener('keydown', updateKeyDict);
document.addEventListener('keyup', updateKeyDict);

(function engine() {
  update();
  window.requestAnimationFrame(engine);
}());
#player {
  position: absolute;
  left: 0;
  top: 0;
  width: 20px;
  height: 20px;
  background: #000;
  border-radius: 50%;
}
Click here to focus, and use arrows
<div id="player"></div>

上面的示例使用Event.code表示Arrows,这表示"ArrowLeft/Up/Right/Down",但您可以相应地将其更改为使用"KeyW/A/S/D".

"WASD"键示例

const keyDict = {};
const Player = {
  el: document.querySelector("#player"),
  x: 200,
  y: 100,
  speed: 2,
  move() {
    this.el.style.transform = `translate(${this.x}px, ${this.y}px)`;
  }
};

const updateKeyDict = (ev) => {
  const k = ev.code;
  if (/^Key[WASD]/.test(k)) { // If is "KeyW,A,S,D" key
    ev.preventDefault();
    keyDict[k] = ev.type === "keydown"; // set boolean true / false
  }
};

const update = () => {
  // Determine move distance to account diagonal move: 1/Math.sqrt(2) = ~0.707
  let dist =
    keyDict.KeyW && (keyDict.KeyA || keyDict.KeyD) ||
    keyDict.KeyS && (keyDict.KeyA || keyDict.KeyD) ? 0.707 : 1;

  dist *= Player.speed;

  if (keyDict.KeyA) Player.x -= dist;
  if (keyDict.KeyW) Player.y -= dist;
  if (keyDict.KeyD) Player.x += dist;
  if (keyDict.KeyS) Player.y += dist;
  Player.move();
}

document.addEventListener('keydown', updateKeyDict);
document.addEventListener('keyup', updateKeyDict);

(function engine() {
  update();
  window.requestAnimationFrame(engine);
}());
#player {
  position: absolute;
  left: 0;
  top: 0;
  width: 20px;
  height: 20px;
  background: #000;
  border-radius: 50%;
}
Click here to focus, and use keys WASD
<div id="player"></div>

Javascript相关问答推荐

通过使用100%间隔时间来代表我们还剩多少时间来倒计时

如何将连续的十六进制字符串拆分为以空间分隔的十六进制块,每个十六进制块包含32个二元组?

我们如何从一个行动中分派行动

Mongoose post hook在使用await保存时不返回Postman响应

如何让npx在windows中运行js脚本?

引用在HTMLAttributes<;HTMLDivElement>;中不可用

Angular 形式,从DOM中删除不会删除指定索引处的内容,但会删除最后一项

更新Redux存储中的对象数组

使每个<;li>;元素的 colored颜色 与随机生成的 colored颜色 列表不同(不重复

基于产品ID更新条带产品图像的JavaScript命中错误

在不扭曲纹理的情况下在顶点着色器中旋转UV

React:防止useContext重新渲染整个应用程序或在组件之间共享数据而不重新渲染所有组件

重新呈现-react -筛选数据过多

JAVASCRIPT SWITCH CASE语句:当表达式为';ALL';

JavaScript:多个图像错误处理程序

重新渲染过多(&Q).REACT限制渲染次数以防止无限循环.使用REACT下拉菜单时

Refine.dev从不同的表取多条记录

Reaction路由v6.4+数据API:重试加载程序而不显示错误

ReactJS Sweep Line:优化SciChartJS性能,重用wasmContext进行多图表渲染

使用重新 Select 和对象理解 Select 器备忘