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
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 ownthis
(link to documentation).Just remove the
=
and the=>
, and your code should work (except there are other [scoping] issues in your code), sinceautoScratch
is a normal method now.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.
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.
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: