Phaser World Issue 205
Welcome to another packed issue of Phaser World. There’s a new release of Phaser Editor available, lots of progress being made on Box2D and the Phaser Beam renderer enters the final stages before integration. Busy, busy, busy! Enjoy …
🌟 Phaser Editor v4.6.0 Released
This brand-new version includes a powerful built-in Tilemap Editor.
Available for immediate download is Phaser Editor v4.6.0. This introduces a long-awaited and powerful feature: A built-in Tilemap Editor. The new Tilemap tools are directly integrated into the Scene Editor, allowing you to combine Tilemap Layers together with Game Object layers simply and effectively.
Watch a video and read more here
🎮 President 47
Regardless of who you vote(d) for, you can't beat a good bullet-hell action game!
President 47 from 2kblater Games is a wry take on recent Whitehouse events. But most of all, it's an action-packed bullet-hell in the Vampire Survivors school of blasting and is available on Steam.
Watch the game trailer (if you can stomach any more politics right now)
😰 101 Games Challenge
The challenge continues with game two, Bolaz, available to play and download.
Emanuele Feronato has set himself an ambitious task: To create 101 games as a solo developer. There are some rules around the games. Such as: all games must be actual playable games, not just prototypes. AI is not allowed to be used to create short-cuts and, most importantly for Phaser developers: the source code and project files for each game must be made available.
😊 MobileFuse hiring Phaser Developer
Experience with Phaser and HTML/CSS? Join a powerful creative team.
MobileFuse (www.mobilefuse.com) is a leading mobile advertising network. We are currently looking to hire an outstanding Freelance Creative Developer to support our growth. We are looking for candidates who have a desire to learn new skills and improve existing ones. The perfect candidates will pay attention to details and take great pride in their work.
You will be joining a team of designers who are the leaders in our industry. Take advantage of this opportunity to use your talents and creativity to design and develop captivating ads for mobile devices. Our world revolves around cutting-edge technology and finding the best talent possible. If you believe you match these qualifications, then we can’t wait to speak with you!
Phaser Studio Developer Logs
🧑💻 Rich - CTO & Founder
Hi everyone. Last week was quite intense, to say the least! From a company point of view, one of the most exciting things that happened was that we hired our permanent CEO. This also means that our interim CEO, Brian, departed for pastures new (and non-techy) - he has been invaluable to me over the past 6 months, and I’ve loved working with him. Yet, in order to move forward, we knew we needed a permanent CEO. After lots of interviews and talks, this process is now complete, and I look forward to him introducing himself next week.
On the coding front, I worked on the Box2D project. While Pete and Zeke kicked the tires from all angles to make sure nothing fell off, my task was to code a Phaser interface to the API. Unlike with Arcade or Matter, this interface is a much more ‘light touch’ approach. It wraps common things you’ll need to do, such as create a physics body from a Sprite, or pair a body with a Sprite and have them remain in sync as the physics world steps. These are available as functions you import:
const block = this.add.sprite(640, 90, 'block');
const world = CreateWorld({ worldDef: b2DefaultWorldDef() });
const worldId = world.worldId;
const box = SpriteToBox(worldId, block, {
restitution: 0.7,
friction: 0.1
});
AddSpriteToWorld(worldId, block, box);
Above, we create a basic Phaser Sprite, a Box2D world, and a body from this Sprite. The two are linked, so the sprite renders as the body updates. Repeat that for hundreds of bodies, and voila:
However, it’s all open and optional. If you don’t want to use the wrapper, you don’t need to. You can just look at the code for it and implement your own approach. Perhaps you may want to create your own extended Sprite class that has a Box2D body as part of it? It’s entirely up to you. We’re not going to force you down a ‘plugin only’ route.
Another task I completed was porting the debug renderer to Phaser Graphics, so you can now render all the examples via a single Graphics instance, such as these tic-tac-like capsules:
You’d rarely want to use this ‘in game’, but for debugging it’s invaluable.
With the documentation in place and the examples pool growing by the week, we’re very close to release now. The aim was always by the end of November, and currently, it feels like we can achieve that.
🧑💻 Pete - Box2D
Now that the implementation has (nearly) stabilised, we've rolled out a Phaser Box2D API. Following our earlier approach, we used the C original as our guide, and I'm pleased to say that the full original API is available. We've kept all the original function names and stuck as close as possible to the original parameters. For the few features that don't quite fit our JS version, we've added wrapper functions that give friendly console warnings.
The great thing about keeping so close to the original is that you can use the official Box2D documentation to learn about the engine and its physics simulation.
On the documentation front, we've added JSDoc comment headers to all 358 functions, complete with a quick summary, detailed description, and annotated parameters and returns. I'll be documenting the types today, and then we'll do several review passes to ensure everything's spot-on.
In the examples section, we've added a tiny Platform Character demo featuring a single (fixed rotation) capsule that responds to keyboard inputs for movement and jumping.
Building on that, we've created "Run and Gun" – a basic scrolling platformer where you can shoot at the mouse position. Right now, it uses a static box for each tile, which gives us that familiar 'tile corner hop' when crossing boundaries. I'd like to work on a helper class for creating edge chains or possibly a grouping algorithm to smooth this out by combining adjacent platforms. Since the mouse aiming was a bit tricky to get right, I've wrapped up some handy coordinate conversions as ConvertWorldToScreen
and ConvertScreenToWorld
.
We've also added an interesting new example called Joint Smasher. It demonstrates breakable joints with 400 boxes arranged in a square grid using Weld Joints, which get repeatedly dropped onto a heavy vertical pole. The joints get progressively weaker with each drop, leading to some pretty cool fragmentation effects, though it is quite bouncy. For a more rigid version, we'd want to group all shapes into a single body.
To make life easier, I've added a CreateChain
helper function that lets you quickly create a series of linked objects in a line – perfect for chains, ropes, or bridges. What used to be a bit fiddly is now nicely straightforward.
And, of course, the week included our usual round of bug fixes, code polishing, and general preparation work to get everything launch-ready!
🧑💻 Arian - Phaser Editor
Hello friends
What a week! We have finally released Phaser Editor v4.6.0, a version that brings a built-in tilemap editor. We have been talking about it for the last four weeks. In the last few days we finished writing the documentation and we have made a new project template with an example of the new tilemap editor. I recommend you to take a look at it, it appears right in the examples section in the project page of the editor:
Now comes one of the most exciting parts, what will be our next project?
🧑💻 Zeke - Box 2D
Worked on two Box2D examples and one game demos this week. After creating the asteroids demo last week, I thought it would be good to break down the full game into smaller, easier to understand components. Here are the demos:
Ship movement
Creating ship movement with Box2D would be the first step in creating a similar top down shooter.
Collision filtering
Filtering collisions is necessary to ensure the right physics bodies are allowed to collide and respond. For example, you wouldn’t want to be able to shoot and hit your ship with your own bullets. Or maybe you do? In the asteroids example last week, asteroids can only collide with bullets and the player ship, while the ship could not collide with the bullets fired.
Suika Watermelon game demo
Do a quick Google search for "Suika" and you’ll be able to play the original full game on the web. In the game demo, you’ll see how to create a basic playable version of the game with Box2D.
🧑💻 Francisco - Phaser Launcher
¡Hola a todos!
This week, I've been focused on enhancing the Phaser Launcher, particularly in the landing section, which serves as the user's entry point to the app. Additionally, I worked on the functionality for importing and creating new projects.
While handling these tasks, I realized that a complete workflow was starting to come together, closing an exciting loop in the development of this tool. So, I decided to take the next step by setting up a simple server and launching a single project through it. The tool is shaping up really well, and we are excited about its potential!
🧑💻 Can - Discord Activities Tool
This week, I continued refining the Discord Activity tool, focusing heavily on enhancing user experience. One major task that consumed much of my time was dealing with Apple’s code signing and notarization process. Thankfully, working with Tauri made it significantly smoother compared to other development tools. Tauri’s streamlined workflow provided a much-needed boost in navigating Apple’s often complex requirements.
After thorough testing and analysis with Rich, we decided that the current UI needed a change for improved user experience. This led to exploring and implementing a new interface design that better meets user expectations.
On top of these significant updates, I added a few small but impactful features, such as the ability to open the project folder automatically after successful creation. These enhancements aim to make the tool more intuitive and user-friendly.
That’s all for this week’s progress. Keep your pixels sharp, everyone!
Tales from the Pixel Mines
November 8th 2024, Ben Richards
This week, I've hit a big milestone: full implementation. That means I've implemented everything on our roadmap for the new Beam renderer. It doesn't mean it's finished, as there's a lot to test and tidy up. We'll probably find things we've overlooked, or awkward features that need a little streamlining. But it's another big step forward!
The last pieces of the puzzle involved the last work on Filters, and a new implementation of the Shader
game object. It also involves changes to the GLSL file loader.
Full Implementation
Beam is a complete rewrite of Phaser's WebGL render system. This makes it more robust and efficient, but it also means that everything that renders has to be rewritten. That in turn gives us opportunities to rethink everything. And that all took a while.
We took care to ensure that things work the same wherever possible. We view the Phaser API as an asset, and stability is important. The Technical Preview program helped users test compatibility, and we're pleased that very sophisticated games can still work with zero changes.
However, anything that touches the render system will need updates. The Filter, and Shader systems in particular are quite different. Filters take the place of FX and Masks. DynamicTexture
and RenderTexture
objects have simplified their command sets, and require you to call render()
before they draw anything.
Further, we've chosen to omit some items from Beam. Several filters have been removed, as we believe they are easy to implement as a flexible combination of other filters. The Mesh
game object and its subclass Plane
have been removed, as we intend to rethink our approach to 3D rendering. BitmapMask
is removed, and GeometryMask
no longer has any function in WebGL, but does still work in the Canvas renderer. FX and Masks are gone from game objects, and are now surfaced in the Filter system, available through cameras or the RenderFilters
game object container.
The Pipeline concept has been retired. Beam uses a collection of types including DrawingContext
to track drawing requirements, ProgramManager
to provide shader programs, and RenderNode
as a superclass for various discrete rendering tasks.
This should all give you a picture of where you should expect changes, and where you should expect things to just work as normal.
Filters
Bloom Filter and ParallelFilters
Last week, I talked about the Bloom filter, and what choices we faced when creating it.
Well, this week I decided that you should make the choices. So I removed the Bloom filter. It's replaced by a special filter called ParallelFilters
. This surfaces the branching logic I discussed last week, splitting the render path into two lists of filters, then blending them together again at the end.
Here's how I made the above full-screen bloom effect:
const parallelFilters = this.cameras.main.filters.internal.addParallelFilters();
parallelFilters.top.addThreshold(0.5, 1);
parallelFilters.top.addBlur();
parallelFilters.blend.blendMode = Phaser.BlendModes.ADD;
That's it! As you can see, it's a really simple effect, but you can tune it yourself. Tweak the threshold to control highlights, lower or raise the blend amount, use a different blend mode, control the blur using the same parameters as the Blur filter, or even add other filters to further take control of the effect.
Blend Filters to Blend Filter
I made an internal change to the Blend filter. My first pass used a dedicated shader for each blend mode. But I quickly realized that this was just inflating package size with a lot of boilerplate.
So I switched to a single shader. This has all the blend modes defined as separate functions. How do we tell which one to use? Simple: we use the shader additions system. When we run the shader, we update its additions to include '#define BLEND ' + blendModeName
. The system is smart enough to know if this changes, and compiles a new shader program only when necessary.
Shader additions are super useful, once you get familiar with their use. You don't have to write an entire shader for every new function - you just add code into an existing framework.
GLSL File Loading
In Phaser 3, GLSL code is loaded as a single program, with vertex and fragment shader code. But because our handling of GLSL shader code is so much more flexible in Beam, we needed to rethink our file loading strategy.
I've changed the GLSL loader to a simpler text system. It no longer treats files as vertex or fragment shaders. And it no longer parses "bundle" files consisting of several shaders. Instead, it loads a single file, which can be a complete vertex or fragment shader, or a snippet of code for use in a shader addition.
This makes it easier to develop GLSL code for flexible use. Bundle files weren't in any recognized syntax, so you wouldn't get warnings about mistakes in your code. I've been careful to make systems which use valid GLSL for Beam, so your editor doesn't have to cope with any special syntax. (The #pragma phaserTemplate()
syntax is valid GLSL, even if it doesn't do anything.)
Shader Game Object
What's a Shader? It's a slightly confusing name, but here's what it does: it applies a shader program to a quad. That's all.
Well, we have to define a few more rules before we can make everything simple. Here's what we settled on.
A Shader has default vertex and fragment shaders. You can override or apply additions to either. We expect that you'll only need fragment code.
A Shader game object is a quad. Its transforms behave just like a Sprite, and it uses the same code behind the scenes.
You can use a Shader anywhere you'd use a Sprite. You can apply filters to it, or set blend modes (previously impossible). The only thing you can't do is set alpha or tint, because those are actually shader inputs and your shader needs to control them itself.
Shader's vertex shader receives two attributes: position and texture coordinates. Texture coordinates are made available in the fragment shader as
outTexCoord
. We expect this to be the primary driver of generative shader textures.Texture coordinates default to describing a rectangle from [0, 0] to [1, 1], but each vertex can be set manually.
These rules mean that you can use a Shader just like a texture. The freedom to change texture coordinates means you can use frames from a texture atlas, or scroll around a generative texture space.
The API of Shader has changed, but remains as compatible as possible.
scene.add.shader(key, x, y, width, height, textures, textureData)
(Phaser 3)scene.add.shader(config, x, y, width, height, textures)
(Phaser Beam)
I've removed TextureData
, as it only changed some texture settings you can edit yourself.
The main change is going from key
to config
. The key
string fetched a GLSLFile from the Shader Cache, which as you'll recall, was a fully compiled shader. The config
object lets you define which parts to combine into your own shader. You can reuse additions and entire shaders in different configs.
But if you don't need that power, I've left a backwards compatible option. If config
is a string, instead of a configuration object, it assumes it's the key for a fragment shader, and creates a config automatically. This is usually what you want: just load your own fragment shader and use that.
We encourage you to switch to the config
object when Beam is released, as this provides a convenient place to define shader uniform handling. This is necessary for any form of animation or interaction in the shader.
Phaser 3 handled several uniforms automatically. These correspond to uniforms once used by Shadertoy. However, Shadertoy has changed its interface, so these are no longer valid, and I've removed them in Beam. This also means Shader is no longer calculating mouse input for all your shaders that don't use it. We will create examples to demonstrate how to achieve common tasks, such as using time to animate shaders, or use a pointer in a shader. It's usually just a boilerplate line or two in your uniform handling.
But enough technical stuff. How about some examples?
Random Noise
Random noise has many uses, so here are a few different kinds. These varieties use a single Shader config, modified with different additions to create different shader effects.
Gradient Noise
Random noise is cool, but it has no structure or continuity, so it's not useful for many applications. Enter Perlin noise, a way to use random noise to construct smoother textures. There are several improvements on this technique, such as Gradient and Simplex noise. This example has a Shader rendering such structured noise, using renderToTexture
to make it available to other objects, and plugging that texture into a Displacement filter on a background sprite. You could actually combine the Sprite, Shader, and Filter together into a single shader to be more efficient, but this is a quick way to experiment with effects.
Clouds and Holograms
This is a quick hologram effect, and a not-so-quick cloud effect. It uses the same gradient noise as the last example, but I adjust the perspective, take multiple samples, and combine them to make something that looks volumetric. (It's not really, but it looks pretty close, no?)
Oh, the Phaser logo here is taken straight from a texture atlas. That was previously impossible. But with texture coordinate control, we have greater powers.
Next Week
Well, looks like it's time to test and trim! We don't have any more additions planned for the initial Beam release. That means I need to go back and pull out all the old systems that were left in Phaser while I developed Beam (the lessons of the past are always valuable).
We also need to update all the Phaser examples, and make sure they still work, or that the fixes are well-documented. That's going to take a while.
But soon we'll be done, and Beam will be ready for alpha. When? I can't say! But as soon as possible.
Until next week, be excellent to one another.
Share your content with 18,000+ readers
Created a game, tutorial, code snippet, 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