Phaser World Issue 213

Richard DaveyRichard Davey
9 min read

Welcome to the latest issue of Phaser World. We’ve some great community tutorials and games in this issue. It’s always inspiring to see developers achieve their dream of releasing their game on Steam (even in beta form), and we’ve another banger this week in the shape of Veliri, the awesome work of a solo dev.

♥️ Create TriPeaks Solitaire with Phaser

Discover how to build a TriPeaks Solitaire game using Phaser JS, the powerful HTML5 game framework. This step-by-step tutorial guides you through the process of creating a fully functional card game, complete with smooth animations and interactive features.

Watch Tutorial

👸 Himegotchi

You are a rich, ferocious dragon, and you have just kidnapped the most beautiful princess in the kingdom; what a great position to be in as a dragon.

The princess is a feisty one and has a lot of demands, keep her happy and don't let the princess escape!

Play Game

🧱 Tile Based Platformer Tutorial

Emanuele Feronato is back with a new tutorial all about building a tile-based platformer with slopes and no physics engine in Phaser.

Read Tutorial

🤖 AI for Texture Generation in Phaser

A recent article on Restack.io explores the integration of AI-driven texture generation within Phaser games. The piece delves into how AI can dynamically adjust game difficulty, generate procedural content, and enhance non-player character behaviors.

Read Tutorial

🚀 Veliri: Planet of Machines

TrashPony's new game Veliri offers players a unique blend of economic simulation and sci-fi warfare in an ever-evolving online world. Set on a distant planet, players take control of synthetic war machines competing for resources and influence across a dynamic battlefield.

Play Game

📺 Phaser Box2D

In case you missed the announcement, our new video covers what’s possible with the new Phaser Box2D release:


Phaser Studio Developer Logs

A brief Dev Log this week as most of the team had Monday off and forgot to write their entries! Normal service will resume next issue 😅

🧑‍💻 Rich - CTO & Founder

Zeke and I have been cracking through the Phaser issues on GitHub, preparing the v3.88 release. We’ve closed loads of them, and it’s shaping up to be a nice little ‘farewell’ version before we join Ben in completing Phaser v4. We will continue to resolve issues in v3 going forward as long as they aren’t rendering-related, which is what v4 is for, so it won’t be the final release of version 3 ever, but we really do want people to move to Phaser 4, which is why we’ve kept the API as in-sync as we possibly could.

I started the week by tidying up the Phaser Discord. Various old, zombie-like channels have been removed, some others have been renamed, and I have tidied up the channel categories a lot. Also, I ditched the emojis at the front of the channel names. It makes it look a bit less colorful, but it also makes it much easier to read on the Discord mobile app.

If you’re not part of the Phaser Discord, please join here!

I’ve also been busy working on the new Responsive Layout Template. This is a comprehensive Phaser template with a set of powerful additional functions that make it significantly easier to work across varying mobile resolutions. It supports full-screen displays, the ability to pin the UI into the corner of the screen, as well as a ‘base game size’ which you can, well.. ‘base’ everything on.

We’ve seen this question come up so many times in our Discord and from our customers that we figured it made sense to create an official response to it. There are lots of different ways to solve it, too, so I had to pick one that felt flexible enough to cater for the most common game types. It was also somewhat depressing to see that even with the Screen Orientation API that browsers provide, there is no consistency between them. And, as always, iOS makes the API next to useless by reporting incorrect dimensions during the event and not implementing the rotation locking part of the API. Such is life.

This template should be ready within the next few weeks and free to all subscribers.

🧑‍💻 Francisco - Phaser Launcher

Hello everyone,

This week, we received a new design for the Phaser Launcher, and I've been working on analyzing the code for the new design and preparing our application so it can integrate the new design without needing a complete rewrite.

Additionally, as we move closer to wrapping up the software to prepare for version 1, I’ve added a split panel to the editor, something that allows users to dynamically resize the panels, which is common in this type of software.

I’ve also been thinking about the first template we’ll provide in version 1. We believe this structure is quite user-friendly for the initial release (though it might change in future versions).

The structure is as follows:

-assets
|   |-src
|   |   |-scenes/
|   |   |    |-Main.js
|   |-main.js
-index.html
-project.zero

In the assets folder, you’ll store all your assets, and this folder will be read by the browser media. Inside src, you’ll find your game’s code, and within src/scenes, you’ll have your scenes. In src, there’s a file named main.js, which will serve as the entry point for your game’s configuration. As you might have noticed, there’s also a file named project.zero. This file is meant to be a metadata file used by our editor. We might also include a file for your game’s icons, but that’s still under discussion with the team.


Tales from the Pixel Mines

17th January 2024, Ben Richards

This week: Bugfixes! I've been working through issues with Phaser 4 beta, eliminating some interesting problems.

Bugfix 1: Filter Camera Size

The inimitable Rex Rainbow identified an issue with filters. When an object was placed at a non-integer position, sometimes its camera was 1 pixel too large on the X and/or Y axis. This was visible as a flicker as filters turned on and off: undesirable!

This turned out to be due to the getBounds() method. I was relying on this to generate a rectangle describing the object, and fit the camera to that size.

Why not simply use the object's width and height? Well, not all objects have width and height. For example, ParticleEmitter does not. It has a getBounds() method, however, which measures the region in which particles exist. For this reason, I initially chose to use getBounds() for camera focusing.

However, getBounds() isn't quite correct for this task. It's supposed to be used for axis-aligned bounding boxes, a non-rotated rectangle which contains the entire object. To measure game objects, I was setting their transforms back to default, getting the bounds, and re-transforming them back into place. In retrospect, this was a sign that it was a poor fit.

The bug itself came from floating point calculations. A sprite which should be 64x64 was sometimes measured as 64x64.000000003 (or some large number of 0s), and this was rounded up to 64x65. This can happen when the position is not comfortably aligned with integers.

I experimented with more temporary transformations, attempting to zero out the position as well. But this didn't work for all game objects - again considering ParticleEmitter, moving the emitter doesn't move the particles, so the bounds don't move. But with a sprite, the bounds do move when the position changes. This means that different objects behave differently - and that makes everything unpredictable. We can't produce confident results like this.

So I changed my approach completely. I started using width and height instead.

Didn't I just explain why those are no good? Yes. But everything is a compromise, and here I was able to find a workable trade-off. Width and height are no good for game objects like ParticleEmitter - but they are perfect for ordinary sprites, with no floating point errors.

For objects that cannot use width and height, we use a fallback: the "external context". This was already implemented for objects that have no getBounds() method. The external context is just the camera in which the external filters are rendered, often the base game screen itself.

This has two consequences.

First: objects without width and height now have no practical difference between internal and external filters, as they use the same context.

Second: some objects which used getBounds() to get a precise internal context will now use the fallback, losing that precise internal context. ParticleEmitter is affected. We don't think this will have a major impact. In fact, it may improve visual consistency, as these are usually dynamic objects which change size rapidly, and many filters take effect in steps based on the size of the context. The external context is generally of fixed size, so these filters will behave consistently.

The update also removes several function calls from the filter camera focus method. This simplifies and improves performance in the function. Performance isn't important here: filters initiate one or more draw calls, which are vastly more expensive than a single function of this kind. But the simplification is always good.

Bugfix 2: Camera Zoom Reversion

The equally inimitable Samme identified an issue with scaling filtered objects. When an object scaled on a single axis, the filter render process rendered it inaccurately. It cut off the sides on one axis, and opened margins on the other axis.

At first, I had no idea what was happening. I thought I might have overlooked something in the filter camera zoom settings, or made a mistake in multiplying matrices in the correct order. Some of my tweaks fixed one axis, but for no apparent reason, and the other axis continued being wrong.

Once I started looking at the raw matrix data, however, I noticed something. The transform matrix for the filter camera was symmetrical.

That didn't seem right. If I scaled an object by 2x1, I shouldn't see a matrix like this:

1.5 0.0
0.0 1.5

Where does 1.5 come from? Those positions in the matrix are supposed to be scale factors (when there's no rotation or skew). When scaling by 2x1, I should see 2 and 1 in those slots.

Then I remembered an old commit. Years ago, camera zoom was a single value - but then it got split up into X and Y components. The single value still exists, but it's not a permanent value; Camera.zoom just returns the average of the X and Y components.

And the average of 2 and 1 is 1.5.

Obviously, the camera was using zoom somewhere, not zoomX and zoomY as intended. And indeed, it was right there.

When we merged the new Beam renderer into Phaser a couple of months ago, we accidentally introduced a regression in the Camera code. One specific function started using zoom again. This meant that all cameras could no longer scale on X and Y axes. Fortunately, this is rare, which is why we hadn't caught it yet.

So I fixed the regression, and everything worked again.

Next Week

I've got a few more bugs to squash. Then it's time to write the Renderer Guide.

Living in Aotearoa New Zealand, I'm currently enjoying summer. But the holidays are over, and I'm picking up steam again. Phaser 4 is almost ready - and that gives us the foundations for a whole lot of cool new stuff!


Share your content with 18,000+ readers

Have you created a game, tutorial, code snippet, video, or anything you feel our readers would like?
Then please send it to us!

Until the next issue, happy coding!

10
Subscribe to my newsletter

Read articles from Richard Davey directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Richard Davey
Richard Davey