03 May 2013
Guest Post: Corona SDK + GameDevHelper = A Powerful Duo
Andy Hadlington is a freelance interactive developer based in the United Kingdom. Once Andy took a dive into mobile development, he created his first 2D top down racer game – Turbo Sprint – with Corona SDK. In his guest piece, Andy breaks down several commonly asked questions about Turbo Sprint.
I’m Andy Hadlington, a freelance interactive developer based in Bristol, UK. My main ‘bread and butter’ work involves developing content for the online gaming/casino sector using Flash and ActionScript but in my (very rare!) spare time, I like to develop small mobile apps.
At first, I thought I’d leverage my existing experience/tools (i.e. Flash/AS3) to develop an app, but I soon became disappointed with the performance of the iOS packager in Flash, so I started to look for other alternatives and I discovered Corona SDK.
After some experimenting, I realized that it was a powerful and relatively easy to develop apps that would rival native apps for mobile, so I decided to start work on the type of game that I’d like to play myself, so I started to develop a 2D top down racer called Turbo Sprint.
This is a quick overview of the main components of the game which over the last few months I’ve been asked about. It’s by no means exhaustive but I did learn quite a lot during the project so would like to share some snippets of information and techniques I learnt with the community. If you have any more questions, feel free to ask!
Track And Level Design
Obviously, a racing game needs tracks and the way I approached this was to design track tiles and place them in a level using the fantastic Level Helper and Sprite Helper tools. This allowed me to create a tile-set and arrange them in a grid to create the basic track layout. This did limit the track directions to a simple N, E, S and W appearance (i.e. no diagonals) but I felt that it was ok and fitted with the style of game I wanted to create. This literally saved me HOURS of development work as Level Helper provides a very simple API that allows you to load a level in just one line of code. If I wasn’t happy, I switched back to Level Helper to adjust the track, save it off, and then try it again. Fantastic!
Track Objects
We need cars right? Of course we do. Again, I used Sprite Helper to create a sprite sheet containing the car sprites (as well as other trackside furniture that racing games have). It was a simple matter of adding a physics hull to each object via Sprite Helper and then placing them on the track via the Level Editor. I could also add other objects (true stacks, trees etc.) to the level, save it off, and was good to go.
Game Logic
Although it was relatively straightforward to get a track and physics objects on the screen, it was necessary to write custom code for the cars, track logic and more. To do this was a matter of grabbing the objects from the level after it was loaded and then injecting the necessary code into them.
Objects within the level can be referenced by name of course, but the nice feature was that they can be tagged by type. For example, the landscape tiles were tagged ‘LANDSCAPE’, car object ‘CARS’, trees ‘TREES’ … you get the picture I’m sure. This made it simple to grab these objects from the level and skip through them injecting the necessary code. For example, it would be wasteful to draw every landscape tile if it wasn’t on screen, so a simple ‘am I on-screen’ check was injected into each object tagged ‘LANDSCAPE’ which was called every frame to make the tile visible/invisible depending on the current player car position. At first I thought this may be a performance killer as some of the tracks had upwards of 40 tiles, and although I threw away tiles I knew couldn’t possibly be on screen, I was doing this every frame. I was wrong; Corona SDK ate it up and demanded more. I had plenty of power available for the other stuff.
Similarly, an ‘enter frame’ method was created for each car. All cars shared the same code (both player and CPU) and this handled the cards behaviour under acceleration, steering and processed the maths behind the skidding and general car handling (which aren’t as difficult was they sound!). I will endeavour to post a more in-depth explanation of this at a later date – that is, if anyone is interested!
Collisions
Collisions were handled using the in-built physics with event listeners added (via the Level Helper API) to collisions between certain types of objects. For example, car to car would play a crashing sound, car to power-up would process the power up and so forth. All very simple.
Controls and AI
The player car was controlled by on screen input (left/right/accelerate), which were passed to the ‘enter frame’ function of the players car, but obviously, the CPU cars needed to be able to navigate around the track somehow. This is where Level Helper came to the rescue as it allows the creation of Bezier curves on the level, so it was a simple process of drawing the ‘racing line’ on top of each track and then via code, grabbing the line from the level and querying the line points. Every few seconds, the CPU car was told to head towards a line point and when it got close enough, move to the next, etc. This proved to be very effective.
Also, each car had a different max speed and acceleration parameter that ensured that they were always sufficiently spread out along the track during the race.
Who’s in the lead?
By using this Bezier line data, it was also possible to create a list of invisible polygons, all numbered from 1 to 20, each encapsulating the track into ‘segments’ (around 20 segments per track was optimal).
Before each race, this data was pre-processed to create each segment polygon. These were then tagged with its distance around the track (in pixels) e.g.:
Then around 5 times per second (it wasn’t necessary to perform this check every frame), each car checks to see what segment it’s in by checking each polygon in this list. After calculating this segment number, we then query its pre-calculated distance to get a rough value of where the car is around the circuit.
However, this value is a little coarse, as positional information needs to be fairly exact, especially when cars are overtaking each other constantly, so we then calculate how far the car is along the current segment and then add that to the original segment distance. By storing the total distance travelled values in each car, and dividing these values by length of the track, we can them simply compare them to decide what position each car is in. For example, a car distance of 0.5 means that it’s travelled halfway around the first lap. A car distance of 1.5 means it’s half way through its 2nd lap and a car distance of 3.9 means it’s on its 4th lap, closing in on its 5th lap.
Example car positions using a track length of 500:
Car number | Distance Travelled | Position | Lap Value |
---|---|---|---|
Car 1 | 1578.3 | #2 | 3.1566 – Lap 4 |
Car 2 | 1976.2 | #1 | 3.1566 – Lap 4 |
Car 3 | 1456.7 | #3 | 2.9134 – Lap 3 |
Car 4 | 1256.9 | #4 | 2.5138 – Lap 3 |
Car 5 | 1000 | #5 | 2.0 – Just started Lap 3 |
Aw, you cheat.
This method was also useful for controlling any non-standard driving on the players part (going the wrong way, detecting short cuts etc.). During each segment check, the segment number had increased, then the car is moving the correct way around the track. However, if the car had moved to a segment with a lower value, then the car was obviously going the wrong way so a message was flashed to the player. However, if the car had moved from the last to the first segment, then the car has completed a lap.
To detect the player attempting short cuts, it was necessary to keep track of how many segments the player has driven though during each lap. When the player completes a lap, if it was found that he was still due to pass through segments (e.g. segments_left > 0) then we can safely assume he’s taken a short cut and flash the appropriate message. This could be achieved in a similar way by using checking the total distance the car has travelled in the current lap against the lap length, but I went for the segment check (as it seemed easier).
Social
Facebook and Game Center integration was a lot simpler than I imagined too (so much so, that I left it until late in the game’s development as I really didn’t want to face it!). However I was pleasantly surprised and after a few questions posted on the forums, I was soon pointed in the right direction. Indeed, one of the great things about the platform is Corona SDK’s community (thanks guys and gals!).
Summary
There are probably numerous other ways of creating a racing game and its associated logic. The way I describe here is purely a personal preference but I thought I’d share it anyway. The important thing I want to put across is speed of development that can be attained using Corona SDK and Lua. I managed to get a player controlled car and CPU cars on screen within the first few hours of development. Also, bear in mind that although I’m an experienced coder, I was completely new to Corona SDK and Lua, so I imagine that I’ll develop my future Corona SDK projects even more quickly.
Coming from a Flash/AS3 background, I found Corona SDK and Lua to be extremely simple yet remarkably powerful. The techniques I’ve described above are relatively easy to implement due to the fantastic performance and dynamic, flexible nature of the SDK. I’m also a great fan of the speed in which you can edit and test code. All changes are viewable virtually instantly using the simulator, which I found incredibly useful, especially when fine-tuning the player car physics towards the end of the project. The built-in physics is great plus too.
All in all. Corona SDK is a major weapon in my arsenal of game making tools these days. A veritable WMD!
–Andy Hadlington
Dave Baxter
Posted at 16:07h, 03 MayHopefully Andy is reading this, few problems.
First time I run the app is rebooted my iPad 1, I had select level 1 and it was at the loading screen for ages.
Second time it didn’t reboot, so not sure why but I have had this before with Crona apps from the app store.
You full screen iAds are a real problem. I came last in my 1st race, clicked retry and got a full screen iAd, took about 5 seconds for the close X to appear. Once I closed the ad, the race had already started!
Dave
Inna Treyger
Posted at 17:20h, 03 MayHi Dave – Andy is traveling today and out of pocket. He requested that I ask you whether you’re using v 1.2, as he believes that RevMob was causing some of these problems.
Andy will investigate and respond with more information once he returns.
Thanks for reading!
Inna
Dave Baxter
Posted at 23:08h, 03 MayHi,
Yes am using 1.2
Dave
Nenad Katic
Posted at 02:55h, 05 MayNice game, and thanks for this great post! And yes, in-depth post about steering and skidding physics would be greatly appreciated 🙂
Andy
Posted at 12:17h, 08 MayHi all! In the developer. I will look into the problems as soon as a I get back from vacation on the 13th May. I do apologise for the bad experiences done of you are having.
Rest assured I will investigate as soon as I’m back at my office 🙂
Chris Maher
Posted at 09:00h, 10 MayHi Andy, Good job with the game. It’s got that “one more race” addiction factor going for it that makes it hard to put down.
One thing I noticed … I haven’t been able to pinpoint when or why it happens yet, but every once in a while the controls seem to stop working. I’ll have my thumb on the throttle for 3 laps (not moving it once) then all of a sudden my car slows down. Then eventually it will pick back up again. Something like this happen to anyone else (I’m on an iphone 5).
Also, seems like this would be a fun game with tilt-steering. Did you experiment with that during development?
sq2
Posted at 17:42h, 11 MayWho was in charge of the music? Sounds very demoscene ish.
Nathan
Posted at 18:01h, 13 MayGreat post Andy – well written. I’m interested in hearing more about the car handling, skidding etc that you allude to in the article.
Andy Hadlington
Posted at 01:55h, 20 MayHello all!
Thanks for bearing with me, I returned from vacation in Vegas to a pile of work that needed addressing and so I only managed to get the time to jump back onto Turbo Sprint over the last couple of days or so.
I’ve fixed the problem with the full-screen ads and now I’m trying to recreate the iPad 1 problem. After that, I’ll take a look at the control problem that Chris has mentioned and when I’ve fixed that, I’ll upload new version to Apple for review (you may have noticed that a new version was pushed while I was away, this was uploaded before I went on vacation and before I was aware of the problems people are experienced on this thread).
@sq2 – the music was purchased from MusicLoops.com
@nathan – I’ll put together a post re : the driving physics as soon as I’ve nailed the new version for the app store!
Nathan
Posted at 19:20h, 06 JuneHi Andy,
I’m working on a small top-down driving section of my new game now, but I’m struggling with the physics – I have the acceleration/deceleration working through applying linear force, but I’m struggling with cornering and deciding when to skid and when not to.. did you just use angular force?
Thanks,
Nathan.