Alright, alright — so Carlos’ post about Flash last week seems to have ruffled a few feathers (as was his intention! :-P). So, now allow me to point you to Exhibit B.
I wanted to share my experience with other developers in the Corona SDK community — particularly with some of the technical challenges involved in developing a top-down racing game, which is different from the more popular platform-based games developed in Corona. Not only did I transition from Flash to Corona, but I learned that Corona SDK really can deliver with a much more efficient effort! 🙂
When we were commissioned to develop the Ferodo Race Challenge app, our confidence was high. We had never heard of Corona so, being Flash developers since the beginning of time, we believed Adobe was the answer. Since Adobe was pushing CS5.5 so strongly, we had no reason to doubt that a Flash top-down racing game could be delivered to iOS and Android via the AIR packager.
How wrong were we!
Once we started the development of the game in AS3, our confidence started to drop as fast as the frame rate. By the time we added a large graphic for the track, applied a physics engine, and implemented steering controls, we were already down to 12-15 fps. It was evident that there were tremendous performance issues with delivering a game like this on mobile.
After attempting many workarounds — trying a GPU based physics engine, dropping the engine altogether, and even bribing Adobe to give us a copy of the AIR 2.7 SDK — we decided to abandon Flash.
That was a scary day because the road ahead was uncertain…
After much searching on the web and lots of feature-comparing, Corona was the one prospective SDK that stood out to us. I was still a little wary about learning a new language (Lua) in the tight timeframe. So, I spent a very geeky weekend by going through the whole API documentation. I had never heard of Lua and, even though I’m not the best programmer in the world, I was able to pick it up in less than two days. The secret is that Lua, being a scripting language, is extremely simple and very similar in the syntax to the fun-to-write ActionScript 2.
This is why the development team at BOOM Interactive decided to adopt Corona for the Ferodo Race Challenge app. For the technically-minded, interested in the specific challenges we faced in developing this top-down racing game, please keep reading!Also, I must give kudos to Ansca Mobile for the way they structured the documentation: general introductions, specific API docs, tons of samples and even full game templates offer everything one needs to know to start and complete a project. Not to mention the resources offered by the community in the forums and in the Share Your Code section — simply invaluable!
Physics engine in a top-view game
We’ve all seen Corona’s capabilities in physics-based platform games. But not many top-view racing games have been made with it. The general rule to use a physics engine in top-down view rather than side view is to simply kill gravity, but that brought a side effect, quite difficult to get around: orthogonal velocity. When the car gained speed, going up or down on the screen, orthogonal forces started to push the car from the side, bending its trajectory until the car flipped and faced the opposite direction!
In Flash’s Box2D, there is a function called killOrthogonalVelocity which applies a ‘friction’ in a direction orthogonal to the body’s axis, therefore keeping the car straight.
Due to the lack of time to experiment, we had to find the quickest way to resolve this issue. This was done by using a mixed approach.
For controlling the car, we created functions to set the linear velocity (position) manually at every frame and to compute all necessary values like speed, acceleration, deceleration, ground friction, steering friction and steering acceleration. Basically, the approach used in classic games without any physics engine.
We then used the physics engine to deal with collisions: set game boundaries, trees and obstacles, opponents and most importantly determine whether the car was on the track or on the grass (this was a tricky one, more on it below).
Corona dealt with the mixed approach really well, never having an effect on performance.
Although the mixed approach worked fine for the purpose of the game itself, we’d still like to get the game working using solely the physics engine. Few days ago we discovered that someone contributed their Lua version of the Vector2D class on GitHub. We’d love to use this class to contribute our own version of the killOrthogonalVelocity function to the Corona community — maybe even provide a top-down racing template. If someone reading is experienced in multiplying 2D vectors, do get in touch!
When we moved to Corona we weren’t sure of how to implement a camera following the car around the track. This is where the community proved its value. There are a lot of contributions on the site forum and we cracked this task in no time by grouping everything in one main object and using no more than 5-6 lines of code to move it across the screen.
Once more, we were genuinely surprised on how smoothly Corona dealt with this, especially after seeing how poorly Flash was doing. Not to mention that the track image is 2048x2048px and so is the shadows layer on top of it!
I guess having a full OpenGL-ES rendering engine really paid off for Corona.
Scene management: the Director class
What to say about the Director class. It’s the true shiny diamond of Corona’s contributions! Many thanks to Ricardo Rauber — don’t forget to donate a few dimes because, if you’re developing an app in Corona, you’ll most likely end up using his class.
It is extremely simple to manage different scenes, apply smooth transitions between them and clean up previous scenes’ objects from the memory.
Regarding the latter point, do make sure you use at least version 1.3. We ran into some issues with grouped graphics not properly being reset when starting a new game with v1.2. When testing on iPhone this behaviour occurred rarely but when we started to test the game on Android this would occur almost every time. All it took to resolve the issue was to update the Director class to 1.3. A real life saver!
The ability to support multiple languages is generally a must when developing an app for large corporations. Corona did not fall short in this area either. In the “Code Exchange” section of the Corona site, one can find the Rosetta module which is a great and ready to use utility.
But since Lua offers great flexibility, it was easy for us to create our own custom solution. We wanted the application to come up directly in the user’s preferred language, without the need of having to choose a language at the start – while still offering the option to switch.
By using the SDK command system.getPreference(“ui”, “language”) we were able to know which language the user had the phone set to. Consequently we loaded a specific language module to fill a global table with the language strings found in the module. Of course the app defaults to English if the user’s preferred language is not among the supported languages.
Noteworthy is the fact that, while the SDK command above is cross-platform, the returned values are not. For instance, iOS returns language codes (en for English) while Android returns the Locale ID in decimal format (2057 of British English, 1033 for American English). Just something to consider when dealing with languages. For a full reference of Locale Codes go here.
Track/Grass collision detection: triangles vs. Bézier curves
(Carlos loves those!)
Until tackling this task, the development had proceeded almost smoothly. But it did come to a halt when we started pondering the best way to determine if the car was on the track or out on the grass. Our objective was simple (so we thought): draw the track outline as a physical object, set it as a sensor and use collisions to determine if the car was either in or out.
There are many third-party tools that can be used to draw collision objects in Corona. Most of them are very reasonably priced and easy to use, but most of them are particularly geared around platform games/levels. The two that seemed most appropriate to our needs were PhysicsEditor and Corona SVG Level Builder.
PhysicsEditor’s simplicity was a breath of fresh air. The Autotrace functionality does exactly what it says: it traces the outlines of your sprites and lets you control the amount of vertices created. Above all, it solves Corona’s limitations (objects cannot have more than 8 vertices) by stitching together triangular shapes. This way one can generate a parent object with as many vertices as one needs.
Even though the generated shape was not pixel-perfect (not being curved) we were happy enough with it. The only problem was that the car generated collisions when crossing the boundaries of each of the child triangles and not only on the parent object’s boundaries.
That was not the desired effect but it did depend on how Corona deals with complex objects. It is indeed a handy feature to have collisions on each of the child objects, but it would be just as handy to be able to disable such behaviour (a feature request for Corona perhaps!). Therefore we moved onto the SVG Level Builder and never looked back!
Corona SVG Level Builder is, simply put, an amazing tool. It lets you create whatever shape you want by drawing in Inkscape (an Open Source vector graphics editor) and then setting the physics properties with the built-in Inkscape XML editor. We found InkScape incredibly easy to use in tracing Bézier curves and optimizing the points count.The SVG parser script did a great job by loading those curves seamlessly into the app and the collisions were truly pixel-perfect too!
All was left to do was to adjust the ground friction variable whenever the car was detected on the grass. This would have been pretty easy if the car shape was to be a circle rather than an 8-vertex shape, but by using two border lines rather than one we got the track/grass collision to work just perfectly!
iOS vs. Android
As you know, Corona delivers cross-platform apps. There are still few APIs that are slightly inconsistent, such as the video player, but generally one can assume that the exported app will behave identically on the two platforms (apart for native components of course).
Whether you are up against a mission-critical commercial deadline or not, it is important to allocate enough time for testing, squashing bugs and resolving unexpected crashes.
In our case for instance, we knew the game was very graphically intensive, so during the test phase we paid specific attention to the iPhone 3GS, having almost half of the hardware capabilities of an HTC Desire. To our surprise, the iPhone never crashed while it was the HTC Desire that caused major headaches — not being able to even run the app for more than one day! Reasons can range from texture memory limits to memory leeks or missing permissions on Android.
Specific reasons aside, if you know your app could push the boundaries of a device, always do your research and some testing ahead of delving into the development.