It was starting to get a bit annoying having to keep whose turn it was in our heads, and most of the time i ended trying to flick on the wrong window. To make matters worse, this week we added rank-based rounds to the gameplay: Once every player has taken her turn, the round is over and the game chooses the order under which players will play the next round based on their distance to the finishing line. Thus, we had even more things to remember, such as the current round and the players’ ranking, and were forced, as it were, to include a heads-up display (HUD).
Something simple, just like this:
Unconcerned, at this point, for the HUD’s aesthetics, we instantiated a new BitmapFont
with its default 14 pt Arial font included with libGDX and, as a first attempt, rendered the current round using the same ViewPort
that we have setup to render the world, expressed in SI units.
The result was a 2 × 1 meters text.
Besides the obvious issue with the size, that could be fixed simply enough scaling down the font, this approach presents two additional problems:
The text size is affected by the viewport’s zoom, and at every frame we have to compute the label position within the world (i.e., in meters) so that it will be shown at the screen’s top-left corner.
This is, of course, doable, but it would be better if we could use pixels to place all elements in the HUD, as this is its “natural” units.
The solution is to use separate ViewPort
objects to render the world and the HUD.
Having separate ViewPort
s works wonderfully for all our purposes but one:
we wanted the “Your Turn” label to follow the player’s puck after she flicks it.
It would seem that we have to place the label in the world at the same position as the puck, that is expressed in meters. And that would mean that we have again the problem with the text size affected by the viewport’s zoom that we mentioned above. We can not just set the zoom to one only to render the text because then its position on the screen would be completely different than that of the puck.
We realized that, instead of placing the text at the puck’s position inside the world, in meters, we can draw it after the world has been rendered to the screen and, thus, “converted” to pixels. For instance, the following figure is a view 3 meters wide and 1.5 meters tall of the world drawn to a screen of 640 × 320 pixels. After rendering, the puck is at pixel coordinates (320, 160) because the camera is centered on it.
Since the camera it is not necessarily centered around the puck, we need to convert to puck’s world position to screen coordinates and then from screen coordinates to the coordinates used by HUD’s ViewPort
.
Fortunately, ViewPort
has the project
and unproject
methods to perform these coordinates conversions.
Therefore, the most obvious code is:
This does not work: the Y axis is drawn inverted.
It turns out that ViewPort.unproject
assumes that the position is in a third coordinate system:
touch coordinates.
This coordinate system also uses pixels as units, but the most important difference with HUD is that the y-axis points downwards.
That is, the point (0, 0) is on the top-left corner when using touch coordinates but on the bottom-left with HUD coordinates.
Therefore, we must reverse the Y-axis returned by ViewPort.project
.