Today’s tutorial comes to you courtesy of Rob Miracle, a member of Corona Labs’ Developer Relations team. Rob has been a Corona developer for over 18 months and has released over a dozen titles using the platform.
A very common question people ask when developing mobile apps is, “How do I support all of these different devices?” On the iOS side alone, we now have three basic “shapes” that we have to deal with, in addition to various resolutions. Factor in Android and things become even more crazy.
Some screens are wider while others are more narrow. If we take resolution out of the equation, its easier to visualize the screens. Corona makes it easy to take resolution out of the picture using Dynamic Scaling. With Dynamic Scaling, you can use a common set of screen coordinates and Corona will automatically scale the text and graphics for different resolution screens. It can scale upwards or downwards depending on your starting point. It also can substitute higher resolution images when it needs to scale up. This is all managed by a Lua file in your project folder called config.lua.
Since available resolutions vary considerably, it’s helpful to use the same scale for each device. It doesn’t matter if you’re on an iPhone 3GS at 320×480 or a Retina iPad at 1536×2048, the location (0,0) represents the top-left corner and (320,480), in vertical portrait mode, is the bottom-right corner. The screen center is (160,240). Each point, in this case, is one pixel on a lower-resolution device like the 3GS, which has a native screen resolution of 320×480, while each point is four pixels on a Retina iPad. Don’t worry about the math — Corona will handle it for you.
Basic Screen Shapes
Consider this image at 360×570 in overall width and height. We are going to make our image backgrounds bigger than the physical screen. Why? Because we need to accommodate different devices. The inner rectangle is a 320×480 block. Parts of the image at the top, bottom, left and right will reside off screen. When we switch to a “tall” device like the iPhone 5 or most Android phones, it will use the full height of the image, but a portion of the left and right sides will reside off screen. If you have an iPad that is closer to square shape, it will crop off the top and bottom area but use the full width of the image. The portions that reside off screen are referred to as the “bleed area”, a term from traditional printing where ink would bleed off the edges of the page.
Now that we understand and can see, visually, how the background will be used, let’s set it up in your config.lua file. Since this is a standard Corona Lua file, we can use several API calls to accomplish the following:
- read the pixel width/height of the device
- use math for various calculations
- read the device model number
- perform string manipulations
- use conditional “if-then-else” logic
The first thing we’ll do is determine if the device is an iPad. We can get the model number of the device by using the following Corona API call.
Currently, if you’re testing on an iPad, it should simply return the string iPad, but Apple might change this in the future and return something like iPad Retina. So, let’s use this information and set up an if statement to manage that block:
Using Lua’s string.sub() function, we read the device model and look at only the first 4 characters, in case Apple ever decides to tack something on the end. In this case we’re looking for iPad, and if that’s true we set up the application content table to include the width and height parameters of 360 and 480 respectively. We use 360 because our sample background is 360 pixels in total width and we want it to span the full width of the iPad screen. Please note that the config.lua file always is always configured as if you’re doing a “portrait” app. If you’re designing an app in “landscape” mode, you should still specify width and height in the portrait orientation — Corona will manage the translation for you.
With 360 as the width, now we need to calculate the height that will fit the iPad’s screen shape. The iPad screen is 768×1024, or 1536×2048 for the Retina iPad (exactly double, thus the same ratio). First, divide 1024 by 768 to get 1.33. Then, to get the height, multiply 1.33 by 360 for a result of 480.
1024 / 768 = 1.33 --screen ratio (height:width) 1.33 x 360 = 480
This time we are only going to look at the first 2 characters of the model string. Why? This catches both the iPhone and iPod Touch models! We also detect the actual pixelHeight of the devices — this display property is included in Corona build #971, so please download it if you haven’t already. If the pixel height is greater than 960 (the height of an iPhone4) then we know the device is an iPhone 5 or latest-generation iPod Touch. Since this is a 640×1136 device when we scale it to 320 pixels, its a 320×568 device.
As stated above, our preference is for the sample background height to span the screen height of the iPhone 5. The native height of the iPhone 5 screen is 640×1136. Since our “inner rectangle” on the background is 320 pixels wide, the math is simple:
320 / 640 = 0.5 --inner rectangle width / device pixel width = factor 1136 × 0.5 = 568 --device pixel height × factor = config.lua 'height' setting
Notice that our sample background image is 570 pixels in height. As such, a mere 1 pixel will “bleed” off the top and bottom of the iPhone 5 screen!
iPhone 3,4 and Older iPod Touch
Since we’ve handled the iPhone 5 above, any model string that starts with iP will be a traditional iPhone or iPod Touch with a screen size of 320×480 or 640×960. Conveniently, we can use the width and height of our “inner rectangle” in this configuration block: 320 and 480 respectively.
Android, Kindle Fire, and Nook
Finally, we need to separate the 16:9 Android devices from the 5:3 devices (Nook and Kindle Fire). The 16:9 devices are at a 1.777:1 ratio. The Nook and Kindle Fire are closer to 1024×600 or 1.7:1, so we can use the pixelHeight and pixelWidth to calculate the ratio for the screen size. If it’s greater than 1.72, we set up a screen similar to the 16:9 iPhone 5; if not, we default to something that fits those tablets better.
The complete file is rather detailed, but it covers most devices or comes very close. The main things you need to remember:
- On some devices parts of the background will be off screen, so don’t put anything “critical” in the bleed areas.
- Objects positioned using fixed numbers like player.x = 64 and player.y = 64 will always be 64 points away from the top and left edges, but based on the device will be closer or further away from center. Likewise, objects placed using the right and bottom edges will stay the prescribed distance away from those edges (player.x = display.contentWidth-64 will stay 64 points away from the right edge) but they will move closer or further from the center too. Objects positioned based on a center coordinate (player.x = display.contentCenterX+100) will stay in the same relative position regardless of the screen shape.
Dynamic Image Selection
Notice that each configuration block contains an imageSuffix table like the following:
If you’re new to Corona, this is how the SDK handles Dynamic Image Selection. For example, you don’t want to use the same image assets on a Retina iPad and an iPhone 3GS. Instead, you should create at least two different image sets to accommodate both “normal” and “Retina/HD” devices.
Each entry in the imageSuffix table consists of two parts:
["@2x"] = --append this suffix to all images designed for those device(s) (decimal value) --scale factor that Corona uses to pick the proper assets
The first value can be named whatever is convenient for you, but it should be reasonable and convenient to append to each image file in that set. The second value is calculated using the following formula:
device width / config.lua 'width' = scale factor
If the scale factor on a particular device is greater than the number you specify for the entry, Corona will use those assets.
Instinctively, you might think that the following entries are logical:
["@2x"] = 2.0 ["@4x"] = 4.0
Not so fast! Inspect the following calculations, derived from our configuration file:
600 / 320 = 1.875 --Kindle Fire & Nook 640 / 320 = 2.0 --iPhone 5 768 / 360 = 2.13 --iPad 800 / 320 = 2.5 --Kindle Fire HD / Nexus7 1200 / 320 = 3.75 --Kindle Fire HD 8.9 1536 / 360 = 4.26 --Retina iPad
Notice that if you specify 4.0 as the scale factor for your Retina/HD image assets, the Kindle Fire HD 8.9 will not use those assets, because its scale factor comes in at 3.75 — and yet, you most likely want to use those assets on such a big, sharp display. Thus, in our configuration blocks, we have chosen the following values:
["@2x"] = 1.5, ["@4x"] = 3.0,
Effectively, this tells Corona to use the highest-resolution “@4x” images for the Retina/HD devices such as the Retina iPad, Kindle Fire HD 8.9, and the Nexus 10. For all other devices, it will choose the “@2x” images instead, conserving texture memory which is especially limited on mobile devices.
One last important note. To display this background, use a call to display.newImageRect() like:
The last two numbers, 360 and 570, indicate the image’s base “1x” width and height, but in practice you don’t need to include that version of the image in your compiled project unless you are targeting lower-resolution devices that require it. In the sample calculations above, the lowest-resolution target device(s) are the original Kindle Fire and Nook Color. For these and other devices in the “@2x” bracket, Corona requires a version at 720×1140. For those in the next bracket, it requires a “@4x” version at 1440×2280. In all cases, it will handle the substitution behind the scenes so you don’t have to worry about it.
This tutorial is rather detailed and comprehensive, and if you’re new to Corona it might seem like a lot to digest. However, the config.lua file we’ve outlined provides you with a pre-made, proven configuration file that is compatible with essentially all new and not-so-new mobile devices from the iPhone 3GS all the way up to the iPhone5, Retina iPad, Kindle Fire HD, and Nexus tablets. If you haven’t downloaded it already, please get it here.