nkholski

Extending Phaser.GameObjects.Sprite (ES6)

Recommended Posts

I decided to take Phaser 3 for a spin but hit a wall I can't find my way around. The update-method is ignored when I extend Phaser.GameObjects.Sprite (when I cheat and call it from the scene update method everything works as expected).

I import this to the scene script:

import 'phaser';

export default class extends Phaser.GameObjects.Sprite {
  constructor (config) {
    super(config.scene, config.x, config.y , config.key);
    this.scene = config.scene;
    this.scene.add.existing(this);
  }
  update ()
  {
    this.angle++
  }
}

And I create an instance like this in the create method of the scene:

this.sprite = new mySprite({
  scene: this,
  key: 'sprite',
  x: this.game.config.width/2,
  y: this.game.config.height/2-150
})

It shows up and there are no errors in the console, but it is not spinning as expected.

Any ideas?

Share this post


Link to post
Share on other sites

Hiya,

This is happening because update isn't called automatically by the Game Loop anymore (for any Game Object, not just Sprites). Although I think I left the function in the GameObject class by mistake.

The reasoning is that most of the time it was just a wasted iteration. The game loop would first run preUpdate, then update and then preRender on every single object, and that was just something that really doesn't need to happen any longer. So many dead iterations and empty functions :) 

I wonder if there's an easy way to accommodate what you need / expect? If you look at the Scene you'll see it has a displayList plugin and an updateList plugin (responsible for calling preUpdate only on Game Objects that need it). I split it up like this because again, there's no need to preUpdate GOs that don't have any requirements there, like Images for example. But perhaps we could use the same approach for this. Or perhaps we just keep it under your control and you call update yourself from your Scene? Open to suggestions :)

Share this post


Link to post
Share on other sites

That explains it, and a good reason too!

I had a quick look at updateList.js (I'm on mobile so no testing) and it seems to be exactly what I would suggest. Having an array with objects who should have their update methods called. 

Something like scene.updatelist.add(gameobject), plus remove method, would be great. Or gameobject.activate/deactivateUpdate().

I think it's cleaner to keep it inside of Phaser instead of custom solutions, even if the problem is trivial.

Also I really appreciate the approach of extending sprites (or other game objects) to be self contained and in separate files. It's really helpful to keep the code clean, easy to navigate and to debug.

Maybe a property for the gameobjects too like updateOutsideCamera that just skips the update method if the object is outside camera and the property set to false. Would be great for platformers etc.

Share this post


Link to post
Share on other sites

Ok, I'll work on that then. I'll likely create a 'private' update list for the scene, for game objects to use internally, and then a public one that you can access for precisely this use.

As for 'updateOutsideCamera' it's not quite as simple as that because you can have multiple cameras in a Scene, so it would need to test all of them and also factor in the new 'ignore camera' flags too! I think what would be easier is if we expose an 'isVisible' function on the Camera, so you can check this yourself in your own code and react accordingly.

Share this post


Link to post
Share on other sites

Removing always-there pre-post updating/rendering/etc is great for performance but will slightly increase the difficulty of using Phaser whether an alternative is native or pure user implementation.

I'd like to see a native solution to this otherwise the forums will be filled with people asking how to hook in an update function and getting several differing answers.  It's common enough functionality to expect something built in to standardise the way people do it.

Looking at UpdateList.js now though reminds me of several problems I've had in the past where the triggering criterea was hardcoded into it, ie here it depends on gameObject.active.

How about a more general callback processor - like a simple event/subscriber model?  The system would have several built-in reserved events ("preUpdate", "postUpdate", "preRender" etc) which any object could subscribe to, along with any custom user events.  Possibly the system could have the ability for subscriptions to also register the triggering criterea, maybe by a list of properties with boolean values.  Eitherway, always calling the callback function would provide the ultimate user control.

Share this post


Link to post
Share on other sites

Hi! I spent a  few hours on the same problem. The update function inside my gameobject just wouldn't work...
I have some experience with Phaser 2 but I am totally new to Phaser 3.

Could you guys show me how to implement what  nkholski  was trying? I would like to see an example of the new approach.

Thank you!

 

EDIT: allright, got it!  I changed update to preUpdate and it worked XD 

Share this post


Link to post
Share on other sites

Although I am a bit torn whether to use a class that inherits Sprite or use the component pattern which I see some advocate. Maybe I am just more used to inheritance as I see the code becomes a bit more terse with the other pattern since you store the sprite in a variable and have to remember to access that whenever you actually want to modify/read something from the phaser sprite itself. What do you feel is the better approach? For example I want my player to react to controls, but that either means to pass the controls object into the update method or create the controls within the player object itself. I sort of feel that it would at least be good to separate those? If so maybe some sort of game model object could be passed into the update method? Especially since movement updates has to e.g. check for collisions with the scene objects. Any pointers would be nice. :)

Share this post


Link to post
Share on other sites
On 9/30/2018 at 8:57 AM, johncl said:

What do you feel is the better approach? (...) Any pointers would be nice. :)

Hi @johncl I've always come across this problem in every game I created (most of them are Phaser 3 now). And so in every new project, I try to improve upon my last way to deal with this. Anyways, right now I've found a way that feels very good for me and I rarely come across this problem anymore that I am looking for a Phaser property on a class that's not set because the class isn't actually the Phaser Sprite.

I'm trying to make an example with a Button object that I created for Phaser 3. It's nothing revolutionary and I'm sure others have already found ways to improve upon this but maybe it helps you anyways:

  • I'm using an object with prototypes but you can use classes as well of course.
  • this.ctx is just a reference to the Scene in which the Button will be created, so we can do things like ctx.add.sprite() etc.
  • So the button is an object (or class for you) that contains a property "spr" which is a Phaser Sprite Object.

However, as I gather from your last post, we both don't want to keep calling Button.x or Button.getCenter().x with a bunch of "undefined" or errors because we forget that these things only work on Button.spr! So after running into some other problems with extending classes, I decided to add the most important Phaser properties and methods directly to my object (or class). So now I can call Button.destroy() for example.

You can use this logic for any Phaser Object and its properties / methods. Obviously at some point, if you add too many, you are just copy-pasting the Phaser doc and that doesn't really make sense either...

However, I've noticed that in most cases I only need a very specific number of Phaser properties and methods, especially because the object (or class) was created with a specific goal and task in mind. So adding just the crucial few turned out to be the smoothest solution for me so far.

The best example is probably Sprite.setDataEnabled() etc., so I've copy-pasted the code for you below. Here are some snippets from a Button object I created in Phaser 3, as an example:

const Button = function(ctx, x, y, callback, string, img_name, origin, frames, fontStyle)
{
	'use strict';
	
	// Set a bunch of properties, i.e.:
	this.ctx = ctx;
	// ...there are many more
	
	// Phaser properties, i.e.:
	this.active = true;
	this.alpha = 1;
	this.angle = 0;
	this.rotation = 0;
	// ...you can add any that you need
	
	// Create	
	this.createSpr();
	
	if(typeof this.txt_string !== undefined)
	{
		this.createLbl();
	}
};

// Create the Button Sprite
// With all the callbacks: over, down, out
Button.prototype.createSpr = function()
{
	'use strict';
	
	let _this = this;
	let ctx = this.ctx;
	let frames = this.frames;
	
	// Create sprite
	let spr = ctx.add.sprite(this.x, this.y, this.img_name);
	spr.setOrigin(this.origin.x, this.origin.y);
	
	// Inputs
	// ...
	
	// Add properties
	this.spr = spr;
	this.width = spr.width;
	this.height = spr.height;
};

Button.prototype.destroy = function()
{
	'use strict';
	
	if(this.spr)
	{
		this.spr.destroy();
	}
	
	this.spr = undefined;
	
	if(this.lbl)
	{
		this.lbl.destroy();
	}
	
	this.lbl = undefined;
};

Button.prototype.setDataEnabled = function()
{
	'use strict';
	
	if(this.spr)
	{
		this.spr.setDataEnabled();
	}
};

Button.prototype.getData = function(key)
{
	'use strict';
	
	if(this.spr)
	{
		return this.spr.getData(key);
	}
	else
	{
		// return a fallback value that makes sense for your use-case
	}
};

Button.prototype.setData = function(key, data)
{
	'use strict';
	
	if(this.spr)
	{
		this.spr.setData(key, data);
	}
};

 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.