Due to the rapid pace of change some links or details may no longer be correct.
Game Resolution, the new experimental DOM Element Game Object and Forthcoming changes to Containers.
It's been a busy week. I mean all weeks are busy weeks, but you know how sometimes you're non-stop, yet it doesn't feel like you actually achieved all that much? And sometimes you're non-stop and the progression feels positive and tangible. It was one of those sorts of week.
As mentioned last issue I'm now focusing on the new Scale Manager and Facebook Instant Games integration. The first task in that journey was to sort out the issue with the resolution game config setting. Some of you may be aware you could set the resolution of your game like this:
However, had you done so, you'd have found various things then either broke or didn't work as expected. For example, input detection would now be wrong if the Camera was zoomed or rotated, the canvas itself wasn't properly resized and even with the resolution set you still got 'low res' Text rendering on High DPI devices. The first part of my week began by resolving all of these issues. The canvas is now scaled properly, input works across Cameras and you can get super-crispy Text, should you require it:
It's perhaps worth talking about what setting the resolution higher than 1 actually entails internally. The first, and perhaps most obvious thing, is that the canvas element that is created has its size set to be width & height * resolution. If you've a game size of 800 x 600 and a resolution of 2 it's going to create a 1600 x 1200 sized canvas. The canvas will be visually displayed at 800 x 600 due to CSS, but it'll actually be rendering twice as many pixels as that.
On a High DPI display this is noticeable in that it makes everything appear crisper (well, now that it's been fixed it does!). If you've ever seen comments such as 'why is my text blurry on an iPad?', this is the reason. So, setting the resolution to match the devices pixel ratio is surely always a good idea? Well, as with most things when it comes to games, "it depends". For a start, you are now asking the GPU to do twice the amount of work it was doing before. The base canvas now needs twice as much memory, as it contains twice as many pixels. If you create a lot of Text Game Objects, each of those is now using textures twice the size they were before as well.
This is assuming a device pixel ratio of 2. 2 isn't the limit, it can actually be any value. The LG Nexus for example has a DPI of 3 while the Samsung Galaxy S6 has a DPI of 4. That's literally 4x the amount of pixels required. With advancements in desktop and mobile displays, such as the new range of 8k UHD monitors, we're easily on the verge of extreme DPIs. And it's not likely to slow down either. The higher it is, the more memory is required. And when the browser is having to push twice, or maybe four times, as many pixels as before, it naturally takes longer and more power to do so.
It's the eternal battle between performance and perfection. For some games you don't even need to worry about it. If your game doesn't require a significant amount of on-screen movement, or isn't overly heavy on graphics, then you can go for visuals without too much worry. As with everything it comes down to testing. Just be aware that you can't invoke the power of high resolution with impunity. There are real costs involved and it's worth being aware of them early in the build process.
DOM Element Game Object
With the resolution issues resolved it was time to move on to a feature that Phaser would need in order to make working with Facebook Instant Games that much easier. The introduction of the new DOM Element Game Object. The concept behind it is simple. You create a Game Object that is directly linked to a DOM element. Once created you can move the DOM Element Game Object around as normal. It will respond to Cameras, have its own scroll factor, can be tweened or even have a Physics Body. But instead of using a texture to render you're actually manipulating an element within the DOM.
The following code demonstrates a simple use-case:
Here we create a new
<div> tag and give it a style and some content. It's then passed to the
add.dom factory method, which returns a DOM Element Game Object. You can now manipulate this as you would any other Game Object. Tween the alpha, bounce it around the screen, scale it, rotate it and so on. The div isn't being rendered to the canvas, it's actually a proper element in the DOM stored in a special container one level above the canvas. Manipulations to it are handled by a CSS transform matrix and a special renderer.
Of course, you're not limited to just div elements. Literally, any HTML can be used. To make it easier I've added in the ability to load HTML snippets directly in the loader:
The HTML can be as complex or as simple as you need. In the following example, you'll see a login form appear. The form, its background, and button are all pure HTML and CSS. Try entering something to 'login' (make-up whatever credentials you like):
I have added some helper methods into the API to allow you to easily select any of the elements children by ID or name and to listen for events from it. You can listen for any event the browser can emit and it'll come through via the normal Phaser event system:
Which is a powerful way to hook into the HTML being displayed.
You don't have to use Phaser to query the DOM element though. Use whatever framework you like, even jQuery. I'll provide an example showing how to get a reference to the Phaser DOM Element from your HTML, so you can control the process from both ends.
If you've run the login form example above you'll notice that the form slides away with a nice 3D perspective effect when submitted. This is handled via the
rotate3d property available. In the code below you can see we're tweening the elements of the property directly, after setting the perspective value:
You can also skew the elements. Again, this is all handled with CSS, so it'll work as long as the browser supports those transforms, which most do these days.
The elements even render in relation to the main camera. In the following example, I've created 250 divs and injected a random emoticon into each one of them. Use the cursor keys to move the camera around and notice how the divs all respond based on their scroll factor:
I picked the value of 250 because on my development system it ran well. On an older laptop it struggled a bit, where-as on my gaming rig I could push it up to 500 before it started to slow down. So the quantity of elements you can handle is based entirely on the device and browser you're targetting, as well as the content of each element. The more complex they are, the more work the browser needs to do to render them.
Stress tests like these aren't all that useful beyond just testing a concept. This feature wasn't created so you could make games with the dom elements themselves, although I guess there's nothing actually stopping you. It was created to allow you to easily ingest html into your game and display it over the top of your game content, accurately, and control it from your game code. So the same code you use to tween your sprites can also tween out your forms or social sharing buttons or whatever.
The use-cases are pretty vast, especially in the realm of IO games. Login forms, high score name entries, sharing buttons, branding links, ad displays and so on. This will be really important when it comes to pulling in the more advanced features of the Facebook Instant Games API too.
There's another area where this could be really useful as well. Remember back to earlier in this Dev Log where I talked about using high dpi text objects to keep your text nice and crisp? If you use a DOM Element instead to render your game text then it will be as crisp and clear as the browser can manage it, without having to force your entire game canvas to be high resolution as well. So you can overlay a visually beautiful UI on your game, benefiting from all the advances browsers are making when it comes to text rendering, especially with the new variable fonts, without the cost of doubling, or more, your canvas size. And those UI elements? They'll still respond to events like your camera shaking :)
There are limitations of course. The most obvious one is that all the DOM Elements are positioned over the top of your game canvas. They have their own display list and you can manage it like you would any other display object, using the setDepth method as well. But they will always appear over the top of anything in your canvas. You cannot insert them between canvas rendered Game Objects.
They also can't be masked via Phaser masks, although you can mask them using CSS. They will also only render to the main camera. I'm not yet decided how to handle the cameras. I may create a special camera specifically for DOM Elements, as right now if you have multiple cameras in your game the elements will re-position themselves for each one.
I'm very happy with how powerful they are though. They are entirely optional. If you don't enable them using a special game config flag they won't even be available, and they're easy to exclude from a custom build just like any Game Object (although they are very light-weight, not taking up much KB at all.)
Again, I cannot stress enough that these are meant to be used for UI, forms, banners and so on, not actual game "sprites". But hey, eat your heart out :) You're an inventive lot, I'm sure you'll think of interesting and varied ways to use them! They'll be available in the full 3.12 release. You can track development in the main repo and the examples being uploaded to the labs.
Forthcoming changes to Containers
This is just a heads-up that support for nested Containers will be removed from Phaser after 3.12 is released.
Let me be clear: Containers, and all of their current functionality, will remain. What will be removed is the ability to add a Container as a child of another Container.
The reasons are two-fold:
1) It will improve speed as we'll no longer need to do any iteration during matrix or bounds calculations. It will boil down to a simple boolean check and then some straight forward math if needed.
2) It will reduce API complexity internally - there are edge-cases where, for example, a deeply nested Game Object doesn't inherit the correct blend mode. Or issues like input handling needing to crawl up a container tree to find its ultimate position, all of which are adding un-needed code into the API and causing issues (both for you and me).
Containers have caused so much branching and additional work in the API that I really question their value as they currently exist. At the same time, I know some devs like using them. I think there is room to explore recoding the feature entirely, but in the meantime stopping the use of nested Containers will help mitigate a number of problems and streamline the internals a lot.
Feel free to add your thoughts to the GitHub issue. I'm still seriously contemplating rewriting Containers completely, so there's plenty of room for suggestions on how they could be implemented.