0

I'm relatively new to Javascript, so please bear with me if this is a rather trivial problem.

I'm trying to make a game where I can control a player with the arrow keys. A simplified version of the code (which doesn't work) looks like the following:

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();
};

So, basically, I'd like to control gamePlayer.X with the left and right arrow keys. However, with the above code, whenever I press an arrow key, I get the following error message:

Uncaught TypeError: Cannot read property 'X' of undefined at playerMovement

So my question is, why can't it read this.gamePlayer.X inside the function playerMovement? How should I change the code to make it work?

Henry
  • 103
  • 2
  • 1
    Does this answer your question? [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – Reyno May 17 '21 at 06:55
  • Are you coding a tic-tac-toe? – Krutik Raut May 17 '21 at 07:25

1 Answers1

1

You only need to register the event listener once, can't hurt to do so in the constructor. You seem to know this already as you're doing it elsewhere, but the reason you're getting an error is the scope changed when you used window.addEventListener("keydown", this.playerMovement); instead of window.addEventListener("keydown", (e) => this.playerMovement(e));. when you pass the function directly the scope becomes that of the caller - in this case window. The lambda maintains the scope.

Also I'm not sure what that setTimeout is doing in gameloop, I suspect it will cause you problems later.

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();
};
  • 2
    `You only need to register the event listener once` while this is true, you didn’t explain what else you changed and why to get rid of the error message. – t.niese May 17 '21 at 06:56
  • @t.niese good point, will add more explanation. –  May 17 '21 at 06:57