Scaling Games for Multiple Devices (iOS)

Monkey Forums/Monkey Programming/Scaling Games for Multiple Devices (iOS)

SHiLLSiT(Posted 2013) [#1]
I know this has been brought up in several threads already, but I haven't been able to find a solution that works for me.

I'm currently using DruggedBunny's Autofit module which works very well for scaling the screen. My issue is that since all the iOS devices do not have the same aspect ratio, I get black bars - which I want to try to eliminate. On the 3GS and iPhone4, I can simply stretch the width of the game to the edges and have minimal/acceptable distortion. However, on the iPhone5 the black bars are much thicker which require the width to be stretched much further causing noticeable distortion. What has everyone else done to combat multiple screen resolutions?

Note: we're building for the iPad retina resolution (2048x1536) and scaling down the art. For slow devices, we swap in art that is 50% the size of the iPad art.


rIKmAN(Posted 2013) [#2]
The way I am doing this is to have 2 sets of background images.

I have 1 set at 1136x640 and 1 set at 1024x768.

I am not targetting 3GS so for me the 1136 works fine for iPhone5, I offset the image and show the centre of the image for iPhone4 (960x640), and the 1024x768 scales perfectly to iPad - I also use autofit.

I detect the device/resolution at the start of my program and load the correct images accordingly.


Nobuyuki(Posted 2013) [#3]
Multiple options for mitigation factors:

1. Set a maximum distortion amount allowable from the standard scaling factor and hack it into AutoFit
2. Add your own custom letterboxing borders
3. Remove letterboxing entirely and let the background graphics bleed through
4. Make hud elements and menu elements aligned by real device percentages


SHiLLSiT(Posted 2013) [#4]
Prior to Autofit, I had been trying to simply let background images "bleed" over into the areas where the black bars would be placed. However, I was having issues with object placement that depended on the width of the screen. For example, I have a semi-transparent rectangle being drawn for my in-game console background that's drawn from 0,0 to the width of the screen. Since my target resolution width is 2048, the box stops short of the edge of the screen when I'm on devices other than the iPad. How would I go about fixing this?


ElectricBoogaloo(Posted 2013) [#5]
Here's my method.
Right at the top I set 2 globals, jscrw and jscrh (Jay's SCReen Width and Jay's SCReen Height)
I also use rawscrw and rawscrh, which are my targeted virtual resolution.

-=-=-
During OnRender I find an approximately decent scaling amount, which gives a nice suitable render style for the target resolution, given the current DeviceWidth() and DeviceHeight() values. This is complex mathy stuff, and is all kinds of messy!
-=-=-

But then, I throw the results back into jscrw and jscrh.
jscrw=DeviceWidth()/Scale
jscrh=DeviceHeight()/Scale

From there, my game code works with 0,0 to jscrw,jscrh and everything inbetween.
This means that when I shove a GUI element at jscrw,jscrh, it'll be on the bottom right corner of the screen no matter what the user's resolution.
Learn to use scales, instead of exact positions, and scale everything to the jscrw,jscrh numbers, and you'll be able to get some nice gui stuff working.

If you need a good practice/tutorial/homework exercise which deals with scalings, I'd recommend trying to create a multi-resolution/ratio/orientation based Testcard pattern, which attempts to scale to any given resolution. It's a tricky thing to figure out in your head, but once you've accomplished it, the methods can be reused in any number of situations. Example here.. Just click the AGameAWeek logo on the game's main menu, then play with all the different resolutions.
If you can learn to do something like that, you'll be pretty much ready for anything different resolutions can throw at you!


Nobuyuki(Posted 2013) [#6]
shillsit: You should determine the scale factor and the translation amount from AutoFit and start drawing your rect at the true zero, inverting the scale factor and subtracting the translation amount. Gonna toot my own horn and note that AutoScale already handles this for you with DeviceX() and DeviceY() functions. The relevant code to steal is something like this:



Replace the variables for scale and translation with the appropriate ones from AutoFit, or steal it from the global matrix using GetMatrix(). Or just use AutoScale, which you can get here: http://www.monkeycoder.co.nz/Community/post.php?topic=4963&post=55649 ;)

Then, to draw a rect covering the entire screen, just specify DeviceX(0),DeviceY(0),DeviceX(1),DeviceY(1) as the dimensions of the rect. If I remember correctly, that should do the job...


SHiLLSiT(Posted 2013) [#7]
Nobuyuki: AutoScale seems to be more of what I was looking for! Thanks for pointing me to this. However...I'm having an issue. Using the DeviceX(0) function, it properly places the rectangle at the true 0,0 but when I use DeviceX(1) the rectangle stops short of the right edge of the screen...DeviceY(1) works fine.

Here's a screen shot:

Edit: If I add the SlideAmount to DeviceX(1) I can get the rectangle to scale the entire window. Is this intended behavior?


Nobuyuki(Posted 2013) [#8]
SHillSiT: My bad. Set the width and height to DeviceX(1)-DeviceX(0), DeviceY(1)-DeviceY(0). That should compensate for the slide amount.

You'll notice that the width has to account for the distance it traveled horizontally from (0,0) because the width value of the rect is still scaled. Absolute point distance of DeviceX() and DeviceY() when the value is 1 still specify the lower-right point on the screen. (try it for yourself using DrawCircle().)

Placement of textures shouldn't be affected by this, but of course, their scale will be affected too, so if you want to draw an image at the original scale on all devices, specify 1/ScaleFactor when blitting to get consistent size at all resolutions. Remember to use wisely and test it so it isn't too crowded at small resolutions!


SHiLLSiT(Posted 2013) [#9]
Awesome! It works great now. However, I'm have one more issue:

My engine uses a camera so that I can pan around the gameworld. After getting AutoScale configured correctly, I notice things that need to be relative to the camera aren't working correctly. After toying around with it, I realized the 0,0 that the camera was originally being placed at isn't the top-left corner of the screen anymore, but rather offset by the slide amount to the left. Here's how it looks:



I've tried placing the camera at DeviceX(0), but it doesn't seem to do anything. Any ideas?

EDIT: okay it DOES do something - it scrolls the camera to the point in the game world as intended. But I'm confused about how to get things lined up again correctly. In the image, you can see the console is offset and the redline marks the camera edge which should be right up along the left edge of the screen.


Nobuyuki(Posted 2013) [#10]
What is your camera code like? Camera should respect the virtual window size you set when you initialized AutoScale, and everything after ScaleScreen() "lives" within the virtual window now. For example:



Notice that I wrapped the Translate() function between push and pop matrix functions. This is important, because if you don't do this, your global matrix commands will override the screen borders of AutoScale (and probably also autofit). When properly wrapped, the Translate() function moves the camera exactly 128px at native scale, and of course it changes based on the global scale.


SHiLLSiT(Posted 2013) [#11]
All of my drawing functions are wrapped between the Push and Pop functions.

My camera class is just a simple class that has "x" and "y" variables, which just defines how far away the camera is from the world origin. So if I want to pan the camera, I simply add X amount to the camera's X and everything is drawn with an offset.

EDIT: Nevermind, I figured it out. I was drawing the console stuff in the wrong position.