Welcome to the biggest release of Phaser, ever! New renderer, isometric tilemaps, layers, post processing effects, layers and lots, lots more!

Article by Richard Davey. Posted on 16th Dec 2020.   @photonstorm

image

After 13 beta releases, over 200 resolved issues, thousands of lines of new code and the culmination of over 6 months incredibly hard work, Phaser 3.50 is finally here.

It's not hyperbole or exaggeration when I say that Phaser 3.50 is the single biggest point release ever in the history of Phaser. There are quite literally hundreds of new features to explore, updates to key areas and of course bug fixes. I did actually try counting all the changes, but gave up after I'd reached 900 of them! Thankfully, they are, as always, meticulously detailed in the Change Log. The changes for 3.50 actually grew so large that I had to split them out from the main Change Log and put them into their own file.

However, don't let this overwhelm you. A massive number of the changes are purely internal and while there are absolutely some API breaking changes in this release (hence the large version number jump), we've kept them as sensible as possible. We already know of lots of devs who have upgraded with minimal, or no, changes to their actual game code. We cannot guarantee that for everyone, of course, but depending on how complex your game is, the chances are good.

It's not possible to cover everything that's new in this single article, so instead I'm going to highlight some of the most important new features. Then, in the coming months new tutorials and articles will be published that delve into specific features in more depth.

Examples Galore!

image

Thanks to the tireless work of new Phaser team member, Francisco (gammafp on the Phaser Discord) every single example in the Phaser 3 Examples repo, and on the examples site and the Labs Testing Site, has been tested and, where required, updated to work with 3.50.

This should give you a great boost when migrating to 3.50, or adopting it for the first time, as you can pick an example from the site and know the parameters and syntax are updated for 3.50. We've also started the process of converting them all to ES6 code as well, so you'll notice this has happened to several hundred of them. Still plenty to go, but it's an on-going effort that we will complete by end of Q1 2021.

If you need them, all of the Phaser 3.24 and archived examples are still available here, but the main examples are 3.50 compatible right now.

Getting Started with Phaser 3.50 - For Beginners

I'm very pleased to announce that SuperTommy over at Ourcade has published this great video on Getting Started with Phaser 3.50 for Beginners.

image

You can watch as he demonstrates the process of setting up a dev environment and gets cracking making a game. The tutorial is nice and easy to follow and there are a couple of great free books to grab from it as well, so definitely check it out.

Even if you're already familiar with Phaser and don't need a beginners guide, I would urge you to visit the Ourcade site because they have a huge number of quality Phaser tutorials covering all kinds of fantastic topics.

So, what's new?!

As mentioned above, I simply cannot cover all new features here, so please follow the Phaser Patreon for dev reports and new tutorials in the coming months. With that out of the way lets dive into some of the changes available in 3.50.

The core WebGL Renderer was virtually rebuilt from scratch in 3.50. It now surfaces far more visual control and power than it ever exposed before. A lot of the R&D work that went into Phaser 4 development has been back-ported to 3.50, so is ready to use today. Here are some of those features:

Post FX Pipelines, Post Processing and Render Targets

image

Thanks to the new Post FX class in 3.50 it's possible to easily create post processing effects and apply them to Game Objects and Cameras.

This allows for some really nice effects such as blur, distortion, vignettes, CRT overlays, outlines, glows, pixelation, god rays, wave shaders and just about any other effect you care to throw a shader at. Game Objects can be given more than one effect, as well:

const logo = this.add.sprite(x, y, texture);

logo.setPostPipeline([ GlowFX, TwirlFX ]);

The effects are processed in order, passing from one to the other, before returning rendering to the game.

Unlike in previous versions, rendering pipelines can now have as many shaders as they like and you can easily swap between them from your code. This allows for a single effect to apply multiple shaders. They can also have as many Render Targets are they like. These are new in 3.50 and offer a way to encapsulate a texture and framebuffer, so you can produce effects that require drawing to multiple targets with ease.

Lots more details about Post FX Pipelines will be released in the coming months.

Mesh Game Objects

image

The Mesh Game Object has existed in Phaser since v3.0.0. It was meant as a way for you to be able to construct Game Objects built from your own vertices and texture data. Where-as a Sprite uses a standard quad, with a Mesh you were under no such limitations and could define the vertices and uvs exactly how you wanted them.

Have a play with this demo.

However, it really didn't offer any assistance in this regard, which I always thought was a shame. So, I took some time to rework the whole class and it's now properly useful and really quite powerful! The new version provides lots of new features, including grid generation and OBJ Loader, better internal caching and an internal camera. It's important to understand this is not a 3D engine, not even a 'mini' one, as the Mesh is drawn by projecting the vertices into a 2D orthographic space. However, you can still create some lovely effects with it and all kinds of Game Objects that were never even possible before.

Multi-Texture Support

image

Phaser 3 has always been single-texture bound. This means that, at most, Phaser would only ever use a single texture slot when preparing a WebGL batch. If a new texture was found in the display list, then a new sub-batch was created. During render it would essentially flush the batch, load the new texture into unit zero and then draw the next batch. And on, this process continued, until all Game Objects had been rendered.

This is fine when you've either not many Game Objects on-screen, or when they are being drawn from, at most, a couple of texture atlases. However, throw some other elements into the mix, such as Text, Graphics or Tilemaps, and suddenly all of this constant texture switching can take its toll.

According to the WebGL 1 spec, the device and browser must support at least 8 texture units. That's the bare minimum. Most modern GPUs support even more. The mid-range one I'm using right now, for example, supports 16. This means WebGL can use up to 16 texture units in a single draw call. When you've got a game that is drawcall bound, rather than fillrate bound, this can have a significant impact on performance. With this in mind, I spent time recoding how the Texture Tint Pipeline worked and recoding the shaders for it. I did away with all of the internal batching and array creation and implemented multi-texture support.

The impact on drawcalls is dramatic. It's not unreasonable to be able to render your entire game in a single drawcall now. There are cases, especially when targeting low-end mobile devices on which branching shaders are more expensive, where you may wish to limit the texture units, which is of course possible via the game config.

Isometric, Hexagonal and Staggered Tilemap Support

image

When Phaser 3.0.0 was completed it had built-in support for Tilemap rendering. This was achieved via the Tilemap class and a whole bunch of supporting functions, allowing you to easily get Tiled, CSV and Impact maps running in your games. One notable feature it didn't have, though, was support for Isometric tilemaps. This is one of those feature requests that crops up on a quite regular, although somewhat lowkey basis.

Several unofficial Isometric plugins were released for Phaser 3 which had varying levels of success. Then the community stuck a Bounty on the feature and someone stepped forward and built it. Originally, I was going to have this feature available under a compiler flag, so you specifically had to package it into your build. But after spending days working on it and tidying things up, it made sense to just include it outright. So, as of Phaser 3.50 you can now import isometric, hexagonal and staggered isometric tilemaps from the Tiled Map Editor, directly in to Phaser.

This is an example you can play with. Use cursor keys to move the camera.

Loading an isometric map is identical to loading a regular tile map. Tiled stores the extra information required in its standard map data, which 3.50 now parses out for you.

Aseprite Support

image

Aseprite is a fantastic animated sprite editor and pixel art tool. It has loads of great features built-in to handle animations, which is why loads of devs use it specifically for that.

It has long been able to export those animations as JSON files and texture atlases. And in Phaser 3.5 you can now use those files directly via the new method createFromAseprite.

It will generate an animation for each tag you've set-up to be exported, or you can pass in an array of tags to create animations just for those. Either way, it's now possible to have your game load and use Aseprite JSON / PNG files natively, which is a great win for animators everywhere :)

You can see this working in this example.

Check out the comprehensive method documentation for details on how to configure your export for this new feature.

Massive Spine Update

image

I've worked hard on the Spine Plugin in 3.50 which now includes some smart new features worth talking about.

The Spine Runtimes have been updated to the latest official release and the plugin build process is a lot easier to use, should you want to upgrade the runtimes yourself.

I went through every single Spine issue in GitHub and fixed them all! Bugs like not being able to use more than 128 Spine objects in a Container, issues with performance when a mixture of Game Objects were present in the display list, patches for HEADLESS mode and lots more.

The most important new feature, however, is the new Spine Container Game Object.

While previously it was possible to add Spine objects into regular Containers, they were not very efficient when it came to batching in WebGL due to the mixed nature of their contents. The Spine Container avoids this and, if your Container only needs Spine objects in it, you should absolutely use it in place of a regular Container. The code is almost identical:

let base = this.add.spineContainer();

for (let i = 0; i < 32; i++)
{
    let obj = this.add.spine(i * 64, 100, 'boy', 'idle', true).setScale(0.25);

    base.add(obj)
}

The difference is remarkable, though. Under Phaser 3.24 a simple Scene using regular Containers this would consume 278 GL commands with 4 draw calls and 4 clears. Under 3.50 using Spine Containers it's 43 GL commands and just 1 draw call and 1 clear.

If you use Spine in your game and you have to use Containers, I strongly recommend swapping over.

Point Lights Game Object

image

The Point Light Game Object is brand new in 3.50 and provides a way to add a point light effect into your game, without the expensive shader processing requirements of the traditional Light Game Object.

Have a play with the Point Lights Demo

The difference is that the Point Light renders using a custom shader, designed to give the impression of a radial light source, of variable radius, intensity and color, in your game. However, unlike the Light Game Object, it does not impact any other Game Objects, or use their normal maps for calculations. This makes them extremely fast to render compared to Lights and perfect for special effects, such as flickering torches or muzzle flashes.

Layer Game Object

image

A Layer is a special type of Game Object that acts as a Display List. You can add any type of Game Object to a Layer, just as you would to a Scene. Layers can be used to visually group together 'layers' of Game Objects:

const spaceman = this.add.sprite(150, 300, 'spaceman');
const bunny = this.add.sprite(400, 300, 'bunny');
const elephant = this.add.sprite(650, 300, 'elephant');

const layer = this.add.layer();

layer.add([ spaceman, bunny, elephant ]);

The 3 sprites in the example above will now be managed by the Layer they were added to. Therefore, if you then set layer.setVisible(false) they would all vanish from the display.

You can also control the depth of the Game Objects within the Layer. For example, calling the setDepth method of a child of a Layer will allow you to adjust the depth of that child within the Layer itself, rather than the whole Scene. The Layer, too, can have its depth set as well.

The Layer class also offers many different methods for manipulating the list, such as the methods moveUp, moveDown, sendToBack, bringToTop and so on. These allow you to change the display list position of the Layers children, causing it to adjust the order in which they are rendered. Using setDepth on a child allows you to override this.

Layers can have Post FX Pipelines set like in this example, which allows you to easily enable a post pipeline across a whole range of children, which, depending on the effect, can often be far more efficient that doing so on a per-child basis.

Animation System Overhaul

image

I took a good, long, hard look at the Animation system in Phaser 3 and concluded it was in serious need of some TLC. Fundamentally, it worked. It wasn't a particularly bug-ridden area of the API by any stretch, yet it absolutely was doing a lot of heavy lifting it just didn't need to do. And, I'd wager, was more confusing than it ought to be to actually use.

So, the changes I've made in this area are virtually all about quality of life for end developers, as well as performance gains.

There are also loads of new examples to play with and learn from.

Although it doesn't impact you very much, I've reorganized the API structure a lot. There is a new class called the Animation State. This replaces the old Animation Component that Sprites used to have. Fundamentally, it performs the same actions, but the new name makes it much easier to find in the documentation. This component used to make lots of calls out to the Animation instance that it was playing, as well as the Animation Manager, just to do simple things like load the animation properties it needed for playback.

This has all changed in 3.50. The Animation State no longer needs to make multiple calls out just to play something. It's far more autonomous and looks after its own house, instead of passing itself up the food chain and having things set on it. The number of internal calls made to play an animation on a Sprite is now significantly smaller than before, which is great, because less steps means faster processing. This doesn't directly impact your game code however, just the end results.

A very important change that definitely does give you a lot more flexibility is the new PlayAnimationConfig object.

Animations have always had the ability to set various properties on them, such as the frame rate, a delay before starting playback, or the number of times they will repeat. These were set when the animation was created and while the old Animation Component did have some properties to modify them, several of them didn't work and those that did only worked once playback had begun. There needed to be a better way. Enter, the new config object.

You can now pass a config object to the play methods, such as:

const mummyAnimation = this.anims.create({
    key: 'walk',
    frames: this.anims.generateFrameNumbers('mummy'),
    frameRate: 16
});

var fastMummy = this.add.sprite(50, 300, 'mummy');

fastMummy.play({ key: 'walk', repeat: 7, frameRate: 24 });

Here you can see we've create a global animation called 'walk', which has a frame rate of 16fps. But we want a special type of uber-baddie that runs quicker than this, so when we call 'play' we pass in a config object with a new frame rate of 24fps. This overrides the default of 16fps. We also tell it to repeat the animation 7 times. The repeat was never set in the global animation, so this new value is specific to this Sprite instance only.

Any property you can set when creating an animation can now be overridden via any of the 'play' methods. This should help you cut-down on the number of variations of global animations required where the only real differences were presentational.

Check out the documentation for more details.

Will 3.50 be the final version of Phaser 3?

I've been asked this a number of times on Discord, so figured I'd address it here as it's not a simple answer. First of all, no, it won't be the final 3.x version, because I fully intend to carry on supporting Phaser 3. There are so many large changes in 3.50 that I'm quite sure it will generate bugs I haven't encountered during my testing, so I'll of course resolve those.

However, I'm not expecting any more 'sweeping' API changes in Phaser 3's lifetime. Yes, I'll carry on fixing bugs, and almost certainly adding features where relevant, but in 2021 most of my attention will be on Phaser 4. The same way that Phaser 2 and 3 overlapped (and still do!), Phaser 3 and 4 will be treated in the same way. Eventually, 4 will get all of my attention and I'll likely hand v3 over to the community, but that won't be for a long time yet.

For as long as people keep backing me on Patreon, I'll keep working to improve Phaser 3 and building out our collective future in Phaser 4.

Thank You!

I'd like to finish by sending a massive thank-you to everyone who supports Phaser on Patreon, GitHub Sponsors and our corporate backers. Your continued funding allows me to keep working on Phaser full-time and this monster of a new release is the very real result of that. If you've ever considered becoming a backer, now is the perfect time!

We've got a really exciting future ahead of us. 2021 is going to be a great year to be a Phaser developer! With a powerhouse 3.50 to work from and development of 4.0 going full steam, it's an exciting time for web games in general.

Right now, though, I'm going to relax for a bit :)

Download 3.50.0