Menu

First Phaser 3 Game – Lessons & Code Pt. 3

ATTENTION: This series was about the BETA version of Phaser 3! But Phaser 3 has long been released and received tons of updates.

There is a NEW series that shows you how to create games from scratch with Phaser 3! I highly suggest you check it out:

How to Create a Mobile Game from Scratch With Phaser 3


This dev blog posts series is about my first Phaser 3 game “Pixel Memory“.

The previous posts in the series can be found here:
First Phaser 3 Game – Lessons & Code Pt. 1
First Phaser 3 Game – Lessons & Code Pt. 2


Phaser 3 was officially released today and is thus out of beta! Please keep in mind that my first Phaser 3 game was built with Phaser 3 Beta 20 and thus some of the things I’ve mentioned in this series might have been fixed or changed by now. I did however try to cover mostly things that should stay true even after the release.

Overview

  1. No Tweens in Preload
  2. Graphics’ Object Peculiarities
  3. Re-Starting the Same Scene
  4. Playing Sound

1. No Tweens in Preload

This first paragraph is more of a discovery than lesson. I wanted to create a custom loading animation for Pixel Memory that doesn’t use the standard loading bar filling from left to right.

I wanted to have cards slowly flipping over while the game is loading. The tweens were working fine but unfortunately not while the assets where loading. The reason for this is that no tween will ever be run before the scene has completed the preload method.

The thing is that all assets are being loaded in the scene’s preload method, which is before the scene’s create method. So by the time the scene reaches the create method, it is already too late and the whole loading animation is pointless.

Even if I created the tweens in the preload method, they would not be triggered until the scene reached the create method. Manually forcing the tween to start in the preload method didn’t work either.

So I was wondering, how come the preload bar can change the width during the preload method? That’s because it is not a tween! In fact, the preload bar image changes width on every load callback – which triggers every time a file (image, audio, font, etc.) is loaded into your Phaser game.

this.load.on('progress', function(value)
{
	txt_percent.text = Math.round(value * 100) + ' %';
});

So what that means is that you can create a custom loading animation, as long as you can make it work with this progress callback function above. The value argument is a float between 0 and 1. If you multiply it by 100 it represents how many percent of the game assets have been loaded so far.

So if you can think of a different animation that you can base off the value argument, then it will work. Like the preload bar, it only needs the value argument to change its displayWidth property. Tweens however will not work; they will start only after everything has been loaded already.

Back to overview

2. Graphic Objects’ Peculiarities

I was so used to working with the Phaser 2 Graphics Object that I ran into some surprising differences in Phaser 3.

Graphic X,Y-Coordinates

Let’s start by creating a graphics object. Most of my graphics objects are backgrounds for UI or similar things. I made the habit to create the graphics objects at its x, y position, and not at 0, 0 like so many examples show.

Here’s why: If you create the graphic at 0,0 and draw the object later at x,y then the position of that object will actually be at 0,0. If you want to write text inside this graphics object and base the text’s position off the graphic’s position, it won’t work if it’s set to 0,0 – that’s the same as the origin of the whole canvas.

When you create a graphics object in Phaser 3, you cannot pass the x,y values as separate arguments anymore. You have to pass an object now with the x and y properties.

If you create your graphics objects like this, then the x and y properties will correspond to where you’ve actually drawn it:

// We want our rectangle to be at 25, 100.
var x  = 25;
var y  = 100;
var bg = this.add.graphics({ x: x, y: y });

// We make it solid black
bg.beginFill(0x000000, 1);

// When you draw the rectangle, you have to start drawing at 0,0 now because
// this position is relative to what you have set in the create method above
var width  = 350;
var height = 200;
bg.drawRect(0, 0, width, height);

// We are done!
bg.endFill();

Graphic Width & Height

Now you can create other objects in a relative position to the graphics x,y-coordinates. Unfortunately, we cannot base anything off the graphic’s width and height properties anymore.

For some reason, in Phaser 3 Beta 20 the graphic objects do not have a width and height value. So if you need this for relative positioning, you have to set them yourself.

But I would be careful to not overwrite any properties that might be added in a future Phaser 3 release. So I would probably not use bg.width; something like bg.my_width is probably safe to use and prevents future conflicts.

// I wouldn't set these properties
bg.width    = 350;

// This is probably never going to be needed by the Phaser 3 framework
bg.my_width = 350;

Tweening Graphic Alpha

A final thing that popped up was the fact that you cannot tween or change the alpha of an existing graphic object.

I don’t know if this is a bug or something that’s just missing in Beta 20. However, it would be really great if it gets added soon.

If you need to change the alpha value of a graphics object, you have to clear it, re-set the fillStyle and draw it again.

// We create the graphics object
var bg = this.add.graphics({ x: 25, y: 100 });
bg.fillStyle(0x000000, 1);
bg.drawRect(0, 0, 350, 200);
bg.endFill();

// Neither of these will work
bg.setAlpha(0.5);
bg.alpha = 0.5;

// You have to clear & re-draw
bg.clear();
bg.fillStyle(0x000000, 0.5);
bg.drawRect(0, 0, 350, 200);
bg.endFill();

This makes it unsuitable for tweens. Maybe a possible workaround could be to have a tween with a number of repeats and then use the onRepeat callback function to manually clear & re-draw every time.

Back to overview

3. Re-Starting the Same Scene

UPDATE March 19, 2018:
As mentioned in the introduction, the Pixel Memory game and this blog series were written during the Beta 20 Version of Phaser 3.

Meanwhile, Phaser has already been published and updated multiple times – and will continue to do so: fixing bugs and expanding the framework.

Last night, Pablo Farias Navarro has kindly pointed out in the comments section how we can re-start a scene in Phaser Version 3.2.1:

this.scene.manager.bootScene(this);

Thank you so much for keeping this series updated with your comments!


In Phaser 2 you could start a state from inside the same state, which would cause the state to re-start with the init method. It might sound confusing but it’s really just this: re-starting the same scene (or state previously).

In Phaser 3 it will not actually re-start the scene if you are currently in it:

// If you are currently in the PlayScene, this will not work
this.scene.start('Play');

So as a workaround I created a simple re-direct scene like this:

var RedirectScene = new Phaser.Scene('Redirect');

RedirectScene.init = function()
{
	'use strict';
	
	// ...
};

RedirectScene.create = function()
{
	'use strict';
	
	this.bg = this.add.image(0, 0, 'bg-main').setOrigin(0);
	
	this.scene.start('Play');
};

If passing data between scenes were possible we could simply pass the name and have the RedirectScene be more flexible. Right now you have to set a global variable if you want the RedirectScene to re-start different scenes.

Fortunately for Pixel Memory I only needed to restart the PlayScene; hence, I’ve hard coded it into my RedirectScene.

You also must set a background. Because the RedirectScene will actually show for 1 frame and that causes a short but still noticeable “flashing” every time you re-start the scene.

I know this looks like an ugly workaround but it’s the only thing I got working and there were no examples on Phaser Labs to give any insight.

Back to overview

4. Playing Sound

To round up this series I just want to point out how amazing playing sound is in Phaser 3!

Once the audio files are loaded into the game, you can play them anytime. No need to create a sound object anymore like in Phaser 2.

It’s a small thing that makes working with sound files so much more fun!

// PreloadScene
this.load.audio('level_won', [ 'levelwin.ogg', 'levelwin.m4a' ]);

// PlayScene
this.sound.play('level_won');

Back to overview

And that’s it for this series. If you find any typos, mistakes, or have suggestions, please feel free to do so in the comments below.

The Ultimate Phaser 3 Guide

Everything you need to make real, polished, professional Phaser 3 games, you find it in this guide here: HTML5 Game Development Mini-Degree

I highly suggest you check it out!

9 comments

  1. Hi there! thanks for making these tuts.

    I managed to restart the same scene by doing: this.scene.manager.bootScene(this);

  2. admin says:

    Hi Pablo, thank you for pointing this out! You are right, it was just recently updated in one of the newer Phaser 3 versions. I forgot to update it here, thank you so much for the reminder! I updated section “3. Re-Starting The Same Scene” with your comment.

  3. No worries! Thanks for keeping the tutorial up to date, and for the mention 🙂

  4. Tim says:

    Hi. In regards to your issue with tweening in the preload, I believe there is a scene config option called ‘files’ that allows for assets to be completely loaded before preload is executed.

    Here’s a snippet from the Scene Manager documentation available at https://phaser.io/phaser3/api/scene-manager

    Files can be specified in the Scene config files payload and they will be loaded in before the Scene is started, meaning they’re available before even the Scene.preload function (if set) is called.

    This is perfect for loading in small JSON config files for example, or a tiny amount of preloader assets that the preloader itself needs to use

    Example:

    var sceneConfig = {
    preload: preload,
    create: create,
    files: [
    { type: ‘image’, key: ‘sonic’, url: ‘assets/sprites/sonic_havok_sanity.png’ }
    ]
    };

    I’ve not tested the code but it appears likely that it would solve your issue.

    Thanks for the tutorial!

  5. Regarding the code to reload a scene, you might wanna hold using it for a bit. It’s not fully reloading the scene as I’ve reported here: http://www.html5gamedevs.com/topic/36938-scene-rebooting-issue-sprites-dont-disappear/

    Not sure if it’s my mistake or if there is a bug.

  6. To restart the scene use Phaser 3.4 and this should work: this.scene.restart();

  7. admin says:

    I’ve just tried re-starting the same scene in a new project. Using Phaser v. 3.11 it is working flawlessly for me now using any of these two methods:
    this.scene.start('SceneName');
    this.scene.restart();

    This new project is very small and in infant stages, so I’m curious to see if there are going to be problems with already set values in the scene etc. But so far the scene seems to be completely cleared and re-started fresh. Thanks for your input!

  8. blackhawx says:

    If the author plans on extending this series, I would love to get your opinion on how to collide sprites (images) with graphics in Phaser 3. It’s not working for me, even when I attempt to convert a graphic into a sprite. But if you happen to know how, that would be awesome.

  9. admin says:

    Hi blackhawx, thank you for your comment. I don’t know how you do collision detection (are you enabling one of the Phaser physics systems and use built-in methods to check for collision? Or do you have a custom method for collision detection?) and also I am not quite sure what you mean by converting a graphic into a sprite.

    However, I think I still know what your issue is, namely that you need to add a Phaser Geometry Object to your Graphics Objects for them to have a bounding box! I don’t have a code example for you where sprites collide with graphics, but I have an example with pointer inputs (mouse clicks) on Graphics Objects:

    // Callback
    let callback = function() {};
    
    // Graphics object
    let input_area	= this.add.graphics({ x: x, y: y });
    
    input_area.fillStyle('0x000000', 1);
    input_area.fillRect(0, 0, width, height);
    input_area.setDepth(this.DEPTH.ui);
    
    // Clickable area
    let hitArea	= new Phaser.Geom.Rectangle(0, 0, width, height);
    
    input_area.setInteractive(hitArea, Phaser.Geom.Rectangle.Contains);
    input_area.on('pointerdown', callback, this);
    

    The black rectangle created above (as Phaser Graphics Object) can be setInteractive(). However, no input events will be registered if you don’t also manually set the bounding box with a Phaser Geometry Object. I’ve used the rectangle here because my graphics object is also a rectangle. Note: The x and y coordinates of the Phaser Geometry Object have to be set to 0 because the position is relative to the Graphics Object!

    Try the code snippet above without the hitArea argument, like this: input_area.setInteractive(); And you’ll see no clicks are registered. Once you add it, it works perfectly. Therefore I am assuming you cannot register any collision for the same reason. Try setting the bounding box making use of the Phaser Geometry Object that fits best. I hope it works!

Leave a Reply