Navigation

Phaser 3 Dev Log - July 2020

Published on 24th July 2020

Yes, you read the title correctly. And yes, this is a Dev Report about Phaser 3, not 4 :)

After releasing Phaser v3.24 just over 10 days ago, I figured that was a good version to have in the wild while I returned to Phaser 4 work. It had lots of nice fixes and kept the API current. However, upon returning to the Phaser 4 codebase, I couldn't help but think it was only fair that I transferred as much of my learnings from v4 into v3 as I could. There were some features that v3 was just crying out for. So, I took a week away from v4 dev and implemented them.

v3.25 Beta is available to download today, ready for your testing. Please see the section at the end of the Dev Log for details on where to grab it. The more testing, the better, so please do try upgrading!

Multi-Texture Support

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 draw call bound, rather than pixel 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 following video will help demonstrate the difference. Note how the total quantity of WebGL commands and draw calls drops dramatically between the two versions:

https://youtu.be/7xWFlDUQtUo

Multi-texturing can be controlled by the Game Config object but is fully enabled by default. There is a new config setting `maxTextures` which you can define:

Here you can see we've set `maxTextures` to 8. This tells the GPU to only use up to 8 texture units at once and it will create a shader specifically targetted to 8 units as well.

The default value is -1 which means "test the GPU on start-up and use whatever the maximum is".

There are cases, especially when targetting low-end mobile devices on which branching shaders are more expensive, where you may wish to limit the texture units. This config value lets you do that. For most games, however, the default is probably fine left at being based on the hardware.

I spent a good while going through all of the Game Objects and updating them to support multi-textures. This means you can now batch together Sprites, Blitters, Bitmap Text, Particle Emitters, Shapes, Graphics, Meshes, Static Text, and Dynamic Tilemaps and they will draw together. None of their APIs have changed, so you don't need to do anything your end to enable this.

I did make a number of significant changes to the Texture Tint Pipeline, however. So if you have written any custom pipelines, especially those that may extend TTP, then you need to check the Change Log to see what's happened and update your code accordingly.

Light Pipeline Updates

While working through adding multi-texturing support, I figured I would take a look at the Lights Pipeline as well. The lights pipeline has offered a forward diffuse lighting system since early-on in v3s life. However, it never did it quite right and, truth be told, I was all for removing it entirely from the API. I knew it was broken, I knew it wasted space in the bundle and I was fed-up with having it in there.

So, I went into the files with the sole aim of removing them entirely. While looking at them, however, I figured I would give it one last chance. I would spend a day trying to fix it and, based on how far I got, that would seal the fate of the pipeline.

As it turned out, due to all the work I had done in the Texture Tint Pipeline (which Lights extends), fixing the Lights became a whole lot easier than removing them. The code in the pipeline itself was dramatically culled. Originally, it used to be hundreds and hundreds of lines of code, almost mirroring the Texture Tint Pipeline directly. Over the years it has been tweaked a bit, but the final incarnation in v3.25 is literally just one custom shader and a couple of new pipeline methods to handle normal map assignment. The whole pipeline is just 6 functions.

Having fixed the pipeline, I set about adding support for the lights system into Game Objects that just never had it before. This means you can now have lights running on, say, Particle Emitters:

Click here for the Particle Lights demo.

Or on Text:

Click here for the Text demo.

Even on Shapes and Graphics objects:

Click here for the Shapes demo.

In short, all of the core Game Objects now support the Lights Pipeline.

Support is offered in two different ways. First, they react to the lights that are in the Scene, both ambient and direct. You can see this in the Shapes demo above. The way in which the iso triangles are changing color based on the spotlight attached to the mouse pointer. While viewing the demo, click to change the light color.

However, lots of Game Objects now also support normal maps. For example, Bitmap Text, both Static and Dynamic, support normal maps now:

Click here for the Bitmap Text demo.

Because the bitmap text font has a normal map associated with it, the Lights Pipeline now uses this automatically in the shader. This allows you to create normal maps for all manner of Game Objects that previously didn't support them. Adding a normal map to your code is as simple as providing one when loading an asset:

As well as implementing normal map support, I also fixed a few lingering bugs, such as the way the tilemap layers with lights would render one tile at a time! Or the Lights Manager cull would use the wrong renderer height. All of these updates are now in v3.25 and I will no longer be deprecating the Lights API. You can, of course, exclude it from a custom build if you don't need it, but hopefully, after all these fixes, more of you will take advantage of it.

Check out the Lights API video to see lots of the new Game Objects in action.

You can play with the examples and see the code in the Lights section of the Labs.

ModelViewProjection Updates

Since the beginning, v3 has had a Model View Projection object, containing loads of functions that it never even used internally. As part of the pipeline clean-up, I decided to resolve this as well. There is a new MVP namespace, into which I have put all of the MVP functions, such as 'modelScale' and 'viewLoad'. The functions are now single files, meaning where they are required by Phaser, it just imports the one function directly.

More importantly, this also means you can now exclude a pretty large chunk of code from your custom build, saving you even more space.

Download v3.25 Beta

There are plenty more changes and fixes in 3.25 and I would really urge you to play with it. I don't normally release beta versions for v3 anymore, but I feel this is a significantly big enough change to warrant one. As a result, the new v3.25 Beta 0 is available from both npm and GitHub.

You can get it from npm using the beta tag:

Or download it from GitHub Releases.

You'll find pre-built bundles to download, or you can check out the master branch and build yourself.

Please do download this beta and test it. It should, in most cases, be a drop-in replacement for v3.24. If you do run into bugs, especially on mobile, raise them as issues on GitHub and stick 'v3.25' in the title somewhere.

I felt like v3 deserved some TLC from me and hopefully, everyone will benefit from the work I've put into 3.25. This is quite similar to when I was working on Phaser 3. After months of hard slog on v3, with the community mostly handling v2 updates, I returned to v2 and added in a bunch of features that I had learned from working on v3! I guess history really does repeat itself sometimes.

I aim to release 3.25 in early August, so I'll give it a couple of weeks for you to help test it, while I carry on testing against the rest of the examples. I plan on returning to v4 development next week but will keep an eye on 3.25 feedback in the meantime, both on Discord and GitHub. If you find a bug, please report it.