Posted on by

Device Support

iOS and Android devices come in all manner of screen sizes. With Zombie Slash, we intended to support every device and screen size possible. In Flash, this happens automatically. It will scale to fit the screen and, as long as all the graphics are in vector format, the game will still look fine. This is not ideal, though, and many Flash games can look stretched, especially when running on widescreen monitors.

The solution is to use dynamic content scaling, a feature of Corona. You can not only specify higher resolution x2 images for use on devices like the iPhone 4 and iPad, but you can also setup your stage in a number of different ways.

I decided on “Letterbox” as our method of scaling the stage, which would keep the aspect ratio of the game the same on every device – but it could have a downside.

On a non-3:2 screen the scaling could introduce borders. On devices like the iPad and Droid, which are not the same resolution or ratio as the stage, I needed to account for that extra screen space.

A few simple calculations reveal the true dimensions of any device’s stage when using the Letterbox method.


local screenLeft = display.screenOriginX
local screenTop = display.screenOriginY
local screenRight = display.contentWidth - display.screenOriginX
local screenBottom = display.contentHeight - display.screenOriginY

These coordinates now allow me to align all the game elements to whatever side of the screen I wish, knowing they will appear in the same place on any device.

This method also allows me to ensure the background covers the entire screen and removes any borders that would have otherwise appeared on nonstandard devices.

The Game Engine

Flash is very different to Corona so there are many occasions where you cannot use the same technique to achieve the same outcome. One very obvious difference is the number of game / physics engines for Flash which cannot be used in Corona. Luckily Corona has its own physics engine, Box 2D, so that was the obvious choice to power the game and also handle collision detection.

As each game element is generated, physics is applied, a collision mask is set and a function is attached with a listener. This function is triggered to fire on collision and it is how we interact with the object now it’s in the physics engine. Without these interactions the object’s motion is otherwise handled separately from the lua code by Box 2D, which applies motion based on a set of preset parameters, such as force and friction. This allows for really smooth, realistic motions with minimal effort and code.

When the object does collide with something this function now checks the name of the object it has just collided with. If for instance it hits an object called “killzone” then it has left the screen and we can safely remove it and deduct 1 life from the player.

Using event triggered functions like this means the application has very little lua code to process per frame allowing it to keep a smooth frame rate.

Audio

Corona has OpenAL built in which means with very little work we can use the same audio files on both iOS and Android. Naming conventions need to be followed to ensure the samples can be found by Corona on the different devices. For music we used MP3 files as both iOS and Android will play those, the same goes for shorter effects, for cross compatibility WAV files were used.

Particles

The original Flash game also used a particle engine to generate explosions, fire and blood among other things. Out of the box Corona does not include a particle engine. Rather than write one myself – which would be time consuming – I opted to use a 3rd party module called Particle Candy. This brilliant module allowed Corona to generate all the different effects I needed. Setup and configuration was minimal due to the many different sample apps that are provided as part of the package.

Shadows

The Flash version of the game used a native filter to generate shadows for all the different objects, obviously this could not happen in the Corona version. The solution was easy enough, though – it’s down to how you construct your game. Corona does not have layers, it has groups, which are much more powerful. By setting up groups for your different game elements you can efficiently layer your game. This is a simplified structure of the Zombie Slash game:

  1. Background Layer
  2. Shadow Layer
  3. Particle Layer
  4. Object Layer
  5. Zombie Layer
  6. HUD Layer
  7. Slash Layer

Now, every time a new object is inserted into the game, we also load the shadow of that object. Essentially the shadow is just a transparent image with a black fill of the object, a slight blur and transparency. An enterFrame listener moves all the shadows each frame to match their counterparts. If the listener can no longer find the real object, because it’s probably been removed either through going off screen or the user hitting it, then the shadow is also removed through the usual methods.

Memory Management

The key to a smooth running game is memory management. Without it the game will soon grind to a halt as the device’s memory is consumed. Errors usually occur when creating and destroying a lot of game objects, as all the interactions can be hard to manage unless you plan in advance.

It’s best to come up with a common method for destroying all of your game elements. I personally attach a function .destroy() to each object on creation, the content is a description of how to destroy that object. By keeping the destroy code with the creation code is easy to manage the different variables, tables and images associated with each object. Just remember if you write code to create something you need to write code to remove it.


-- Destroy Function
zombie.destroy = function()
-- Remove Images
for i = zombie.numChildren , 1 , -1 do
zombie[i]:removeSelf()
end

– Remove Physics & Group
zombie:removeSelf()

– Cleanup
zombie = nil
end

I now have a way of removing anything in the application. If the object is hit, the collision listener can call the destroy function removing the object and its associated physics and images.

If however I wanted to clear the game screen I could just loop through a group and remove every child I encounter by just call its .destroy() function. These methods can save a lot of time when creating and debugging code and allow for a very efficient game structure.

Images

To fully support retina displays and higher resolution tablets it is necessary to include double resolution images in you application. Corona will automatically load the x2 version, on the appropriate devices, it’s that simple.

With Zombie Slash we had the vector drawings for the Flash version, so creating the .png versions was very simple. If you are creating elements from scratch I would recommend using a vector based editing package so you can export at any resolution. Adobe Fireworks is especially suited for this as it’s engine is a pixel / vector hybrid. This means when you resize the source design it remains sharp rather than being run through a filter and becoming pixelated or blurred.

The other thing to remember and which is very important in ensuring the images look crystal clear when loaded into Corona is to always use images sizes that are an even number of pixels wide and high. For example an image of the size 30px x 50px is fine but 31px x 50px will cause the image to become blurred. This occurs because the center point of the image, 31 / 2 = 15.5, is now set to position the image at a ½ pixel offset to the actual pixels of the screen. Each pixel of the screen is now trying to draw 2 pixels of the image so everything becomes blurred.

Multitouch

The last big consideration when building any application is multitouch. With the Flash version of Zombie Slash, the player would control the game with the mouse so there was only ever one slash on screen. With the iOS version we wanted to allow the player to use more than one finger, something that could only be achieved by enabling multitouch.

A number of pitfalls exist now that you are registering more than one touch. For example if you have two buttons the user can press them both at once. If you are triggering a function on release then the user will still be holding down the other button, which will still fire when it too is released. This can cause serious bugs if you are not careful as firing 2 opposing functions could bring your app to a halt.

The solution, to a problem that only really becomes apparent when you are testing on a device, is to manually send an event to all the other buttons telling them to end any touch they may have focused.

myButton:dispatchEvent( { name = "touch" , phase = "cancelled" , target = myButton } )

 
 

Summary

With these basics in place the construction of the game became simple. Converting ActionScript to Lua is very easy as the two languages have a lot in common. This allowed for all the game logic, the scoring, timing, leveling and other interactions to be easily transferred from Flash into Corona. We even built demos in Flash and applied the same settings in Corona / Box 2D to ensure we were getting similar movements.

Now I had the basic object design, an image, a collision function and a destroy function it was easy to duplicate the code / module for each of the different objects in the game. The main differences being the actual image, the physics settings and what happens on collision, where some objects explode and so trigger the particle engine, some remove a life and some deduct time.

With all the elements falling into place we were able to spend our time refining the gameplay and making the overall presentation of the app as appealing as possible.

Something, which is very easy in Corona due to its native transitions, graphical capabilities and overall ease of use.

Oh, and go check out the Corona-made version of Zombie Slash and let me know how I did! ;-)


Posted by . Thanks for reading...

10 Responses to “Tutorial: Resurrect your Flash games”

  1. Mo

    Hello,

    Thank you so much for this article! I learned quite a bit of things. I was really interested about the device support section. How do you use these formulas?

    local screenLeft = display.screenOriginX
    local screenTop = display.screenOriginY
    local screenRight = display.contentWidth – display.screenOriginX
    local screenBottom = display.contentHeight – display.screenOriginY

    For instance if you want avoid those black top and bottom black bars on an iPad. For instance I have two set of images (320×480 and 960×640) for iPhone and iPhone 4. But in the iPad I have bars on top and bottom (landscape mode). Would I need to add another set of image with 768×1024? I rather not since it will only add size to the app. Would your code above could help? How? Maybe I can replace the iPhone 4 with iPad size image (768×1024) and count on the system to downsize the image to iPhone screen size?

    Anyway thank you so much for this article.

    Mo

    Reply
  2. Matt

    Hey Mo

    You can setup the stage in different ways, with the default Letterbox method on an iPhone the 0x,0y point is the top left of the screen. On an Nesus One, the 0x,0y point say 40px in from the left of the screen. See diagram, the pink area is the extra pixels.

    This means the above calculation gives screenLeft as -40px ( or whatever the real result is ).

    So in a game, rather than checking if a character has left the screen using if x < 0 then…
    you use if x < screenLeft. This means screenLeft is always the leftmost visible part of the screen, where as 0 can move.

    You can setup Letterbox so Corona shifts the 0 point to the left of any screen but I found this cause other issues with code and plugins so I didn't go down that route.

    I find using the above method left you develop for the default iPhone, and by keeping in mind a few simple things you can support any device without any extra work.

    Reply
  3. Matt

    Mo, for a background image, just load the size 610px x 420px, on every device with an @2x version to support iPad and iPhone4. Then centre align the image.

    Now this slightly oversized image for the iPhone, will cover all the androids and is of such a size that on the iPad it loads the @2x version which covers the iPad screen.

    3rd diagram down shows that method.

    Downside, slightly larger texture memory used than needed on iPhone, but saves having lots of different sized which would increase the overall size of the app. An the extra memory used is not really that much, so the methods sound.

    Reply
  4. Mo

    Thanks Matt! So If I understand you correctly I needs (background image):

    1- image: 320X480 for regular iphone

    2- image@2x: 610X420 for both iphone 4 and ipad (I am not too worry about Android right now)

    Now, are you not afraid that the image@2x will not be enough to cover the ipad (1024X768) nor the iphone 4 (640X960) and will end up just zooming the smaller image@2x (610×420) and make the image fuzzy?

    I am sure I am missing something!

    Mo.

    ps: maybe you are talking having the image = 610X420 and image@2x = 1220X840? That will fit both the ipad and iphone 4 and be just a little bit overkill. Again I am not too concern about the Android right now…

    Reply
  5. Matt

    a standard image for iPhone and all low res android is 610X420. The 1220X840 @2x version covers the iPad and iPhone 4 and any other tablets.

    Reply

Leave a Reply

  • (Will Not Be Published)