Ludicrous Software

Corona Ads API Tutorial

With the recent release of the latest version of the Corona SDK, Ansca has gone and made my AdMob hack obsolete - not that I’m complaining: it’s nice to see native ad support in Corona. I thought I’d kick the tires on it and put together a quick tutorial on how to set up ads in your app. There are a couple of gotchas that I encountered, so hopefully this will save you some hassle as you incorporate ads into your own app.

If you want to see the results in action, I encourage you to grab Knight’s Puzzle from the Android Market. And click on some ads while you’re at it. My boys’ RESP isn’t going to grow by itself, you know.

The first thing you need to do is create an InMobi account, as this is the only ad provider that is currently supported. That’s pretty straightforward so I’ll assume you don’t need to be walked through that.

Once that’s done, you can create your app from the “My Sites/Apps” screen:

One thing to bear in mind: once an app is created, you cannot delete it. I find this kind of annoying - you can see in the above screen shot that I tried creating an iOS test app, as well as a web app, when I was getting things set up. I’ll now be staring at these unneeded apps for the entire time I have an InMobi account. Ah well.

Here’s the information required for an Android app; the requirements for an iOS application are pretty much the same (substitute App Store URL for “Market URL”):

The package name is the reverse-DNS style name that you’ll use when you’re building your Android app from within Corona (e.g. com.bar.foo.mygreatgame). The Market URL follows a standard format: http://market.android.com/details?id= followed by the reverse-DNS package name. I’ll assume you don’t need me to explain the app name and description.

Once you’ve created your app, you’ll be provided with an App ID. You can find this on the “App Details” page. You’ll need this in a little bit to plug into your Corona application. Ignore the reminders to download the InMobi Android SDK.

A word of warning: before they start serving ads to your app, InMobi must approve the app. This seems to apply even when you set your ad code to test mode - more on that in a bit. When I first created the Knight’s Puzzle entry, the game wasn’t yet live in the Android Market, so I left the App URL empty. But, InMobi wouldn’t begin the approval process because I hadn’t provided all of the required information. Since the Market URL is easy enough to figure out in advance (see above for the formula), I plugged that in and waited to see what would happen. I received notice that my app had been rejected because of an invalid URL - this makes sense, given that the app wasn’t live yet (which I’d indicated when I first filled out the form), but it makes it kind of tough to test the ad integration before the app goes live. The other strange thing was that it took about 12 days to find out that the app was rejected. I’m pretty sure that’s some sort of outlier - or at least I hope it is. And yes, I could have made an inquiry with InMobi to find out what was going on. In any event, after Knight’s Puzzle went live in the Android Market, I resubmitted the app to InMobi and it was approved within a matter of minutes, so there must be some sort of automatic check that happens after an app is submitted.

Unless I did something wrong - which is entirely possible - the upshot of this is that to capitalize on the initial downloads of your app, you want to make sure that your ad code is as bulletproof as possible. This is less of an issue with Android, because you can upload an update and it will get picked up almost immediately, but the lag with iOS approvals can cause problems. Happily, Ansca has done a great job of making the integration of ads as easy as possible. I hit only minor speed bumps, which I’ll mention in a little bit.

Here’s the basic code to get you ready to show ads:

1
2
3
4
5
local ads = require("ads")
local adNetwork = "inmobi"
-- the value for appID is the string assigned by InMobi after you create your app
local appID = "adflksadjflkasdfjoasgu" -- not a real app ID
ads.init(adNetwork, appID)

First, we include the ad module. Next, the name of the ad network and the app ID are set. Right now, InMobi is the only ad network supported by the Corona ad API, so you can certainly skip creating the variables and just set the adNetwork and appID as literals in the ads.init() call. Once that initial code is in place, then you can display an ad using the ads.show() method. Here’s where I started:

1
2
3
4
local showBanner = function()
  local adX, adY = (display.contentWidth - 320) / 2, display.contentHeight - 48
  ads.show( "banner320x48", { x = adX, y = adY, interval = 60, testMode = false } )
end

The first parameter passed to ads.show() is the size/type of ad you want to show. The second parameter is a table of optional values. The first values are the x and y positions of the ad, the third is the refresh rate, expressed in seconds (aside: this is in seconds, timer.performWithDelay() intervals are in milliseconds; that sort of inconsistency drives me bonkers).

The last argument sets the test mode - I’m still unsure of its purpose. During initial testing, the ads.init() call would fail because the app wasn’t live in the Android Market yet. Perhaps I’m missing something? The table is optional. Without it, you’ll get an ad appearing at the 0, 0 position of the screen that will refresh every ten seconds.

And that’s all you need to get your ads showing. The ads seem to behave like a native widget, rather than a Corona display object, which means that they’ll appear on top of whatever other display objects you have in your app. If you want to remove the ad at any point in your application, all you need to do is call ads.hide().

You’ll notice that I’m requesting an iPhone-sized ad, since the API reference for ads.show() doesn’t list any Android-specific sizes. Accordingly, I initially tried to position the ad according to the size I requested (centred on the screen, 48 pixels up from the bottom of the screen). This is where I hit a couple of issues:

  1. It looks like InMobi will override the ad size you’ve requested in some cases. In this case, it displays a banner ad that is 480 pixels wide by 72 pixels high, instead of the size requested. So the positioning was off because of that.

  2. After I modified the values of adX and adY accordingly, the ad was still showing up in the wrong position. It turned out that passing in the value of variables for the ad position wasn’t working, for some reason. Instead, I had to inline the calculation. Once I did that, the ad shows up exactly where I want it:

1
2
3
local showBanner = function()
  ads.show( "banner320x48", { x = 0, y = display.contentHeight - 72, interval = 60, testMode = false } )
end

I didn’t change the value for the ad that I’m requesting. I probably should do this, or at the very least do some device sniffing and display a more appropriate ad (e.g. a larger ad for tablet devices).

Another thing to bear in mind: when an ad request fails, it fails silently, at least from the user’s perspective, and the area reserved for the ad will remain ‘blank’. So, you have to plan for the possibility that no ad will appear. What will the user see in your app if the space reserved for an ad isn’t showing an ad? In the case of Knight’s Puzzle, it’s not really a problem, since the game play is confined to a smaller area of the screen. But for other games, you may need to plan for the possibility that the ad request will fail.

For example, assume you’re building a game with a lot of enemy objects moving around the screen. If your code assumes an object is off-screen when it reaches the area reserved for an ad, but there’s no ad there, what happens? One approach may be to code your app as if the ad will never be there, and accept that when an object is in the ad space, it’s still “in play”. This can have consequences for some types of games, however - for example, you may not want your enemy planes shooting at the player from underneath an ad, since the bullets can look like they’re coming out of nowhere. So perhaps in that situation, the planes keep moving until they’re completely off the screen, but don’t shoot at the player unless they’re in the ‘safe’ (i.e. non-ad) area. There may be other approaches depending on the specifics of your game, but it’s something you should keep in mind.

Handling this sort of thing would be easier if Corona dispatched an event indicating whether an ad request was successful, so hopefully we’ll see that added in a later version (or if InMobi served up house ads, the way AdMob does, then you wouldn’t need to worry about a less than 100% fill rate, since the unfilled requests could be filled with house ads).

All in all, I’m pretty pleased with the ad integration in Corona. Now go play Knight’s Puzzle so my kids can go to a good university!