RabbitB

Phaser CE 2.9.4 - Issue calling methods within update()

Recommended Posts

I'm looking to use Phaser to develop small embedded, interactive examples for game-dev articles I write on my website. I'm writing in Typescript, and so far I've gotten a basic program put together and running based on various online tutorials. I did however run into an issue that I managed to resolve myself, but being a traditional desktop developer that has minimal experience in either JS or Typescript, I'm not sure exactly what's going on, and I'm hoping someone can give some insight on why this is happening, and if there's a better solution.

The issue I was running into, is that when calling methods from within my update callback, I kept getting `no method 'x' found` errors. The solution I found was adding the methods I wanted to call, to the `state` dictionary I was passing to the game engine. I'm sorry for the code dump, but I'm trying to demonstrate exactly what I ran into.

class BaseGame
{
	game:Phaser.Game;
	spaceKey:Phaser.Key;

	mainState = {
		preload: this.preload, 
		create: this.create, 
		update: this.update,
		doStuff: this.doStuff,		// These two required to be here if they're to be used
		restartGame: this.restartGame	// as callbacks or within other callbacks, like update()
	};

	constructor()
	{
		this.game = new Phaser.Game(400, 490, Phaser.AUTO, 'content');
		this.game.state.add('main', this.mainState);
		this.game.state.start('main');
	}

	preload(): void
	{
		// ... load assets here
	}

	create(): void
	{
		// ... do game setup here

		// If this.doStuff isn't present in the mainState dictionary, then nothing ever happens, as the method can't be found by Phaser.
		this.spaceKey = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
		this.spaceKey.onDown.add(this.doStuff, this);
	}

	update(): void
	{
		if (...)	// Conditions that require restarting
		{
			// If this.restartGame isn't present in the mainState dictionary, then we can't use it here; it'll return an error of `method not found`
			this.restartGame();
		}
	}

	doStuff(): void
	{
		// ... do whatever we need to do when SPACE is pressed.
	}

	restartGame(): void
	{
		this.game.state.restart();
	}
}

window.onload = () => {
	var game = new BaseGame();
}

As stated, the solution here was just to add the methods I want to call to the `state` dictionary I pass to the engine, but unfortunately I don't understand exactly what's going on here and why. Obviously the function references don't contain their full scope (the class they belong to), so when they are called, they're not found. Yet adding them to the `state` dictionary keeps that scope intact somehow, and allows me to continue to use instance variables like normal. I think that's where I'm really getting lost here.

Hopefully someone much more experienced with JS/Typescript can chime in and shed some light.

 Basically all my experience in JS and Typescript is from writing plugins for VS Code and doing minimal work for my website. Mostly either adapting 3rd party utilities to work with the CMS for my website (such as adding source-code highlighting through prism.js), or adding custom functionality myself, to say mimic extending the available markdown syntax. A good example is how embedded video (webm) has no markdown syntax in the CMS I use, so I wrote a function that runs on pageload, that looks for a custom string and a following url in the content of the page, and properly embeds the video found at the url, effectively extending the markdown syntax instead of requiring me to write the required html in every article I wish to embed something.

Share this post


Link to post
Share on other sites

This is because Phaser sets the context (this) to the current state within all of the state callbacks (init, preload, create, update, …). So you can't reach the BaseClass instance as this from there.

You could save a reference in the constructor, like

this.game.baseGame = this;

and then use it.

Or it may be simpler to write a State class (instead of BaseGame) and add your methods there.

Share this post


Link to post
Share on other sites

Thank you; I'll likely end up splitting responsibilities and create a state class that does the actual work, and keep BaseGame as just the constructor that essentially assembles the pieces to get it all running.

Share this post


Link to post
Share on other sites

Maybe do it like this:

class BaseGame extends Phaser.Game {
	constructor(width?: number | string, height?: number | string, renderer?: number, parent?: any) {
		super(width, height, renderer, parent);
		this.state.add("Main", MainState, true);
	}
}

class MainState extends Phaser.State {
	preload(): void {
	}

	create(): void {
	}

	update(): void {
	}

	doStuff(): void {
	}
}

window.onload = () => {
	let game = new BaseGame(400, 490, Phaser.AUTO, 'content');
}

 

This way you won't have to hold a separate reference to the instance of Phaser.Game (BaseGame itself will be it). In the same line of thought MainState extends Phaser.State, so you don't have to explicitly define an object like you did. Of course, split this into files (each class in its own, plus the window.onload in a third one, maybe App.ts depending on your project structure).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.