如何在游戏循环中使用 addEventListener

Posted

技术标签:

【中文标题】如何在游戏循环中使用 addEventListener【英文标题】:How to use addEventListener in a game loop 【发布时间】:2021-05-17 06:46:57 【问题描述】:

我对 javascript 比较陌生,所以如果这是一个相当琐碎的问题,请多多包涵。

我正在尝试制作一款可以用箭头键控制玩家的游戏。代码的简化版本(不起作用)如下所示:

class Player 
  constructor(X) 
    this.X = X;
  


class Game 
  constructor() 
    this.gamePlayer = new Player(0);
    window.requestAnimationFrame(() => this.gameLoop());
  

  playerMovement(event) 
    switch (event.code) 
      case "ArrowLeft":
        console.log(this.gamePlayer.X);
        this.gamePlayer.X -= 1;
        break;
      case "ArrowRight":
        console.log(this.gamePlayer.X);
        this.gamePlayer.X += 1;
        break;
    
  

  gameLoop() 
    window.addEventListener("keydown", this.playerMovement);

    setTimeout(() => 
      window.requestAnimationFrame(() => this.gameLoop());
    , 50);
  


window.onload = () => 
  let game = new Game();
;

所以,基本上,我想用左右箭头键来控制 gamePlayer.X。但是,使用上面的代码,每当我按箭头键时,都会收到以下错误消息:

未捕获的类型错误:无法读取 playerMovement 处未定义的属性“X”

所以我的问题是,为什么它不能读取函数 playerMovement 中的 this.gamePlayer.X?我应该如何更改代码以使其正常工作?

【问题讨论】:

这能回答你的问题吗? How to access the correct `this` inside a callback? 你在编写井字游戏吗? 【参考方案1】:

您只需要注册一次事件监听器,在构造函数中这样做不会有什么坏处。当您在其他地方执行此操作时,您似乎已经知道这一点,但是您收到错误的原因是当您使用 window.addEventListener("keydown", this.playerMovement); 而不是 window.addEventListener("keydown", (e) => this.playerMovement(e)); 时范围发生了变化。当你直接传递函数时,作用域就变成了调用者的作用域——在这种情况下是window。 lambda 维护范围。

另外我不确定setTimeoutgameloop 中的作用,我怀疑它以后会给你带来麻烦。

class Player 
  constructor(X) 
    this.X = X;
  


class Game 
  constructor() 
    this.gamePlayer = new Player(0);
    window.requestAnimationFrame(() => this.gameLoop());
    window.addEventListener("keydown", (e) => this.playerMovement(e));
  

  playerMovement(event) 
    switch (event.code) 
      case "ArrowLeft":
        console.log(this.gamePlayer.X);
        this.gamePlayer.X -= 1;
        break;
      case "ArrowRight":
        console.log(this.gamePlayer.X);
        this.gamePlayer.X += 1;
        break;
    
  

  gameLoop() 
    

    window.requestAnimationFrame(() => this.gameLoop());
  


window.onload = () => 
  let game = new Game();
;

【讨论】:

You only need to register the event listener once 虽然这是真的,但您没有解释您还更改了什么以及为什么要删除错误消息。 @t.niese 好点,将添加更多解释。

以上是关于如何在游戏循环中使用 addEventListener的主要内容,如果未能解决你的问题,请参考以下文章

如何在游戏循环中使用 repaint() 方法

js中for循环中的addEventListener

在 for 循环中动态创建的按钮上的 addEventListener 问题

如何在opengl游戏循环中使用双核?

我如何将游戏循环放入我的代码中?

如何在纯 Haskell 中编写简单的实时游戏循环?