When I started building a mini game using Phaser within an Ionic/Angular app, things were going pretty well until I tried to access an external variable from my Angular service (GlobalvarProvider
) inside the Phaser lifecycle methods like preload()
. That’s when I ran into a scoping nightmare.
Let me walk you through what went wrong, how I fixed it, and what I learned along the way.
The Problem this
Isn’t What You Think It Is
In JavaScript (and TypeScript), this
can be tricky. Inside my GamePage
class, I had access to the Angular service just fine for example:
this.heightscore = this.gvp.highscore;
But the moment I tried to access this.gvp
inside preload()
, it failed. No errors at compile-time, but the variable was undefined
. The issue? Phaser changes the this
context when calling preload
, create
, and update
. So, this
in those functions no longer refers to my Angular component, but instead to Phaser’s internal object.
Here’s the part of my code that caused the issue:
this.game = new Phaser.Game(this.width, this.height, Phaser.CANVAS, 'phaser-example', {
preload: this.preload,
create: this.create,
update: this.update
});
I passed the methods directly, and they got unbound, so this
was lost.
The Fix Use Arrow Function
Arrow functions are my new best friend. Why? Because they preserve the outer this
context. That means if I wrap my preload
, create
, and update
in arrow functions when initializing the Phaser game, everything works as expected.
The Final Working Code
Here’s the updated working version of my GamePage
class with bonus practice functionality like loading an image and showing scores:
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams, Platform } from 'ionic-angular';
import "pixi";
import "p2";
import * as Phaser from "phaser-ce";
import { GlobalvarProvider } from '../../providers/globalvar/globalvar';
@IonicPage()
@Component({
selector: 'page-game',
templateUrl: 'game.html',
})
export class GamePage {
width: any = 0;
height: any = 0;
game: Phaser.Game;
heightscore: any;
totalscore: number = 0;
constructor(
public navCtrl: NavController,
platform: Platform,
public gvp: GlobalvarProvider
) {
this.heightscore = this.gvp.highscore;
this.width = platform.width();
this.height = platform.height();
// Use arrow functions to preserve `this`
this.game = new Phaser.Game(this.width, this.height, Phaser.CANVAS, 'phaser-example', {
preload: () => this.preload(),
create: () => this.create(),
update: () => this.update()
});
}
preload() {
// Now this.gvp works!
this.heightscore = this.gvp.highscore;
console.log("Loaded highscore from provider: ", this.heightscore);
// Load a dummy asset
this.game.load.image('logo', 'assets/imgs/logo.png');
}
create() {
// Display the high score
this.game.add.text(50, 50, 'High Score: ' + this.heightscore, {
font: '24px Arial',
fill: '#ffffff'
});
// Display the current score
this.game.add.text(50, 100, 'Your Score: ' + this.totalscore, {
font: '24px Arial',
fill: '#00ff00'
});
// Show image
this.game.add.sprite(100, 200, 'logo');
}
update() {
// Simple score incrementation (for demo)
this.totalscore += 1;
}
}
What I Added for Practice
- Loaded an image (
logo.png
) - Displayed the high score from Angular service
- Showed a dynamic live score
- Used
console.log
to verify the provider is accessed - Preserved
this
context using arrow functions
Final Thoughts
This issue reminded me how important understanding JavaScript’s this
really is especially when combining frameworks like Angular with game engines like Phaser. Using arrow functions made the difference between broken and fully functional. Now I can use values from Angular services (like high scores or settings) directly inside my Phaser game loop. It’s a small fix that unlocked a lot of flexibility. If you’re hitting the same wall, try arrow functions and never underestimate the scope of this
.