Navigation

Details all of the changes to the input system in the up-coming 3.16 release.

Published on 3rd December 2018

Welcome to the first Developer Log in a few weeks. I took some time away from the newsletter to catch-up with other tasks, do some learning of my own (I've been addicted to VueSchools videos!) and just have a bit of a break from the relentless news and editing cycle. But, that doesn't mean development of Phaser itself stopped. Far from it, in fact. There's quite a bit of ground to cover, so let's get stuck in!

Phaser 3 Doc Jam is over!

Thank you to everyone who took part in the Phaser Doc Jam! I've now finished importing the last batch of comments into the code and it has bought the total down from 3,452 items left to document, to under 800. Which is a fantastic achievement for a few months of community effort.

As promised, I picked 6 who contributed the most towards the documentation, to receive the Amazon gift vouchers. Those are as follows:

▪ @telinc1
▪ rootasjey
▪ ajmetal
▪ cyantree
▪ Elliott Wallace
▪ STuFF
▪ @telinc1 ▪ rootasjey ▪ ajmetal ▪ cyantree ▪ Elliott Wallace ▪ STuFFAgain, thank you to everyone who took part. You've helped make a real dent in the documentation and we're closer than ever to being fully complete.

image

Phaser 3.16 Development

One of the side effects of working on the new Scale Manager is that because it's such a massive system, it touches upon a lot of areas within Phaser. One of those is, of course, the Input system. After all, it's essential that input still works, no matter how the game is scaled. While working in the Input Manager I took the opportunity to improve a number of things.

Get to the Point

The first was that I wanted you to be able to detect when a pointer left the game canvas. This was previously half-implemented, but you had to listen to an event coming from the Game instance itself, and it only worked for the mouse. Responsibility for this has now moved away from the Visibility Handler to the Input Manager, as it should be. There are two new events: gameover and gameout, both of which come from the Input Plugin in a Scene, which means you can listen for them with this code:

image

This gives you a chance to react to the primary pointer leaving your game. What you then do with that information is up to you, of course. It's worth noting that the pointer doesn't have to have left the game canvas bounds, it just isn't directly over the canvas DOM element any more. This means that if you were to open-up a DOM based overlay for your game, like an advert or text input field, that it would still fire a gameout because the pointer ain't in cansas any more (as the focus has been taken by the new DOM element).

I also added two more events, which are related to this: pointerupoutside and pointerdownoutside. As you can infer from their names, they are dispatched if the pointer is released, or pressed down, while outside of the game canvas. Internally, a pointer will no longer think it is 'up' if you press it down on your game, then drag outside the canvas, and release it. Which I know had been a source of concern for some of you. I didn't want it to dispatch the usual pointerup event, though, because it's a much more specific action that has happened.

Hence the creation of the new events for dealing with pointers outside the canvas. To go with these events the Pointer class gained the following new properties: downElement and upElement. These hold a reference to the DOM element on which the pointer was pressed down, or released. In normal activity, i.e. someone playing your game, they will both just be references to your game canvas. However, if someone presses down while outside your game, you can now check these properties to see what it was they clicked on. This should come in handy if you want to create an external DOM driven UI layer, for example.

Pointer Gestures

While working on an example for the Phaser Backers on Patreon, I was asked if I could show how you 'throw' a Game Object, by swiping on it. I had wanted to add in Gestures to the Phaser Input system for a while, so I figured it would make sense to try and kill two birds with one stone. In theory, all it would require is for a Pointer to keep track of its velocity. From that, you can pretty much determine any gesture you like.

This should be trivial, I thought. After all, to calculate it all you need is the difference between the Pointers current and previous position, right? Indeed if you do any research on this matter online, that's exactly what all the implementations suggest you do. So I added a naive velocity property based exactly on this. Strangely enough, it utterly sucked. In order to 'throw' something you need to get the velocity at the point at which the mouse button (or finger) is released. As it's the release that indicates the user wishes to fling the object. The speed at which they were moving just prior to the release determines the velocity it gets thrown at.

The issues with using a velocity based on just the previous position were two-fold. First, the browser samples DOM input events at a pretty high rate. And by their very nature, they're noisy as hell, too. What this means is that the data you get from the pointer positions is often all over the place, as it's sampling at such a high rate. In testing I found that most of the time when I released the mouse to 'throw' my object, the pointer had a velocity of zero. This was because when you move your mouse, it moves pretty fast, but when you release a button your hand is in the act of releasing the button and is usually no longer moving the mouse any more at all. It might only be a split second this happens (that your mouse remains still as you release the button) but that's all it needs, as the input sampling is so high. As far as the Pointer was concerned, it had stopped, so it had no velocity at all. Which isn't much use for throwing things.

image

It was possible, with practise, for me to time the button release while still moving. But, it felt unnatural, and the results were inconsistent. A new approach was required. My immediate thought was to increase the sample rate. So, instead of basing velocity on just the previous frame, instead I'd record a short movement history and calculate it from that instead. This presented it's own problems, however. How long should the history be? If the sample rate was too large, it would take too long for the velocity to catch-up and would 'lag' behind the Pointers true speed. If it was too short, it suffered the exact same problem as before. Plus there was the issue of recording the history every movement, which required array manipulation that I really didn't want to be doing in a potentially hot area of code.

I spent quite a while working through different ways of handling this. Phaser 2 had used the history array approach, but I was determined there was a better solution. What's more, each solution I tried varied from platform to platform. Using a high precision Steel Series mouse on my main work PC gave very different results to using the trackpad on my Macbook, which in turn gave very different results to using an iPad. Our hands just work differently in different environments. Who'd have thought?! :)

Finally, I decided to flip the problem on its head. It was no longer about recording the history of the Pointer positions, but instead about using just the current position to influence a hidden vector that each Pointer maintained. Think of it as a 'ghost' trailing the Pointer. This ghost is constantly chasing the Pointer, seeking the Pointers last known position every frame until it finally gets there. It's never too far behind, but just enough that we're able to read the data we need from it to get the angle of movement, the speed and the distance travelled. All variables we need to calculate gestures and throwing motions. The ghosts path is also passed through a Smooth Step interpolation, to reduce the noise inherent in pointer motion.

You can now access all of the following values on any Pointer instance, from any input event:

Pointer.smoothFactor is a float-value that allows you to automatically apply smoothing to the Pointer position as it moves. This is ideal when you want something smoothly tracking a pointer in a game, or are need a smooth drawing motion for an art package. The default value is zero, meaning disabled. Set to a small number, such as 0.2, to enable.
Config.inputSmoothFactor is a new property that allows you to set the smoothing factor for all Pointers the game creators. The default value is zero, which is disabled. Set in the game config as input: { smoothFactor: value }.
Pointer.velocity is a new Vector2 that contains the velocity of the Pointer, based on the current and previous positions. The velocity is smoothed out each frame, according to the Pointer.motionFactor property. This is done for more accurate gesture recognition. The velocity is updated based on Pointer movement, it doesn't require a button to be pressed first.
Pointer.angle is a new property that contains the angle of the Pointer, in radians, based on the current and previous positions. The angle is smoothed out each frame, according to the Pointer.motionFactor property. This is done for more accurate gesture recognition. The angle is updated based on Pointer movement, it doesn't require a button to be pressed first.
Pointer.distance is a new property that contains the distance of the Pointer, based on the current and previous positions. The distance is smoothed out each frame, according to the Pointer.motionFactor property. This is done for more accurate gesture recognition. The distance is updated based on Pointer movement, it doesn't require a button to be pressed first.
Pointer.motionFactor is a new property that controls how much smoothing to apply to the Pointer positions each frame. This value is passed to the Smooth Step Interpolation that is used to calculate the velocity, angle and distance of the Pointer. It's applied every frame, until the midPoint reaches the current position of the Pointer. The default value is 0.2.
Pointer.time is a new property that holds the time the Pointer was last updated by the Game step.
Pointer.getDistance has been updated. If called while a button is being held down, it will return the distance between the Pointer's current position and it's down position. If called when a Pointer doesn't have a button down, it will return the historic distance between the up and down positions.
Pointer.getDistanceX is a new method that will return the horizontal distance between the Pointer's previous and current coordinates. If called while a button is being held down, it will return the distance between the Pointer's current position and it's down position. If called when a Pointer doesn't have a button down, it will return the historic distance between the up and down positions.
Pointer.getDistanceY is a new method that will return the horizontal distance between the Pointer's previous and current coordinates. If called while a button is being held down, it will return the distance between the Pointer's current position and it's down position. If called when a Pointer doesn't have a button down, it will return the historic distance between the up and down positions.
Pointer.getDuration is a new method that will return the duration the Pointer was held down for. If the Pointer has a button pressed down at the time this method is called, it will return the duration since the Pointer's was pressed down. If no button is held down, it will return the last recorded duration, based on the time the Pointer button was released.
Pointer.getAngle is a new method that will return the angle between the Pointer coordinates. If the Pointer has a button pressed down at the time this method is called, it will return the angle between the Pointer's downX and downY values and the current position. If no button is held down, it will return the last recorded angle, based on where the Pointer was when the button was released.
Pointer.smoothFactor is a float-value that allows you to automatically apply smoothing to the Pointer position as it moves. This is ideal when you want something smoothly tracking a pointer in a game, or are need a smooth drawing motion for an art package. The default value is zero, meaning disabled. Set to a small number, such as 0.2, to enable. ▪ Config.inputSmoothFactor is a new property that allows you to set the smoothing factor for all Pointers the game creators. The default value is zero, which is disabled. Set in the game config as input: { smoothFactor: value }. ▪ Pointer.velocity is a new Vector2 that contains the velocity of the Pointer, based on the current and previous positions. The velocity is smoothed out each frame, according to the Pointer.motionFactor property. This is done for more accurate gesture recognition. The velocity is updated based on Pointer movement, it doesn't require a button to be pressed first. ▪ Pointer.angle is a new property that contains the angle of the Pointer, in radians, based on the current and previous positions. The angle is smoothed out each frame, according to the Pointer.motionFactor property. This is done for more accurate gesture recognition. The angle is updated based on Pointer movement, it doesn't require a button to be pressed first. ▪ Pointer.distance is a new property that contains the distance of the Pointer, based on the current and previous positions. The distance is smoothed out each frame, according to the Pointer.motionFactor property. This is done for more accurate gesture recognition. The distance is updated based on Pointer movement, it doesn't require a button to be pressed first. ▪ Pointer.motionFactor is a new property that controls how much smoothing to apply to the Pointer positions each frame. This value is passed to the Smooth Step Interpolation that is used to calculate the velocity, angle and distance of the Pointer. It's applied every frame, until the midPoint reaches the current position of the Pointer. The default value is 0.2. ▪ Pointer.time is a new property that holds the time the Pointer was last updated by the Game step. ▪ Pointer.getDistance has been updated. If called while a button is being held down, it will return the distance between the Pointer's current position and it's down position. If called when a Pointer doesn't have a button down, it will return the historic distance between the up and down positions. ▪ Pointer.getDistanceX is a new method that will return the horizontal distance between the Pointer's previous and current coordinates. If called while a button is being held down, it will return the distance between the Pointer's current position and it's down position. If called when a Pointer doesn't have a button down, it will return the historic distance between the up and down positions. ▪ Pointer.getDistanceY is a new method that will return the horizontal distance between the Pointer's previous and current coordinates. If called while a button is being held down, it will return the distance between the Pointer's current position and it's down position. If called when a Pointer doesn't have a button down, it will return the historic distance between the up and down positions. ▪ Pointer.getDuration is a new method that will return the duration the Pointer was held down for. If the Pointer has a button pressed down at the time this method is called, it will return the duration since the Pointer's was pressed down. If no button is held down, it will return the last recorded duration, based on the time the Pointer button was released. ▪ Pointer.getAngle is a new method that will return the angle between the Pointer coordinates. If the Pointer has a button pressed down at the time this method is called, it will return the angle between the Pointer's downX and downY values and the current position. If no button is held down, it will return the last recorded angle, based on where the Pointer was when the button was released.Which is quite a lot! You have full control over this. If you don't want any smoothing at all, you can disable it from a single config property. If you find you want things smoother (perhaps you're making an art package and need smoother brush movement?) then you can increase it, too. I'm very happy that these new properties will allow you to make any type of gesture you need and be pretty creative when it comes to pointer input in your games.

Using the new features in 3.16 I created a Pointer Monitor class, which you can see below:

image

The idea is that you create a Pointer Monitor in your code and then it can listen for swipe events. Meaning you can do this:

image

And all of a sudden, you've things being swiped and thrown all over the place :) The full code for this specific example is available today for Phaser backers on Patreon and will be released into the public domain in a few months time.

3.16 has also gained a really neat new blend mode called 'ERASE', which I'll cover with full examples next issue. I'm still working on packaging up a final release, but there's still quite a bit to do before I'm happy with it. Part of me is tempted to release it 'as is' just to get it out into the wild, but I'll make a firmer decision on that towards the end of this week.

In the meantime you can find all the latest code in the master branch on GitHub. Every new feature being added and every bug being fixed, is of course free and public and you can test them all out right now if you don't want to wait for a final release :)