Text Metrics and a new Time Step loop

Published on 28th April 2017

image

Last issue we got to show you the new Render Pass object, with all of its beautiful shader goodness. It had lots of pretty demos and eye candy. Sadly, not every week can be like that, and this week is a perfect case in point. It's been busy, and I've made a lot of progress, but there's very little to actually visually show for it.

Text Metrics

If you use a Text Game Object it internally works by creating its own Canvas object, rendering the text to it, and then using that in the main render loop. In order to know what size the internal canvas should be it first performs some complicated measurements on the text style. It takes the style you've defined, applies that to another internal canvas, draws a text string to it, and then does a pixel by pixel scan in order to determine the text metrics. These are then used in the sizing calculations. The metrics are the ascent, descent and font size.

The reason we have to do this is that there is no way to measure the size of text on a canvas. Canvas has a built-in 'measureText' function, but it only returns the width, not the height, and even the width isn't usually that accurate. A few browsers have new extensions that give much better metrics back, but they are still experimental. Without the metrics, we don't know what size canvas to create to render your text on.

It's a frustrating limitation of the canvas API, and the above 'pixel by pixel' scan method is the same used in Phaser 2 and Pixi, as it's the only accurate way to do it. However, while working on an unrelated part of the Text I realized that there is no reason those values cannot be provided in the Text config object. If you're using a fixed text style, that doesn't change, then you can avoid the expensive pixel scanning and canvas creation entirely, by just providing the metrics it needs in advance:

image

In the above, you can see the metrics are provided as part of the Text Style configuration. If you do this, none of the pixel scanning takes place at all, which can save you considerable precious CPU time.

To accompany this is a new function: Text.getTextMetrics() which will return a metrics object from an existing Text. You don't have to 'guess' the values - just create one, get it looking right, and then export the data you need.

New Variable Time Step Loop

When it comes to game loops there are two trains of thought on how updates should be managed: they're either Variable, or they are Fixed. There are some classic articles on the subject, including the famous 'Fix your timestep' which advocates using a fixed timestep, always. The problem is that the article is talking about traditional environments, such as consoles and desktops. The browser is a world unto its own though, with its own rules and limitations.

So far Phaser 3 has been using a fixed timestep based on MainLoop.js. It has worked well for us so far, but I always knew it wasn't going to be enough, and more importantly - probably wasn't the optimal way to do it either. After all, what makes sense on console doesn't always apply to the browser. Ashley Gullen, of Scirra (developers of the awesome Construct game making package), wrote a really good post about the problem.

So I spent several days this week working on the new Variable Time Step loop, and it's finally finished and implemented. Everything is hooked to the request animation frame event, as before, but then it will always execute the game update and render functions once, and only once, per raf call. The delta values are passed into the game update. You can use these for your own motion calculations, and they're also used internally by things like the Animation system.

The delta value is not just a simple 'elapsed' time though. First they are clamped to always be within a sensible range. Next, they are recorded in an array which keeps the previous 30 deltas and then averages them all out to obtain the final value. This is sometimes known as a 'semi fixed timestep', but we've found it works so much better than the old one. Gone are the issues with the 'spiral of doom', or running multiple updates per frame when it's just not needed.

Please understand this doesn't mean you can't run physics at a fixed time-step, as you absolutely can - which will keep it fully deterministic. It just means the main 'heartbeat' that drives the game loop will no longer be constrained. Here's a simple demo showing the variable step in action, using a new 'Get Speed' method for sprites:

image

Making this change also fixed a bunch of issues animations had with going super-fast after a browser tab lost focus and regained it. It also reduced the math complexity within the animation update handler as well.

In true Phaser 3 fashion though I was careful to keep the new time step isolated. This means that v3 will likely ship with 2 game loop implementations: variable (which we'll make the default) and fixed. And if you need something more specific it's easy enough to drop your own in as long as it follows our interface.

So, sorry for the lack of eye candy demos this week. Well done for making it this far into the article. As a reward here is a fun little Muybridge animation test based on something I'd seen Keith Peters make :)

Next week we should be able to show off the new TileSprite implementation that Felipe has been working on. Until then, if you've any questions about Phaser 3 then please don't hesitate to reach out and ask them.

Phaser 3 Mailing List and Developers Guide

If you're interested in helping evolve the shape of Phaser 3, then please join the Phaser 3 Google Group. The group is for anyone who wishes to discuss what the Phaser 3 API will contain.

The Phaser 3 Developers Guide is available. Essential reading for anyone who'd like to help build Phaser 3.

image

Last issue we got to show you the new Render Pass object, with all of its beautiful shader goodness. It had lots of pretty demos and eye candy. Sadly, not every week can be like that, and this week is a perfect case in point. It's been busy, and I've made a lot of progress, but there's very little to actually visually show for it.

Text Metrics

If you use a Text Game Object it internally works by creating its own Canvas object, rendering the text to it, and then using that in the main render loop. In order to know what size the internal canvas should be it first performs some complicated measurements on the text style. It takes the style you've defined, applies that to another internal canvas, draws a text string to it, and then does a pixel by pixel scan in order to determine the text metrics. These are then used in the sizing calculations. The metrics are the ascent, descent and font size.

The reason we have to do this is that there is no way to measure the size of text on a canvas. Canvas has a built-in 'measureText' function, but it only returns the width, not the height, and even the width isn't usually that accurate. A few browsers have new extensions that give much better metrics back, but they are still experimental. Without the metrics, we don't know what size canvas to create to render your text on.

It's a frustrating limitation of the canvas API, and the above 'pixel by pixel' scan method is the same used in Phaser 2 and Pixi, as it's the only accurate way to do it. However, while working on an unrelated part of the Text I realized that there is no reason those values cannot be provided in the Text config object. If you're using a fixed text style, that doesn't change, then you can avoid the expensive pixel scanning and canvas creation entirely, by just providing the metrics it needs in advance:

image

In the above, you can see the metrics are provided as part of the Text Style configuration. If you do this, none of the pixel scanning takes place at all, which can save you considerable precious CPU time.

To accompany this is a new function: Text.getTextMetrics() which will return a metrics object from an existing Text. You don't have to 'guess' the values - just create one, get it looking right, and then export the data you need.

New Variable Time Step Loop

When it comes to game loops there are two trains of thought on how updates should be managed: they're either Variable, or they are Fixed. There are some classic articles on the subject, including the famous 'Fix your timestep' which advocates using a fixed timestep, always. The problem is that the article is talking about traditional environments, such as consoles and desktops. The browser is a world unto its own though, with its own rules and limitations.

So far Phaser 3 has been using a fixed timestep based on MainLoop.js. It has worked well for us so far, but I always knew it wasn't going to be enough, and more importantly - probably wasn't the optimal way to do it either. After all, what makes sense on console doesn't always apply to the browser. Ashley Gullen, of Scirra (developers of the awesome Construct game making package), wrote a really good post about the problem.

So I spent several days this week working on the new Variable Time Step loop, and it's finally finished and implemented. Everything is hooked to the request animation frame event, as before, but then it will always execute the game update and render functions once, and only once, per raf call. The delta values are passed into the game update. You can use these for your own motion calculations, and they're also used internally by things like the Animation system.

The delta value is not just a simple 'elapsed' time though. First they are clamped to always be within a sensible range. Next, they are recorded in an array which keeps the previous 30 deltas and then averages them all out to obtain the final value. This is sometimes known as a 'semi fixed timestep', but we've found it works so much better than the old one. Gone are the issues with the 'spiral of doom', or running multiple updates per frame when it's just not needed.

Please understand this doesn't mean you can't run physics at a fixed time-step, as you absolutely can - which will keep it fully deterministic. It just means the main 'heartbeat' that drives the game loop will no longer be constrained. Here's a simple demo showing the variable step in action, using a new 'Get Speed' method for sprites:

image

Making this change also fixed a bunch of issues animations had with going super-fast after a browser tab lost focus and regained it. It also reduced the math complexity within the animation update handler as well.

In true Phaser 3 fashion though I was careful to keep the new time step isolated. This means that v3 will likely ship with 2 game loop implementations: variable (which we'll make the default) and fixed. And if you need something more specific it's easy enough to drop your own in as long as it follows our interface.

So, sorry for the lack of eye candy demos this week. Well done for making it this far into the article. As a reward here is a fun little Muybridge animation test based on something I'd seen Keith Peters make :)

Next week we should be able to show off the new TileSprite implementation that Felipe has been working on. Until then, if you've any questions about Phaser 3 then please don't hesitate to reach out and ask them.

Phaser 3 Mailing List and Developers Guide

If you're interested in helping evolve the shape of Phaser 3, then please join the Phaser 3 Google Group. The group is for anyone who wishes to discuss what the Phaser 3 API will contain.

The Phaser 3 Developers Guide is available. Essential reading for anyone who'd like to help build Phaser 3.