Truly Universal

In a previous post I discussed the approach that I used to make a Universal app for iPhone and iPad. That approached circumvented some problems others were having on the iPad 3 with cocos2d. Even though apps continued to run properly on the iPad 3 with that approach, we were not taking full advantage of the extra pixels.

The first thing we need to do is generate new high res assets. I am going to skip that step for now and assume you have higher resolution assets. Add the suffix "-hdpad" to the end of your high res assets or pick your own suffix and adjust the code below accordingly.

Next we need to define our Retina iPad suffix in ccConfig.h:

Then we need to edit CCFileUtils to make sure we use the "-hdpad" suffix when a Retina iPad is running our Universal app:

You will notice in the above code we are checking for the Retina iPad by checking to see if the content scale factor is equal to 4. If we load "-hd" assets and scale the points by 2 on the iPad, then we will need to double that for the Retina iPad.

We also need to update the utility function that cleans up the hd suffix:

Next we need to modify our enableRetinaDisplay flow we outlined in the previous post to enable Retina on the iPad 3.

The first thing we do is create a new enableRetinaDisplay:onPad method so we can choose not to enable the Retina Display on the iPad 3 if we don't have the assets to support it:

You will notice that the old enableRetinaDisplay method calls the new method but passes a default value of FALSE. This will keep our app working as is until we have our "-hdpad" assets ready.

The next step is to modify the content scale we will pass to setContentScaleFactor. Remember that we want to end up with a content scale factor of 4.

Notice that if we are not supporting the Retina iPad, then we set the scale exactly as we did before. If we are supporting the Retina iPad, then we set the new scale equal to the scale times the point scale factor which is set to 2 in makeUniversal. Hey Maw, look at my handy chart:

Next we have to take care of the line that saved us from having Retina iPad headaches in the first place:

Referencing out handy chart, you will see that __ccPointScaleFactor is not one. This means that __ccContentScaleFactor is ignored and we use one instead. We need to generalize this line so that it still works under the old case, but will also handle the new scenario:

Now we will still end up with a scaleFactor of 1 on the non-Retina iPad and on the Retina iPad if we did not enable Retina iPad mode. If Retina iPad mode was enabled, then we end up with a scale factor of 2 which enables retina mode on the device.

Here are all the changes to CCDirectorIOS.m in one handy gist:

Now you have a universal app that supports the full resolution of the iPad 3.

Asset Addendum

Preparing your asset pipeline to fit into the hd, hdpad, sd world will take a bit of work. If you have vector or pixel art, then your job is easier but no one wants to export every asset three times. Some of the common tools like Texture Packer will likely build in support for this in the future. In the interim you can probably get by with a few command line scripts.

Caveat: The modifications in this post were done to cocos2d 0.99.5. There are minor differences between the supplied code and the changes you should make to cocos2d 1.1.

Caveat Emptor: You aren't actually buying anything but use at your own risk anyway. There will likely be tweaks and adjustments to this code in the future.

Note: If you are building a stand alone iPad version, then I recommend you use the latest updates in the develop branch for cocos2d 1.1. They have already added Retina iPad support. This code provides a simple way to create a Universal app.

Recommendation: If you haven't had them before, I recommend you go buy some tomatillos and try them on your next taco in place of tomato. Prepare yourself for a surprising and unique flavor.

More Pixels Equals More Panic

The March 2012 Apple Keynote brought no real surprises. The new iPad (hereinafter iPad 3 despite protestations) has a Retina Display with a whopping 1.572864 million pixels. I have some worries about whether the device will have enough oomph to push those pixels, but we will burn that bridge when we get to it.

Like a good little Apple dev, I downloaded the newest XCode and iOS Simulator to test out my apps. The rest of the cocos2d community was doing the same and that is when the excrement came in contact with the bladed cooling device. Devs started to report that their apps were showing a black screen on the iPad 3 simulator. More reports were coming in of other strange behavior.

I had only tested out my current project when I first heard the buzz. It hadn't shown any problems but I was worried about my apps that were already for sale. I fired up every project and tested them one by one. They all behaved exactly as expected, but why? What was the difference between my apps and all of the apps that were having problems?

The Issue

The root of the problem was that the iPad 3 was behaving exactly as it should. Universal cocos2d applications that support the Retina Display on the iPhone often have this snippet of code in the app delegate:

The enableRetinaDisplay method attempts to turn on the Retina Display if the hardware supports it. Turns out that the iPad 3, with its Retina Display, activates the Retina Display when asked to do so. The problem is that since the rest of cocos2d and our apps aren't expecting this to happen on an iPad all sorts of craziness happens.

The good news is that Apple didn't go crazy and that things are behaving as one might expect. The bad news is that we have code built around an assumption that is now wrong. So why did my code work? Clearly I must not have had that handy snippet of Retina enabling code from above. WHAT?!?! I did? Oh, something else must be going on then.

Pixel Pushers

The solution to the problem was a bit of an accident. I wanted to create universal apps that took advantage of the iPad's resolution without needing new assets. With careful asset management and a few hacks it is easy to use your Retina iPhone assets on the iPad. This helps you reduce your app's footprint and take advantage of additional pixels. DOUBLE RAINBOW!

The method that I use is partially outlined in this forum post. It adds a makeUniversal method to CCDirector which you call immediately after instantiation of the director in your app delegate. Here is the code:

This method sets the __ccPointScaleFactor to 2. This sets the scaling between points and pixels. This also causes an escape to be skipped in enableRetinaDisplay display which causes the content scale factor to be updated to 2. The content scale factor is used by cocos2d to determine if the "-hd" assets should be used.

This has another side effect due to a check in updateContentScaleFactor which is indirectly called by the enableRetinaDisplay method:

If __ccContentScaleFactor is 2, then the method forces the display's scaleFactor to 1. This means that we don't actually activate the Retina Display even though we call enableRetinaDisplay. We are now safely using iPhone Retina assets on all (existing) models of the iPad. WOOOOOOOT!

But All Those Pixels!

You are right, this does not take advantage of all the extra pixels that the iPad 3 has available. You will need higher resolution assets to fully take advantage of the new iPad, so we need another approach. We can go over that in another post. (Edit: In fact, I just wrote up a post on how to make a truly universal iOS application here.)

Unity: Day 1.5

It's been a week since my last session with Unity. Work on a Four Hats update and the yet unnamed TBDOP project has been stealing all of my time.

I logged on to Twitter this morning and learned that Unity has a sale on the basic iPhone & Android add-ons for the amazing price of ZERO dollars. I could not resist so I upgraded my license for free, downloaded a sample project, and loaded my first Unity project onto the iPad. In total I only spent an hour with Unity before getting back to TBDOP but having the mobile publishing kits will draw me back soon.

The sale runs through April 8th, so make sure you grab it while you can.

Four Hats 1.1

Four Hats 1.1 has been submitted to Apple, now with more cowbell! We took all of the feedback that we received, sprinkled it with cowbell, and packaged it into an update. The big changes are an updated control scheme, a new game mode, and skips.

Controls

There were times when switching between four characters and using their abilities was a bit like doing gymnastics with your fingers while riding a unicycle on a tight rope over shark infested waters. It worked well once you got the hang of it but the learning curve was a bit steep.

We simplified the controls by removing the action button. Now switching to a character will immediately use that characters ability. The gameplay is still fast paced, but your fingers no longer have to wildly flail to keep up.

If you are absolutely in love with the old controls, then you can switch back to them in the settings menu.

Endless Mode

You can now ensure that the crowd will catch you by playing Endless Mode. In Endless Mode the level gets progressively harder and the crowd gets faster the further you go. Each of the four songs has it's own endless level where you can test your limits by running until your inevitable capture. When you unlock a new song, you unlock a new endless level.

Skips

Song skips can now be purchased to jump ahead and unlock content before you beat the prerequisite levels. You can also choose to unlock every level and song all at once. It is not necessary to buy skips. You can choose to beat everything the old fashioned way.

What's Next?

Our primary focus is on our next title, but we will continue to support and update Four Hats in response to your feedback. Two things we are considering include Game Center and OpenFeint integration. Tell us what you want and we will do our best to make it happen.

Breading - Yeah We Went There

Breading, the act of putting a slice of bread around your cat's head, has been in the news lately. Check out coverage over at Gawker, Discovery, Refinery29, Neatorama, and even the International Business Times. I am slightly embarrassed to admit that thanks to Taco Graveyard there is now an app for that.

My girlfriend revealed to me that she had been trying to bread the cats, but was unable to get them to sit still and accept their fate. I coded up an app that allowed her to take a picture of the cats and virtually bread them. Steve scanned in additional slices of bread, prettied up the app, and then we unleashed it on the App Store.

You can check out a gallery of pictures created using BreadIt over on our Facebook page. You will see that breading has moved on from cats to just about anything you can imagine.

Update: There is a BreadIt giveaway over at breadedcat.com.

Recording iOS Gameplay Trailers - Sound Stage & iSimulate

You've finally wrapped up that first game and want to show it to the world. Time to make a trailer! Most advice about contacting reviewers suggests providing a link to a gameplay video. I understand why. I am not a professional reviewer, but when someone sends me a link to a new game I want to see it in action.

There are a wide array of tools available for recording trailers and what works best for you will depend on your needs and your budget. Two of the tools I have used are Sound Stage and iSimulate.

Sound Stage

Sound Stage is an OS/X app that allows you to screen capture your entire desktop, a specified region, or the iPhone Simulator output. It is available on the Mac App Store and only costs $5.

Pros

  • Easy to use
  • Optimized for utility apps
  • Fair price
  • Designed for capturing from the iPhone simulator

Cons

  • No simulator audio capture
  • Optimized for utility apps

Using the App

There are a number of options that allow you to set the quality, output file, viewing area, and touch indicators. One option that is lacking is simulator audio capture. This isn't a big deal for utility apps, but as game developer it is a feature I miss. They do provide audio capture from the built-in mic or the input jack so I could jerry-rig something, but it would be nice to have it baked in.

The best thing about the app is that it is very easy to get up and running. Just click the big red record button:

The app also allows you to select a soundtrack and drop in images so you could build an entire trailer using it. I prefer to use it for screen capture only and edit video in a more powerful solution.

This approach began to fall down for me when I needed to record footage from an app that required me to touch the screen in multiple places at once. I'm just not that fast with the mouse. This isn't a Sound Stage problem, it extends right through to testing on the simulator. There are shortcuts for the basic multi-touch gestures, but games often move beyond that.

The approach I used for Four Hats was to port it to OS/X and then map keyboard controls so I could simulate touches in all the right places. Cocos2d makes this pretty simple but it still takes some work and isn't going to work for every game. Then I discovered iSimulate.

iSimulate

iSimulate allows you to send the multitouch, GPS, accelerometer, and compass data from your iPhone to the iPhone simulator. The SDK is free but to use it you need to purchase an app from the iOS App Store that costs $16. I have to admit there was a bit of sticker shock looking at an app that costs 16X most of my App Store purchases. I then felt pretty ridiculous for feeling shocked about spending $16 on something that will save me hours. I believe in using good tools to magnify the effect of your time and this is one that is well worth it. This is a great tool not only for recording footage but also for debugging.

Pros

  • Easy to get up and running
  • Multi-use tool: gameplay trailers, faster debugging
  • Good documentation

Cons

  • Sticker shock (for an iPhone app)
  • A bit laggy
  • Ugly UI

Using The App

It is simple to set up an app to work with the iSimulate SDK. Download it. Link to it. You are done. One thing to note is that if you are not using CoreLocation in your app you will still need to link to it while linking to the iSimulate SDK or you will get a few linker errors that look like this:

Undefined symbols for architecture i386:

 "_OBJC_CLASS_$_CLHeading", referenced from:

_OBJC_CLASS_$_iSimulateCLHeading in libisimulate-4.x-opengl.a(libisimulate-opengl.a-i386-master.o)

Once you have your app up and running in the simulator you can launch iSimulate on an iDevice on the same WIFI network. You should see your computer in the list. Tap it to connect and then the data starts streaming.

Don't be scared off by the UI. It is one of the ugliest apps that I have purchased, but it works. I definitely suggest you read the documentation here. They have a few screenshots with legends that will help you get around.

If you are using iSimulate to record gameplay footage, then one of the first things you will want to do is remove or change the touch indicators. The default ones are giant gray circles.

You can remove them entirely by adding the following code to your AppDelegate's applicationDidFinishLaunching method:

if (NSClassFromString(@"iSimulate")) {

[NSClassFromString(@"iSimulate") disableTouchesOverlay];

}

If you want to replace the touch indicator simply add an image to your project named "isimulate-touch.png". There is a lot more useful information in the provided documentation so I suggest you check it out.

There is also an option to stream simulator output back to the iPhone which I can definitely see being useful.

I found that if I used iSimulate in conjunction with the simulator for more than 5 minutes the target app would start to slow down to a crawl. This happened both while connected to the debugger and while running the app independent of the debugger. I tested the same app without iSimulate connected and there was no lag.

Summary

I recommend adding both Sound Stage and iSimulate to your indie game dev toolkit. Both have flaws but save more than enough time to outweigh their cost.

If there are tools out there that fill these gaps at a lower cost or more completely, please share.

Hey Cocos2d Noob - A Letter To Past Me: Part 2

If you missed it, catch Part 1 here.

Another week has slipped into the past and I've yet to find a Delorean, an FTL spacecraft, a TARDIS, a wrinkle in spacetime, or any other means of time travel. I guess I am back to leaving notes in the now for alternate me's.

While working on a project, I tend to use an old fashion notebook in which I keep ideas, todo lists, discovered gotchas, and other scribbled bits of madness. There is something about crossing an item off a list or working through an idea on paper that I enjoy. Call me old school.

Fortunately for everyone else, this notebook is littered with evidence of my past mistakes and misunderstandings. I took some time to review this catalogue of errors to pull out some of the more helpful pointers for anyone who finds this.

Yet Another Point

CGRect

I covered CGPoints and all the hidden gems in CGPointExtension last time, so I thought it was high time to move on to the second dimension. Really, this is more of an Objective-C slash Apple Library thing than a cocos2d thing, but often both are learned at the same time. Here are a few key helpers, I also suggest you read the docs.

CGRectMake - Construct a rectangle given the x and y of the origin, the width, and the height.

CGRectIntersectsRect - Check if two rectangles intersect.

CGRectContainsPoint - Check if a rectangle contains a point.

Rectangles are great for collision and hitbox checks that are not part of a physics system. Once you start dialing up the complexity or simulating physics you should use something like chipmunk or box2d.

Back to My Point - A few forgotten helpers

In building a game that had a HUD layer and a world layer ("Level") that panned and zoomed, I found myself having to convert coordinates between the two distinct spaces. CCNode provides a few helpers that will save you a ton of conversion math.

My explanations assumes that the HUD and the Level are both children of the game layer (if you are using the cocos2d Hello World example this would be your HelloWorld layer). It also assume that the HUD has a fixed position and is in the same coordinate space as your HelloWorld layer (or HelloWorld layer equivalent).

convertToWorldSpace - Converts a point to world space. This means you can take a point in your panned and zoomed Level and figure out the corresponding point on your HUD.

convertToNodeSpace - Converts a point to node space. You can take a point on the HUD and figure out the corresponding point in the game world. ie [level convertToNodeSpace:targetPosition]

convertTouchToNodeSpace - Converts a touch to node space. This is extremely useful in figuring out where a touch point corresponds to in the Level.

Multitouch

There comes a point in every little games life (well, "every" is hyperbole) where it wants to be touched in two places at once. This usually results in a burst of outrage or a post to the cocos2d forums. Some cocos2d samples start with the single touch delegate enabled, as if rubbing your belly and patting your head at the same time wasn't hard enough. There is actually a good reason for this, it is much easier to handle one touch than a set of them. Anywho… on to the magic:

Inside your scene's registerWithTouchDispatcher you need to add the standard delegate instead of the targeted one. Edit: The cocos2d templates should all have the standard delegate enabled by default rather than the targeted delegate. In that case skip to the other steps.

Replace:

 

[[CCTouchDispatcher sharedDispatcheraddTargetedDelegate:self priority: swallowsTouches:YES]

 

With:

[[CCTouchDispatcher sharedDispatcheraddStandardDelegate:self priority:0];;

If you are currently handling touch begin, moved, and ended events for single touch, then you will have to replace them with the multitouch versions:

ccTouchBegan:withEvent:event becomes ccTouchesBegan:withEvent:event

ccTouchMoved:withEvent:event becomes ccTouchesMoved:withEvent:event

ccTouchEnded:withEvent:event becomes ccTouchesEnded:withEvent:event

Note that the first argument passed to the multitouch version is a set of touches rather than a single touch.

After doing all this work you might get excited that you have your fancy new multitouch code working, only to be disappointed when you realize that it is still only handling a single touch! One last change to make: In your app delegate be sure to enable multitouch. Somewhere after you initialize and set your EAGLView, do this:

[glView setMultipleTouchEnabled:YES];

Caveat: If you are using gesture recognizers then they may be swallowing additional touches. If you have all this code in place, are using gesture recognizers, and your multitouch code isn't getting multiple touches, then the gestures are the likely culprit.

Edit: As Gaminghorror points out you need to clean up your delegates as well to prevent crashes and memory leaks with:

[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];

And if you are looking for other material to learn cocos2d go check you his site: learn-cocos2d.com.

CCParticleSystem & Particle Designer

Rolling my own particle system is one of those bad habits that I have. There comes a point when working with a new framework and/or language that I decide to build a particle system. It is usually a good exercise in building something more complex than a Hello World application. Needless to say, I did this for cocos2d. Also needless to say, it was a lot of fun but did not move the ball forward on actually getting a game built.

Cocos2d comes with a decent particle system, CCParticleSystem. Go read the documentation: http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:particles Welcome back! In all seriousness, odds are that this will meet all of your needs, it will be updated and maintained by other people, and if you really need to do something magical it is open source.

I blabbered on about the glorious merits of Texture Packer last time so I figured I would pick a new tool this time. Particle Designer, which you read all about at that link up there is an excellent tool. True to my nature, I started rolling my own and then I bought Particle Designer when I realized I had games to build. It doesn't support everything that CCParticleSystem does and there are some minor differences, but they are pretty well documented. I recommend buying Particle Designer after reading the CCParticleSystem docs. The source code for CCParticleSystem is also highly informative.

Protip: Don't be like me, read the docs and learn about the tools available.

Protip 2: Reading other people's source code will learn you good.

Next Time

I am not going to bother pretending I have a plan. I have a near limitless bag of errors for my past self to avoid, I will attempt to dig out the most useful stuff and share it. Worst case scenario, I will try something new, make lots of mistakes, and share those.

Editor's Notes:

  1. I think there is a word I forgot to use in this part of the series.
  2. Next time will likely be a few weeks away. I plan on covering a few other hopefully useful things first.
  3. There has been an underuse of taco references lately, please correct. -ed