skip to Main Content

I am making a game, using angular and phaser. The issue is that for some reason when I try to call the autoScratch function from my class ** located in a different file then component ** nothing happens, the weirdest thing i know autoScratch works cause I tested it inside the create() and it does what it should. However when I call it in the component, autoScratch is executed. But nothing happens other then receiving the console.log message in my autoScratch, and runCard function.

main.component.ts

    import { Component, OnInit, ElementRef, HostListener, OnDestroy, AfterViewInit } from '@angular/core';
    import { Subject } from 'rxjs';
    import * as Phaser from 'phaser';
    // Import the class ScratchCard
    import { ScratchCard } from './../../../logic/game.logic';
    import { ApiService } from '../../../services/api.service';
    import { NgZone } from '@angular/core';

    @Component({
      selector: 'app-game',
      templateUrl: './game.component.html',
      styleUrls: ['./game.component.scss']
    })
    export class GameComponent implements OnInit, AfterViewInit, OnDestroy {
      private scratchCard!: any;
      public game!: Phaser.Game;
      public config!: Phaser.Types.Core.GameConfig;

      constructor(public apiService: ApiService, private ngZone: NgZone) { }

      public readonly gameConfig: Phaser.Types.Core.GameConfig = {
        type: Phaser.AUTO,
        width: 500,
        height: 500,
        parent: 'scratch-card-game',
        scene: [ScratchCard]
      };
      ngOnInit() {
        this.game = new Phaser.Game(this.gameConfig);
        console.log('game initialized');
      }

      ngAfterViewInit() {
        // Check to make sure game is ready to prevent the issue of calling runCard to early.
        this.game.events.once('ready', () => {
          this.game.scene.add('ScratchCard', new ScratchCard(this.apiService), true);
          //this.game.scene.add('Loading', new Loading(), true);
        });

        this.scratchCard = this.game.scene.getScene('ScratchCard');
        console.log('finalized')
      }

      public runCard() {
        if (this.scratchCard) {
          //Call autoScratch from class
          this.scratchCard.autoScratch(250, 5, 5);

          // Alternative approach did not work as well either:
          // this.scratchCard.onCardReveal = true;

        } else {
          console.log('no card');
        }
      }
      ngOnDestroy() {
        this.game.destroy(true); // Pass the required arguments to the destroy() method
      }
    }

The code below is the phaser game logic, I import the class as the scene, and it renders the code for that class, the game works. If I added all the logic, you should be able to scratch a card and reveal the assets / items below the surface, which currently is no assets at this time.

The weirdest part is autoScratch works fine, if I call it inside create using this.autoScratch(250, 5, 5); the output of it is what I need.

But if I call it within my component, firstly referencing the required class then the method, nothing happens other then printing the console.log(); inside the autoScratch function… The face the autoScratch function prints out a message, is proof that I am calling it correctly and it’s able to execute the function to an extent at least. My current best theory is that this is related to some weird phaser issue as phaser uses "this" keyword, a lot. However I’ve also tried doing this.scratchCard.autoScratch.bind(250, 5, 5) and that does not seem to work at all either too.

game.logic.ts

import * as Phaser from 'phaser';
import { ApiService } from '../services/api.service';
import { IAssetsData } from '../_models/GameSetup.model';

export class ScratchCard extends Phaser.Scene {
  background!: Phaser.GameObjects.Image;
  surface!: Phaser.GameObjects.Image;
  mask!: Phaser.Display.Masks.BitmapMask;
  constructor(public apiService: ApiService) {
    super({ key: 'scratchcard' });
  }
  preload() {
    this.load.image('surface', '../../assets/game1/images/wallpaper.png');
    this.load.image('background', '../../assets/game1/images/background.png');
      // this.apiService.gameSetup.images.forEach((image: IAssetsData) => {
      //   this.load.image(image.name, image.fileName);
      // });
  }

  public renderTexture!: Phaser.GameObjects.RenderTexture;
  public backgroundTexture!: Phaser.GameObjects.Image;
  public override scene: any;
  public canvasSize!: number;
  public brush: any;
  public lastPointerPosition: Phaser.Math.Vector2 | null = null;
  public onCardReveal: boolean = false;

  create() {
    console.log('game executed');
    this.scene = this.game.scene.scenes[0];
    this.canvasSize = this.scene.scale.width;


    const circleBrush = (scene: Phaser.Scene, radius: number, color: number): string => {
    // removed unrelated code...
    }

    this.background = this.scene.add.image(this.canvasSize / 2, this.canvasSize / 2, 'background');
    this.background.setDisplaySize(this.canvasSize, this.canvasSize);

    this.renderTexture = this.add.renderTexture(this.canvasSize / 2, this.canvasSize / 2, this.canvasSize, this.canvasSize);
    this.renderTexture.setOrigin(0.5, 0.5); // Set origin to center
    this.renderTexture.draw('surface', 0, 0); // Add additional parameters as needed
  

    this.brush = circleBrush(this.scene, this.canvasSize / 15, 0x000000);

    this.renderTexture.draw(0, 0);
    // Code works fine here.
    // this.autoScratch(250, 5, 5);
    this.scene.input.on('pointerdown', (pointer: any) => {
      this.renderTexture.erase(this.brush, pointer.x - 50, pointer.y - 50);
      this.lastPointerPosition = new Phaser.Math.Vector2(pointer.x, pointer.y);
    });

    this.scene.input.on('pointermove', (pointer: any) => {
      if (pointer.isDown) {
        this.renderTexture.erase(circleBrush, pointer.x - 20, pointer.y - 20);

        this.interpolatePoints(pointer, this.lastPointerPosition!, () => {
          this.renderTexture.erase(this.brush, pointer.x - 50, pointer.y - 50);
        });

        this.lastPointerPosition = new Phaser.Math.Vector2(pointer.x, pointer.y);
      }
    });
  }

  override update() {

    if (this.onCardReveal) {
      console.log('edit');
      this.autoScratch(this.canvasSize / 2, 0.25, 0.45);
    }
    // Update game logic here
  }

  // Here is the function in question.
  public autoScratch = (amplitude: number, frequency: number, speed: number) => {
    let time = 0;

    const canvasWidth = this.game.canvas.width;
    const canvasHeight = this.game.canvas.height;
    const scratchInterval = setInterval(() => {
      time += speed;
      const pointerX = this.canvasSize / 2 + Math.sin(Math.cos(time * frequency)) * amplitude;//(Math.LOG10E * (time * frequency * 2)) * amplitude;
      const pointerY = 5;
      this.interpolatePoints({ x: pointerX, y: pointerY }, this.lastPointerPosition, () => {
        this.renderTexture.erase(this.brush, pointerX - 50, pointerY - 50);
      });
    }, 16); // 60 FPS

    setTimeout(() => {
      clearInterval(scratchInterval);
    }, 25555);
  };

  public interpolatePoints = (pointer: any, lastPointerPosition: Phaser.Math.Vector2 | null, callback: () => void) => {
// removed unrelated code.

}

I tried debugging and adding console.logs, I tried adding bind. to the call in the component, I tried calling the function via the update loop instead setting a flag condition to ensure it was executed when needed, but none of this worked as necessary.

I was expecting to have autoScratch to scratch the board ** which is does **, and be called when I click a button in the component which called runCard, ** the button works **, but the call is not properly running the code. Even when I tried all of the previous stuff. It should, call the autoScratch method from the component, and proceed to scratch the board automatically with a wave.

2

Answers


  1. I not 100% precent sure how "typescript" is transpiled/compiled into javascript, but I assume your issue has to do, with the fact, that you declared the function/method autoScratch, you are using an arrow function (lambda function), such functions don’t have there own this (link to documentation).

    Just remove the = and the =>, and your code should work (except there are other [scoping] issues in your code), since autoScratch is a normal method now.

    public autoScratch(amplitude: number, frequency: number, speed: number) {
      let time = 0;
    
      const canvasWidth = this.game.canvas.width;
      const canvasHeight = this.game.canvas.height;
      const scratchInterval = setInterval(() => {
        time += speed;
        const pointerX = this.canvasSize / 2 + Math.sin(Math.cos(time * frequency)) * amplitude;//(Math.LOG10E * (time * frequency * 2)) * amplitude;
        const pointerY = 5;
        this.interpolatePoints({ x: pointerX, y: pointerY }, this.lastPointerPosition, () => {
          this.renderTexture.erase(this.brush, pointerX - 50, pointerY - 50);
        });
      }, 16); // 60 FPS
    
      setTimeout(() => {
        clearInterval(scratchInterval);
      }, 25555);
    };
    

    just for completeness: It works inside the class, since the function has there access to the specific this, in the other classes it would have access to other this objects, which causes the error.

    P.s.: I personally would refrain from using arrow functions (in javascript / typescript), unless absolutely needed, since they are more than syntactic sugar unlike some other programming languages, and scope might behave unexpected.

    Login or Signup to reply.
  2. I’m not sure how you integrate Phaser into Angular, there may be better ways to do it. But one thing you can try is to create the reference to the scratchcard in the component, and use that reference directly.

    ngAfterViewInit() {
            this.myCard = new ScratchCard(this.apiService)
    
            this.game.events.once('ready', () => {
              this.game.scene.add('ScratchCard', this.myCard, true);
            });
        
            // test if you can call functions on your phaser class
            this.myCard.autoScratch(250, 5, 5);
          }
    

    And like the other answer says, there is some confusion about where you need => functions and where you don’t. I would remove it here:

    export class ScratchCard extends Phaser.Scene {
         public autoScratch(amplitude: number, frequency: number, speed: number) {
            let time = 0;
         }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search