1

Whenever I call moveAvatar(), this.vx and this.vy are are undefined.

How?

How it works in plain JS application:

Game starts: set an interval that calls draw() over and over, drawing on the canvas. I'm perplexed as to why this wouldn't work using typescript.

vx and vy are both defined, and assigned values. You can even print them out...

I literally ported this code from a pure JS application that works fine.

I have a feeling it has something to do with how angular treats intervals. Please let me know!! Thanks!

import {
  Component,
  OnInit
} from '@angular/core';

@Component({
  selector: 'app-avoid',
  templateUrl: './avoid.component.html',
  styleUrls: ['./avoid.component.css']
})
export class AvoidComponent implements OnInit {
  avatarImage;
  enemy;
  x = 30;
  y = 30;
  _x = 300;
  _y = 300;
  score = 0;
  vx: number = 0;
  vy: number = 0;
  scoreTracker;
  gameCanvas;
  frames;
  gameStatus = false;
  hud = {
    score: 0,
    left: 0,
    right: 0,
    up: 0,
    down: 0
  };
  backstage;
  time = 0;
  level = 1;
  enemySpeed = 0.5;
  levelTracker;

  constructor() {
  }

  ngOnInit() {
    let body = document.querySelector('body');
    body.addEventListener("keyup", this.moveAvatar);
    this.gameCanvas = document.getElementById("gameCanvas");
    this.scoreTracker = document.getElementById("score");
    this.levelTracker = document.getElementById('level');
    this.enemy = this.getEnemy();
    this.avatarImage = this.getAvatar();
  }

  levelUp() {
    this.level++;
    this.enemySpeed += 0.25;
    this.levelTracker.innerText = this.level.toString();
  }

  startCanvas() {
    this.backstage = document.createElement("canvas");
    this.backstage.width = this.gameCanvas.width;
    this.backstage.height = this.gameCanvas.height;
    if (this.frames != null) {
      this.gameOver();
    }
    this.backstage.getContext("2d", {
      alpha: false
    }).drawImage(this.avatarImage, this.x, this.y);
    this.backstage.getContext("2d", {
      alpha: false
    }).drawImage(this.enemy, this._x, this._y)
    this.gameCanvas.getContext("2d", {
      alpha: false
    }).drawImage(this.backstage, 0, 0);

    this.time = 0;

    const that = this;
    this.frames = setInterval(() => {
      that.Draw();
    }, 100);
  }

  getAvatar() {
    var avatarImage = new Image(30, 30);
    avatarImage.src = "../assets/images/avatar.png";
    return avatarImage;
  }

  getEnemy() {
    var enemy = new Image();
    enemy.src = "../assets/images/enemy.png"
    return enemy;
  }

  moveAvatar(key) {
    switch (key.keyCode) {
      case 37:
        this.vx--;
        break;
      case 38:
        this.vy--;
        break;
      case 39:
        this.vx++;
        break;
      case 40:
        this.vy++;
        break;
    }
  }

  Follow() {
    if (this.x > this._x) {
      this._x += this.enemySpeed;
    } else if (this.x < this._x) {
      this._x -= this.enemySpeed;
    }

    if (this.y > this._y) {
      this._y += this.enemySpeed;
    } else if (this.y < this._y) {
      this._y -= this.enemySpeed;
    }
  }

  Draw() {
    this.time++;

    this.Accelerate();
    this.Follow();

    if (this.x < 0 || this.y < 0 || this.y > 520 || this.x > 770) {
      this.gameOver();
      return;
    }

    this.score += 1;
    this.scoreTracker.value = this.score;
    this.backstage.width += 0;
    this.gameCanvas.width += 0;
    this.backstage.getContext("2d", {
      alpha: false
    }).drawImage(this.avatarImage, this.x, this.y);

    this.backstage.getContext("2d", {
      alpha: false
    }).drawImage(this.enemy, this._x, this._y);
    this.gameCanvas.getContext("2d", {
      alpha: false
    }).drawImage(this.backstage, 0, 0);
    if (this.x <= this._x + 20 && this.x >= this._x - 20) {
      if (this.y <= this._y + 20 && this.y >= this._y - 20) {
        this.gameOver();
      }
    }
    if (this.score % 500 === 0) {
      this.levelUp();
    }
  }

  Accelerate() {
    this.x += this.vx;
    this.y += this.vy;
  }

  gameOver() {
    clearInterval(this.frames);
    this.x = 30;
    this.y = 30;
    this._x = 300;
    this._y = 300;
    this.score = 0;
    this.scoreTracker.value = 0;
    this.level = 1;
    this.enemySpeed = 0.5;
    this.levelTracker.innerText = "1";
  }
}
Oram
  • 1,589
  • 2
  • 16
  • 22
Larry
  • 365
  • 4
  • 12
  • that.Draw(); is this correct? y are using arrow function and you don't have to assign this to that – Sheik Althaf Mar 19 '19 at 07:10
  • typescript has nothing to do with it not working. typescript is a superset of javascript and as such, whatever works with javascript will work with typescript. – Oram Mar 19 '19 at 07:18
  • @SheikAlthaf, Well it works, but it had to do with me not fulling understanding "this" inside of the method of moveAvatar() which was binding to the method rather than the component's class. It was purely for experimentation. – Larry Mar 19 '19 at 07:32
  • @Oram, True, however ES6 syntax had everything to do with why I had a problem with "this" keyword. I should have clarified that I my code was binding to the incorrect "this". Oh well, relearned something I thought I had committed to memory at the expense of 2 hours of my sleep. Hope this serves its purpose in helping someone else. – Larry Mar 19 '19 at 15:56

2 Answers2

3

call it like this

body.addEventListener('keyup', (ev) => this.moveAvatar(ev));

(OR)

body.addEventListener('keyup', this.moveAvatar.bind(this));
Sheik Althaf
  • 1,595
  • 10
  • 16
  • Yeah that worked. I quickly realized after I posted this that it was calling the wrong "this". Why does arrow syntax fix this? – Larry Mar 19 '19 at 07:18
  • 1
    I think this question will help you understand: https://stackoverflow.com/a/34361380/9839191 – Oram Mar 19 '19 at 07:21
  • @Oram Perfect. Thank makes sense. Didn't realize that arrow functions don't get their own this binding. – Larry Mar 19 '19 at 07:26
1

Alternative to above correct answer, you can also use Renderer module to achieve the same. this.renderer.listen('keyup', target, callbackfn); Instead of manually adding the listeners, you let angular create and destroy the listeners.

KiraAG
  • 773
  • 4
  • 19