Skip Navigation

Ramblings from the team at zinc Roe

Metal Fish Eggs

AVAudioPlayer Memory Leak

While coding the sound for Arctic Shuffle 2, I ran into a non-obvious memory (to me, anyway) memory leak related to the AVAudioPlayer that took forever to track down.

Here was my original code, which leaks if an error occurs in the initWithData:error: method:


- (AVAudioPlayer *)audioPlayerWithContentsOfFile:(NSString *)path {
    NSData *data = [NSData dataWithContentsOfFile:path];
    return [[[AVAudioPlayer alloc] initWithData:audioData error:NULL] autorelease];
}

If an error occurs in the initWithData:error: method (say because the file is missing), it returns nil. If it returns nil, autorelease gets called on nil, not on the AVAudioPlayer object, and the AVAudioPlayer object gets leaked.

Here’s the leak-free version:


- (AVAudioPlayer *)audioPlayerWithContentsOfFile:(NSString *)path {
    NSData *data = [NSData dataWithContentsOfFile:path];
    AVAudioPlayer *player = [AVAudioPlayer alloc];
    if([player initWithData:audioData error:NULL]) {
        [player autorelease];
    } else {
        [player release];
        player = nil;
    }
    return player;
}

The leak-free version stores the pointer returned by alloc, rather than the pointer returned by initWithData:error:. That way, whatever happens, the player can still be released.

Read the full post1 Comment

Artifacts in small textures on the iPhone

For the next version of Arctic Shuffle, we’re using Open GL to speed things up. One problem we’re run into along the way is strange artifacts in textures smaller than 64 pixels by 64 pixels. After a lot of head scratching, an obscure forum post saved the day. It turns out there’s a bug in Apple’s GLSprite sample code (where our texture loading code came from).

Here’s the important bit from our texture loading code:

if(image = [UIImage imageWithContentsOfFile:aPath].CGImage) {
  width = CGImageGetWidth(image);
  height = CGImageGetHeight(image);
  data = (GLubyte *) malloc(width * height * 4);
  context = CGBitmapContextCreate(data, width, height, 8, width * 4, CGImageGetColorSpace(image), kCGImageAlphaPremultipliedLast);
  CGContextDrawImage(context, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), image);
  CGContextRelease(context);
  glGenTextures(1, &name);
  free(data);
}

I have literally no idea how malloc works, but from what I gather it doesn’t clean out the memory that it allocates. For some reason, the memory allocated for textures smaller than 64 pixels by 64 pixels has a bunch of garbage in it, which shows up as artifacts in the texture. Switching from malloc to calloc, which explicitly sets all the bits in the allocated memory to zero got rid of the problem.

Here’s the working code:

if(image = [UIImage imageWithContentsOfFile:aPath].CGImage) {
  width = CGImageGetWidth(image);
  height = CGImageGetHeight(image);
  data = (GLubyte *) calloc(width * height, 4);
  context = CGBitmapContextCreate(data, width, height, 8, width * 4, CGImageGetColorSpace(image), kCGImageAlphaPremultipliedLast);
  CGContextDrawImage(context, CGRectMake(0.0, 0.0, (CGFloat)width, (CGFloat)height), image);
  CGContextRelease(context);
  glGenTextures(1, &name);
  free(data);
}

Happy texturing!

Read the full post1 Comment

Units in Box2D

There were some good questions during my presentation last night and via email this morning about units in Box2D.

@jasonkrogh asked what kind of units Box2D uses. Here’s the relevant bit about units from the Box2D manual:

Box2D works with floating point numbers, so some tolerances have to be used to make Box2D perform well. These tolerance have been tuned to work well with meters-kilogram-second (MKS) units. In particular, Box2D has been tuned to work well with moving objects between 0.1 and 10 meters. So this means objects between soup cans and buses in size should work well.

When you’re working with on screen objects like balls and squares, what’s important is that the units are not pixels.

@satefan asked:

For my little demo I set the dimensions of the world to 320 meters wide by 480 meters high. This seems to work fine and I don’t have to do any coordinate translations. Just curious… was there a reason why you decided not to do this?

The short answer: the physics won’t look right because your bodies and shapes will be much larger, heavier and faster than Box2D is expecting. Here’s what the manual has to say:

Being a 2D physics engine it is tempting to use pixels as your units. Unfortunately this will lead to a poor simulation and possibly weird behavior. An object of length 200 pixels would be seen by Box2D as the size of a 45 story building. Imagine trying to simulate the movement of a high-rise building with an engine that is tuned to simulate ragdolls and barrels. It isn’t pretty.

Let’s do the math for the the balls in the example game on the iPhone. If we set the world size to match the screen, then the world would be 320 meters across, 480 meters tall and 1 pixel on screen would equal 1 meter in Box2D. If the ball had a diameter of 64 pixels (which is does), it would have a diameter of 64 meters in the physics engine – that’s pretty small on screen, but more than six times the size of Box2D’s recommend 10 meter maximum.

Here’s an example of where this becomes a problem: Box2D has setting for the maximum velocity any body can have and the default value is 200 meters per second (you can see all of Box2D’s settings in b2Settings.h). Because things are so big, they’re also very heavy (think about how much a 64 meter wide ball would weigh!). To make them move, you have to crank gravity way, way up and apply a lot of force. You quickly run into the limit and everything ends up moving around at the same speed (which looks terrible, trust me). Rather than fiddling with the (many) settings, the best thing to do is a simple translation between screen coordinates and world coordinates:

// MyGameViewController.h

#define SCREEN_TO_WORLD(n) ((n) / 30)
#define WORLD_TO_SCREEN(n) ((n) * 30)

In the macros above, 30 is an arbitrary value. The idea is to choose a value that will convert the size of an object in pixels to a value that falls in the 0.1 meter to 10 meter sweet spot. By dividing the ball’s 64 pixel screen diameter by 30 in the macro gives it a diameter of 2.13 meters in the world. What is a relatively small object on screen is now a relatively small object in the world – it also weighs less, so you can use smaller forces and don't run into the maximum velocity limit.

Just make sure that any time you set any linear value (position, size, force) in the world, convert from screen coordinates to world coordinates. When you read any linear value from the world, convert from world coordinates back to screen coordinates. I like to use custom accessors to make it easier:

// Ball.m

- (CGPoint)position {
  if([self body]) {
    return CGPointMake(
      WORLD_TO_SCREEN(body->GetPosition().x),
      WORLD_TO_SCREEN(body->GetPosition().y)
    );
  } else {
    return CGPointMake(
        WORLD_TO_SCREEN(bodyDef->position.x),
        WORLD_TO_SCREEN(bodyDef->position.y)
    );
  }
}

- (void)setPosition:(CGPoint)aPosition {
  NSAssert(![self body], @"Cannot set position");
  [self bodyDef]->position.Set(
    SCREEN_TO_WORLD(aPosition.x),
    SCREEN_TO_WORLD(aPosition.y)
  );
}

Read the full postLeave a Comment

iPhone and Box2D

For the next version of Arctic Shuffle, we’re using an open source 2D physics engine called Box2D to improve the gameplay. There seems to be a lot of interest in Box2D for the iPhone, but I haven’t found a great overview anywhere, so I’ve put together a presentation for tonight’s Toronto iPhone Developer Meetup.

Here are the slides and the source code for the example game I’ll be using in the presentation. I’ve included two versions of the slides, a short version that I’ll be using tonight, and a longer version that covers collision detection and using the accelerometer. The source code includes everything described in the longer version.

Read the full post5 Comments

Project Organization and Coding Conventions for iPhone Games

Now that we’re knee deep in iPhone game development, we’re working with a number of different outside developers. Since everyone involved is pretty new to Objective-C, XCode and iPhone development, project organization and coding conventions in our projects have been all over the map. To improve the quality of our projects and make them easier to maintain and update, here’s a list of things to keep in mind when developing casual games for the iPhone. Most of them are common sense, but going back to basics every now and then never hurts.

  • Remember that somebody else (who may not know all the ins and outs of XCode) will be working on the project with you. Take the time to keep things neat and organized. Create a Classes folder with all your source code and a Resources folder with nib files, images, sounds, etc. Keep the organization of the project and the organization of the files on disk as close as possible, it’s less confusing for everyone. Give all of your files consistent descriptive names. Delete files that aren’t being used anymore (Tip: Make bundles for any resources that don’t need to be compiled, like images and sounds. XCode doesn’t keep track of the contents of bundle files, which means when you add/remove/rename files in the bundle XCode will automatically see the changes so you don’t have to remember to add and remove them from the project). Simple stuff, but it makes a big difference.
  • Pushing updates to the App Store is a proven way of improving a game’s visibility, so make sure the project and the code are easy to update. Remember to slow down and do things well.
  • Use Foundation and UIKit classes wherever possible — it helps keep user interface elements consistent and it’s easy for another developer to quickly see what’s going on and make changes without learning another 3rd party framework. For example, use a UIView with UIButtons for game screens. The exception to the rule here is the game engine itself, where the benefits of using OpenGL or other open source code are worth the trouble.
  • Use Interface Builder wherever possible — this goes hand-in-hand with the last point. Using UIKit classes and Interface Builder makes it easy to adjust the layout of screens, labels for buttons, etc. without hunting through the code. Remember, the person making the adjustments may not be a programmer.
  • Use UILabels for interface text, rather than embedding text in images. Keeping text out of images makes the project much easier to update and localize. Using native text for everything also helps keep things consistent visually – your high score screen is going to need dynamic text and the app will look better if it matches everything else.
  • Memory management is very high priority on the iPhone. Use as little memory as possible. Don’t load things into memory until you need them. Release things from memory when you’re done with them. For example, rather than loading all of a games screens when the app launches and keeping them in memory for the life of the app, load each screen when it’s going to be shown and release it from memory after it’s hidden.
  • Test the app regularly in Instruments during development to find memory leaks and avoid wasting memory:
    • Check using Leaks — This will help you find good, old-fashioned memory leaks.
    • Check using Object Allocations — An object in memory may not technically be a leak, but if it doesn’t need to be there it’s wasted memory. When you’re playing the game, are any of the objects associated with the other screens in memory? When you’re showing the game over screen, are any of the objects associated with the game engine in memory?
    • Check using Memory Monitor — Apple doesn’t specify a hard limit on the amount of memory an app can use, but the general consensus is that 15Mb to 20Mb is fairly safe. If your app is using 25Mb of memory you should be concerned. If your app is using 45Mb of memory you have a very big problem.
    • Check OpenGL Texture Memory — OpenGL on the iPhone has a soft limit of 24Mb for all textures. If you go above the limit, there’s a massive performance hit. See: How To Check iPhone Texture Memory Usage With Instruments
  • Follow standard Objective-C naming conventions. They’re different from other languages, so take the time to read up on them. See: Coding Guidelines for Cocoa, Cocoa Style for Objective-C: Part I, Cocoa Style for Objective-C: Part II
  • Put the dealloc method at the top of the @implementation block for every class you write. dealloc is the most important method in every class, so show it some love.
  • Use accessor methods for everything — it’s the best way to avoid simple memory management bugs. Use accessor methods within classes, too. You can define private accessor methods in a category at the top of your implementation file. You can redefine a readonly property in the public interface as a readwrite property in the private category.
  • Mark properties as nonatomic unless you have a good reason not to.
  • Use square bracket syntax (i.e. [self foo], [self setFoo:@"bar"]) instead of dot syntax (i.e. self.foo, self.foo = @"bar") for accessing properties. Using one syntax keeps your code consistent, and makes it easier to see where you’re accessing a property of an Objective-C object as opposed to a member of a C struct or C++ class (which use dot notation).
  • Avoid subclassing UIViews. Build the view hierarchy in Interface Builder and put your logic in a UIViewController. If you have a lot of UIView subclasses, you should be concerned.
  • Remember to mark properties as IBOutlets and methods as IBActions wherever it makes sense.
  • Organize related methods and properties into groups in the source code (i.e. the UIViewController subclass for a game screen might have groups like button properties, action methods and overridden UIViewController methods). Mark the groups in both the header and the implementation using #pragma mark Label. Organize methods within a group alphabetically. You’ll spend a bit of time organizing things and a lot less time looking for things.
  • Use NSAssert() liberally. If you intentionally stop the app when something unexpected occurs, it’s easier to find where the problem started and fix it. It takes a bit of time to write the assertions, but I guarantee it takes less time than tracking down a bug later on.

The list above is by no means definitive, but it covers the basics. Let me know if I’ve left out anything obvious, or if there’s something that rubs you the wrong way. I’m always happy to learn a better way to do something.

Read the full post1 Comment

How To Add Box2D To An iPhone XCode Project

Using Box2D is a great way to add physics to a 2D iPhone game. Since Box2D is written in C++ rather than Objective-C, adding it to your XCode project and getting it to build cleanly can be a bit tricky. If you just want to get aquainted with Box2D, there is an sample XCode project in the Box2D svn repository that includes the Box2D testbed and basic debug drawing support for the iPhone. Once you get past some quick experiments, you’ll probably want to add Box2D to a clean XCode project with a minimum of cruft. It’s not tough, but there are a couple of gotchas along the way. Below are instructions for adding Box2D to a new iPhone XCode project and getting that project to build cleanly on a device (note: the project doesn’t actually do anything, that’s where you come in…).

The instructions below assume you’re a registered iPhone developer and have:

  • Mac OS X 10.5
  • XCode 3.1.2
  • iPhone SDK 2.2.1
  • Box2D 2.0.2
  • A valid app id and development provisioning profile that you can use to build the project for a device.

So, for my future reference and yours….

  1. Create a new XCode project. I’m creating a View-Based Application called MyGame on my desktop.

    01

    02

  2. Open the terminal and export Box2D 2.0.2 from the Box2D svn repository into your project folder:

    svn export https://box2d.svn.sourceforge.net/svnroot/box2d/tags/Box2D-2.0.2 ~/Desktop/MyGame/Box2D-2.0.2

  3. Right click on MyGame under Groups & Files and choose Add > Existing Files…

    04

  4. Browse to the your project directory, select the Box2D-2.0.2 folder and click Add.

    05

  5. Make sure the Copy items into destination group’s folder checkbox is unchecked and click Add.

    06

  6. Expand the Box2D-2.0.2 group and delete everything except the Include and Source groups. Whether you move the files to the trash or just delete the references is up to you.

    07a

    07c

  7. Expand the Source group and delete the Makefile.

    08a

    08b

  8. If you haven’t already, set the app id (in Resources/Info.plist) and the Code Signing Identity in the project build settings (in Project > Edit Project Settings > Build). Make sure you have Device 2.2.1 and Debug selected in the menu at the top left corner of XCode (anyone know what this menu is called?). Finally, build the project (Command-B).
  9. Open up the build results (Command-Shift-B), and you’ll see a whole bunch of errors, along the lines of:

    error: 'finite' was not delcared in this scope

    09

  10. Open the project build settings (Project > Edit Project Settings > Build) and find the Other C Flags setting. Add the following flag, which tells Box2D that it’s being compiled for the iPhone so that it knows where to find the standard C++ libraries.

    -DTARGET_OS_IPHONE

    10

  11. Build the project again (Command-B). It will build cleanly, but we’re not quite out of the woods yet.
  12. Open MyGameViewController.m and add the following line at the top of the file:

    #import "Box2D.h"

    11

  13. Build the project again (Command-B). Yay, more errors:

    error: initializer element is not constant

    12

  14. Select all of the .m files in your project (yes, all of them).

    13a

  15. Click the Info button in the toolbar, go to the General tab and find the File Type dropdown.

    13b

  16. Change the filetype from sourcecode.c.objc to sourcecode.cpp.objcpp. This tells XCode to compile the files as Objective-C++ (mixed Objective-C and C++ code) instead of plain Objective-C code.

    13c

  17. Build the project again (Command-B). That’s it!

So there you go, you’re ready to start using Box2D on the iPhone. Just remember to change the file type from sourcecode.c.objc to sourcecode.cpp.objcpp for any .m files you add to the project.

Read the full post1 Comment

Storybuilder – A History

Flashcan on the TPL Kidsspace

This is a little story about a storybuilder and a project that came and went and came back again. Way back when the universe was young and zinc Roe was operating out of a bachelor apartment we created this thing called flashcan. It was a simple tool that let users drag and drop artwork onto a background and add text. This was pretty nifty stuff for the year 2000. We approached some clients and ended up licensing it to Teletoon and Family Channel.

Flashcan caught the attention of a team working at the CBC and soon I was in Vancouver working with the cbcr3 team to rebuild it as the CBC storybuilder. Shortly after the launch of the most-beautiful cbc4kids.ca site the project was cancelled and it disappeared into the ether. But not long after we started working with the Toronto Public Library. Once again we went back and improved things and launched it as part of the new KidsSpace website. The library also uses the system for their Summer Reading Club sites. This summer it will showcase the work of Werner Zimmerman.

In each of its various incarnations the storybuilder idea has resonated with kids and each project was just as rewarding as the last. For us, it also brought us in contact with some especially gifted folks – the artists who created the assets, the designers who helped shape the interface, the teachers and educators who integrated it into their classrooms and the kids who continue to produce crazy and amazing work with these tools.

Read the full postLeave a Comment

How to check iPhone texture memory usage with Instruments

When developing iPhone games, one of the biggest performance optimizations you can make is to use OpenGL ES for rendering (instead of CoreGraphics or UIKit). For a 2D game like Arctic Shuffle, this means loading all of the animation into Open GL as textures, and drawing them onto simple rectangular shapes.

There are two caveats to this approach. First, disk access on the device is dirt slow, so you can forget about loading textures on the fly – all of the textures need to be loaded before gameplay begins. Second, OpenGL ES on the iPhone has a texture memory limit of 24MB. If you try to load more than 24MB of textures (including any buffers, like the framebuffer), the device has to start swapping them in and out of memory, and you go over a performance cliff, so to speak.

With that in mind, it’s very helpful to know how much texture memory is being used by your game – this is where Instruments comes in (the instructions below assume that your device is plugged into your development machine and you already have a working debug build of your game on the device).

1. Open Instruments (/Developer/Applications/Instruments.app) and choose the “Blank” template.

Step 1

2. In the bottom-left corner of instruments, click on the gear icon, then choose Add Instrument > Memory Monitory. Click the gear icon again and choose Add Instrument > OpenGL ES.

Step 2

3. Click on the OpenGL ES instrument’s info icon, and make sure that GARTResident Object Size is checked.

Step 3

4. Choose the device and application you wan to check. My device is named Barracuda and the application is ArcticShuffle2.

Step 4

5. Click the record button. This will launch the application on the device, and start monitoring it’s memory and OpenGL ES usage. The GARTResident Object Size column shows the amout of texture memory used, in bytes. Divide that by 1024 (i.e. amount / 1024 / 1024) to convert the number to megabytes. Notice how the Frames Per Second drops dramatically while the textures are loading.

Step 5

Happy Optimizing!

Read the full post6 Comments

Arctic Shuffle Feedback

Since we launched Arctic Shuffle we’ve been keeping a running list of feature ideas for future versions. Ideas have come from our own experience playing (useful), talking and watching friends and colleagues play (more useful), iTunes reviews (mostly useless), reviews on app review sites (mixed bag), and emails from users (all over the map).

When we’re weighing feature ideas they have to pass through a few basic filters - how long will it take to implement? will it have an adverse affect on another aspect of the game? will it translate into happy players (read: sales)?

I’ll cut to the chase and share a few of the items that are on our ‘no-brainer’ list:

  • performance: we’re rewriting it all to use openGL which will go a long way toward addressing the ‘laginess’ of the current version.
  • scoring system: once you unlock a level there’s not a whole lot of reason to go back and play it again. The next version will award a score based on how quickly a level is cleared and how many penguins were used.
  • launching UI: it’s very hard to get a sense of the power of your shot in the current version because the information you need is obscured by your finger. We’ve drawn up half a dozen ideas on how to improve on this. Now we just have to pick one.
  • better help: we hate help. we like to just play and learn by doing. Apparently not everyone is like this so we’re going to expand on the help to explain more explicitly how some of the obstacles work.
  • keep the tunes rolling: this is our bad. the current version of the game stops your tunes when it launches so there is no way to listen to your own playlist while you play. Maybe you like boy band action with your penguins or maybe a little Einstürzende Neubauten? Who are we to stop you?!

Of course there are many more items on our list - more obstacles, more sophisticated collision detection, power ups and other fun stuff. Now we just need to see how many of them we can cram into the next few weeks of development.

Read the full postLeave a Comment

Museums and the Web

Looking forward to attending Museums and the Web conference this year. Last year was the first time I attended and I really enjoyed all the lively discussions around how museums and galleries can take advantage of new media to educate and promote. Far more interesting was discussion of how these institutions can learn from their audiences (shocker, I know).

This year I will be heading to Indianapolis along with Davin. He and I will be showing off Softcover, a new software product designed to allow institutions to annotate material from their archives and share it online or on kiosk systems. More on that soon.

Read the full postLeave a Comment

 Page 1 of 4  1  2  3  4 »