Responsiveness. That buzz word that has all front end web developers nodding in agreement is the process of making sure your virtual product is rendered well and usable in all sizes from the phone to the cinema screen. By using one code base and adapting to different resolutions and aspect ratios, this makes our experiences fluid and suited […]
Responsiveness. That buzz word that has all front end web developers nodding in agreement is the process of making sure your virtual product is rendered well and usable in all sizes from the phone to the cinema screen. By using one code base and adapting to different resolutions and aspect ratios, this makes our experiences fluid and suited to the device we’re using while keeping code maintainable and not branching off for every target. With the rise of Android and the many many different screen sizes, this also becomes an important factor in games.
There are a few ways developers have tackled this in the past, some are more effective than others. Letter-boxing is a common solution, centring the content with the best fit to keep the aspect ratio and just ignoring the unused areas of the screen. Stretching to fit is another option but things can get messy if you code for 4:3 and display at 16:9. The method I use is not bulletproof but has worked for the past few games I have developed with great success.
The method I use I call the “scaled stage with margins” and is exactly as it says on the tin. It may have a proper name somewhere but I haven’t found one yet.
The idea is that the stage is a defined size – for example I often use 1280px x 720px as this is a common mobile resolution. This gets scaled and centred using the same method as the letterbox idea. The main difference is that I then calculate the extents of the screen in app space and these previously unused sections become the margins. My app can extend into these margins in any way it wants. I store these margins and can access their sizes from any part of my application through my “viewport” class. I can immediately tell where the furthest visible left part of the screen is by using viewport.left and if I need the total width including margins I can use viewport.totalWidth.
The maths behind this is quite simple. First scale the stage to the size it needs to be to fit wholly on the screen without cropping any edges. This usually looks like:
container.scaleX = container.scaleY = Math.min( screenResolutionWidth / targetWidth, screenResolutionHeight / targetHeight );
Then centre the stage:
container.x = (screenResolutionWidth – (targetWidth * container.scaleX)) * 0.5;
container.y = (screenResolutionHeight – (targetHeight * container.scaleY)) * 0.5;
The margins are added by transforming the stage.x and stage.y into app space. This gives you a number which is much easier to work with when positioning things on the stage.
marginLeft = container.x / container.scaleX;
marginTop = container.y / container.scaleY;
marginBottom and marginRight are the same as the top and left one respectively as the stage is centred. Once we have this we can store some other numbers for convenience. The point (0,0) will be the top left of the actual stage as if there was letter-boxing and the margins are defined as below.
totalWidth = targetWidth + (2 * marginLeft);
totalHeight = targetHeight + (2 * marginRight);
left = -marginLeft.
right = targetWidth + marginLeft;
top = -marginTop;
bottom = targetHeight + marginTop;
There are many ways that this can be used. If I need a image that stretches to be full screen regardless of resolution. I can set it to x=viewport.left, y=viewport.top, width=totalWidth, height=totalHeight. Sorted. If I need something to stick to the top right I can set it to x = viewport.right – itemWidth, y = viewport.top. All that needs adding is a resize function that re-positions everything if the window is resized. It may not fix every solution but for many of the subtle differences in aspect ratio we see today this solution hits the nail on the head every time for me.