Phaser World Issue 200
When Phaser World first started in 2015, we had no idea it would still be going nearly ten years later. Yet here we are with issue 200. Yes, there was a bit of a break around 2021, but aside from that, it has been a pretty epic journey so far. In Issue One, I talked about the release of Phaser 2.4.4, and there was a tutorial telling you how to use Phaser with the Atom Text Editor (remember that?!) - oh, how times have changed. Let’s not dwell on the past. Instead, here is what has been happening recently.
🕸️ The new Phaser website is live!
Out with the old and in with the shiny, glowy new!
It took a lot of effort from the whole team, but the new Phaser website is finally live! We had been stuck with the previous design for too long, so it was time to reboot it. Designer Simon Floter took our requirements and nailed the creation of something nice and modern, which kept the characters and charm we had built into the Phaser brand over the years. Then our site developer, John Pope, turned that into the site you see today, complete with all of the backend goodness that powers it all.
We also released our brand new Phaser Examples site, which offers a much quicker way to search through our thousands of examples. It also has new features, such as downloading the entire example as a zip file, including all the source files if it’s a multi-file example.
As with all undertakings of this magnitude, plenty of work remains to be done. We need to make tweaks (especially for ultra-wide monitor users) and add new content, but overall, we’re thrilled with the changes. It would appear our visitors are, too, because our user registration rate has skyrocketed since the redesign's launch.
The new Phaser Documentation site is next up for release. Hopefully, we can announce it shortly.
🎮 Kitchen Sorting
Sort your zucchinis in this food themed puzzle game!
Kitchen Sorting is a lovely new puzzle game that challenges you to sort ingredients into correct jars before you add them to your delicious dish. Tap a jar to pick ingredients from the top, tap another jar to drop them in. Once a jar is filled, it's added to the pot and the meal gets closer to completion.
💰 Let us publish your Phaser Games
Got a Phaser game you’d like published? Get in touch!
At Phaser Studio, we’re all about empowering developers to create great games. By focusing on the framework and tools to allow this, we’ve seen thousands of games released over the years, many of which went on to critical and commercial success.
And we’ve noticed the publishing landscape is changing yet again. New platforms are opening up that introduce keen opportunities for the right games. And it’s something we’re deeply invested in.
So - if you’ve developed a Phaser game you feel would benefit from wider distribution to exciting new platforms, then we’d love to hear from you. Please email a link to your game or games portfolio to bizdev@phaser.io
, and we’ll be in touch.
🌟 Solitaire Tutorial Series
Scott Westover takes on Solitaire
Scott Westover is back with another tutorial for a classic game. This time, he is building Solitaire. While Solitaire may not be the next summer blockbuster that you were planning to create, the foundational knowledge that you’ll gain while making it will allow you to create any kind of card game or deck builder in the future!
Watch the Solitaire Tutorial Video Series
Phaser Studio Developer Logs
👨💻 Rich
This week has been quite the rush. We kicked it off by finally releasing the new Phaser website. This was a monumental amount of work. You’d think building a new website would be pretty easy, yes? Perhaps for some projects, it is. But the Phaser site has so many moving parts and such a legacy to uphold it became a lot more challenging than we could ever have conceived. However, Simon, John, and Jean finally smashed it out of the park, and the new site went live early in the week.
Because it was so fresh, I didn’t announce it or mention it on the Phaser Discord. I just wanted to keep an eye on our regular organic traffic and try to catch issues as they popped up. Thankfully, they’ve been pretty minor. We still need to do some more layout tweaks for those psychos people who run their browsers full-screen on ultra-wide monitors 😀 (I mean, I love ultra wides, I have 3 of them side-by-side! But I never fullscreen my browser on one), but aside from this and a few other little CSS text sizing and footer tweaks it’s all pretty solid.
The result of the redesign on user conversions is pretty remarkable, too. The Phaser site has always enjoyed a good quantity of organic traffic. We average around 100k users a month and use Sentry to monitor and alert us to site issues, so if something does break, we get notified within minutes. Thankfully, nothing too gregarious happened! However, we saw a significant uptick in users registering and subscribing. We can attribute this purely to the redesign, as we did no marketing or announced the site on socials. So, that’s a massive win for everyone involved.
Aside from working on the site, I’ve also been busy with the new docs site and our partnership with Hashnode. We found a few critical issues in their service, which they promptly addressed, and we’re very hopeful that when they close off one large stopper for us in the coming days, that will be it! We can then do a final pass on the content, links, and layout and release the new Phaser docs site. It’s already much better than the current one and will have such a huge impact on developers’ lives that I cannot wait for it to be available. If all goes well, that should be this week. Fingers crossed!
👨💻 Francisco - Phaser Launcher
This week, I've been working on organizing the code of the Phaser Launcher tool we had from our previous draft. While it was functional, it had several issues, such as difficulties opening large folders with many subfolders. Additionally, the lack of proper state management made it harder to know which folder was selected or which tab should be displayed.
In response, I reorganized the code and researched how to optimize the loading of large folders, which led me to implement lazy loading. This improvement significantly enhances the tool’s ability to handle large datasets efficiently. I also added internal state management, allowing the tool to track which elements have been added or selected.
Moreover, I started working on implementing a tab section, which is a key feature needed for the system, similar to how Visual Studio Code handles multiple files and views. This will be essential as the tool evolves to manage multiple file operations simultaneously.
👨💻 Pete - Box2D
Over the past week, we've been on a whirlwind tour of optimization and refinement for our physics engine. We wrapped up the 'easy access' interfaces for the remaining joint types, giving developers useful tools to work with distance, wheel, prismatic, motor, and mouse joints. We then dove into a performance and stability comparison.
While our version showed excellent stability, we uncovered some performance issues, especially when dealing with numerous contacts. We discovered we were creating and destroying a staggering number of b2Vec2 objects per world step, triggering frequent garbage collection.
We noticed that we were still using Object.seal()
. Removing this legacy safeguard doubled our performance - a pleasant surprise! We took a look at debug drawing performance, which proved to be a constant and minor factor. Comparing our JavaScript version to its C counterpart highlighted the challenges of garbage collection in JS.
This prompted us to roll up our sleeves and dive deep into optimizing object creation. We added instrumentation to track the biggest consumers of b2Vec2
objects and began refactoring to reduce run-time object creation. b2Vec2 allocation has dropped significantly from 140k-240k per world step, to < 25k. However, mobile performance is not good in longer runs, with frame rates declining substantially after an initial smooth performance of 60fps with 400 objects in a tumbling box test, which you can see below:
Looking ahead to next week, we'll continue optimization, targeting other memory-hungry elements like b2ManifoldPoint
and b2ContactSim
. We're also gearing up for wider testing, which means we need to polish our 'easy access' interface and create some JS-specific documentation.
👨💻 Can - Discord Activities Tools
After updating our Discord templates and tutorials, I dived into our new tool, to get boring tasks from the devs as much as we can. The biggest struggle we identified is dealing with tunneling the project from the local, not having assets loaded properly, connecting client and server on the Discord itself.
It's my first time going with Tauri, so lot's of tinkering, research and of course failures going on behind of the scenes. I've came up with minimal working solution as you select your project folder, once its selected, the app will read your package.json to catch npm script commands in order to execute with just one click and it will filter the tunneling url you will need.
Idea is great, yet execution is not that great yet! Once the quirks are solved, we will be going much faster for a possible build! You can get the grisp of the idea from the app's screenshot, though it might change all over too, so don't look too close!
Till next week, keep your pixels quality everyone!
👨💻 Zeke - Phaser Docs
Hello and welcome to another weekly update on Phaser Documentation:
Sound
. I discovered Web Audio API actually has an analyzer node feature that enables creation of audio visualisations. With some work, it’s entirely possible to make procedural rhythm-based games like Osu!, Beat Hazard or Piano Tiles in Phaser. Besides the usual audio playback and volume control, you can also play around with spatial audio to give a sense that some sounds are coming from a specific direction.Mesh
. This Game Object allows you to add simple 3D objects into Phaser games. Import your 3D .obj files, apply some textures to it and voilà! Well, checkout the examples mesh folder here for more details. If you really want to try making a full fledged 3D game in Phaser, check out Enable3D.Animations
. One feature you may want to consider using is chained animation where you specify the animation that plays after an animation sequence ends. Think of using chain animations to create smooth transitions between different Game Object states such as from walking to running to jumping.Timer
andtimeline
. Timers help you keep track of time, trigger time-based events and create complex timed sequences. One interesting feature is called time scale where you can manipulate a given time by scaling up or scaling down to increase or decrease the rate of change in time. Check out this example in the Phaser labs.Display List
. Used to control the order that Game Objects are rendered in a Scene. A common way to use the Display List is to add Game Objects into Containers so it is rendered as a layer. Use this to sort the rendering order of different game elements such as the background, in-game objects, user interface and overlay menu.Phaser.Utils.Array
. While JavaScript already comes with its own set of methods, Phaser adds on additional functionality commonly used in games. Some features I’ve found most useful areShuffle
,GetRandom
,RemoveBetween
,Swap
andCountAllMatching
. No more writing lines of code to create such features, Phaser does it in just a single line. Example:Phaser.Utils.Array.Shuffle(array)
automatically shuffles all items in the givenarray
. Check out our existing docs here and examples here.
Tales from the Pixel Mines
October 7th, 2024 by Ben Richards
This week, I finished the DynamicTexture revamp, and finally got started on the new FX/masks system. We'll look at each of these in a bit more detail.
DynamicTexture
The finalized DynamicTexture (and RenderTexture) have a diminished instruction set. I've removed any need to manage render state; it all happens automatically now. Just call render()
and it executes all the commands you've queued up.
The drawing commands are now reduced to the following:
stamp
draws a configurable texture frame.repeat
draws a texture frame using a spare TileSprite.draw
draws a texture frame, game object, Group, DisplayList, or array.erase
is likedraw
but it uses the ERASE blend mode to erase holes in the texture.clear
clears the texture.fill
fills a rectangle with color.render
executes all commands in the queue.
Several of these commands just draw texture frames directly, without creating a dedicated game object. I've changed the origin of texture drawing to make defaults more consistent. The origin defaults to (0.5, 0.5), the middle of the texture, instead of the top-left. This may change the way existing DynamicTexture uses render. It is now consistent with other game object positioning.
Several other commands have been removed. Now you don't have to worry about batching. Everything performs at maximum efficiency. This will also affect existing uses of DynamicTexture.
If you want to use DynamicTexture, either in a future release or if you routinely look at working branches of the Phaser repo, you'll need to make a few changes. Most of the removed commands can just be replaced with draw
. If you're drawing a texture frame, and its coordinates have changed, you should be able to get the same results with stamp(texture, frame, x, y, { originX: 0, originY: 0 })
. This will set the origin back to the top-left and get the previous behavior.
These revisions have made DynamicTexture more efficient. For example, Phaser 3 renders a DynamicTexture with two draw calls: one to render it to a temporary framebuffer, and another to flip it upside-down in the destination framebuffer. The new Phaser Beam renderer will use just one draw call, and one framebuffer, making it nominally twice as efficient! This doesn't necessarily mean twice as fast, because the repeated operations are in WebGL and typically run much faster than CPU code. And it doesn't mean memory usage is halved, because temporary framebuffers can be recycled, so there are typically only a few being passed around. But it does mean things are better.
RenderFX
I'm now working on the RenderFX object. This was previously referred to as "Box" until Rich reminded me that a box could be anything. I'm calling it RenderFX now, because that puts it next to RenderTexture, and that might help users associate the two types.
A RenderFX is a wrapper around another game object. It puts it into a framebuffer, and applies FX to it. This replaces the existing FX component on game objects. This does make it slightly more awkward to set up an object with FX; you need to make a RenderFX and put the object into it (which I'm making simple with automation functions), and you need to consider input and physics for the RenderFX instead of the original object.
However, this makes the number of RenderFX more clear. Because each RenderFX consumes resources (a few temporary framebuffers, and a few draw calls), it's a good idea to know how many you have in your scene. You can probably have hundreds without losing performance, but you do want to be careful. FX do lots of different render operations, so they are performance intensive.
Removing FX from other game objects also eliminates some inconsistencies. For example, a Container or Layer renders a list of child objects. But how do these children blend with other objects in the scene? Well, that depends on which framebuffer they're drawn to. And that framebuffer depends on whether FX is active on the collection.
Here's a diagram showing how things can differ. I've used a DynamicTexture to keep things simple, but it uses a framebuffer just like the FX system.
In the first example, two sprites are drawn with additive blending. They combine with the background and with each other.
In the second example, two sprites are drawn on a DynamicTexture. They do not have additive blending, so one overlaps the other.
In the third example, two sprites are drawn with additive blending, but this time they're drawn on a DynamicTexture. They combine with each other, but do not combine with the background, because the DynamicTexture itself does not have additive blending enabled. When they are drawn to the DynamicTexture (which has a transparent black background, thus additive blending has no effect), they turn into texels and have no memory of what blending mode was used to draw them.
In the fourth example, two sprites are drawn on a DynamicTexture. They do not have additive blending, but the DynamicTexture does. Again, one sprite overlaps the other, but now they combine with the background. This is because the finished DynamicTexture adds to the background.
In Phaser 3, a Container draws directly to the background (as in the first example). Unless it has FX enabled, in which case it draws to a framebuffer (as in the third example). As you can see, this will produce different results! We don't want that to happen. In Phaser Beam, a Container or Layer will always draw to the background of the current DrawingContext. You can put it in a RenderFX to use a new DrawingContext with a new framebuffer.
RenderFX provides certainty around compositing. If something is inside a RenderFX, it will be drawn to another framebuffer. If not, it's drawn to the current drawing context. (There may be exceptions, such as disabling the framebuffer for a RenderFX if you want an object that only displays FX sometimes. This will be under your control, and remain predictable.)
This is why you want to understand where the framebuffers are in your game! They make a big difference.
Next Week
More work on RenderFX! I've worked through a whole lot of logic to figure out the best approach, and I'm in the middle of putting things together. Once this gets going, it'll be amazing.
Let us know what you think of all this! I'm trying to keep things familiar, but we had to change a few pieces. I think it will all work much better when we're done.
Submit your content
Created a game, tutorial, sandbox entry, video, or anything you feel Phaser World readers would like? Then please send it to us!
Until the next issue, happy coding!
Subscribe to my newsletter
Read articles from Richard Davey directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by