Archive for the ‘Technical’ Category
Fun with [redacted]
Friday, August 21st, 2009
So the whole [redacted] thing is a joke in the Mac/iPhone dev community. Whenever someone wants to talk about something covered under NDA by Apple, they put [redacted] in its place. Hehe
I thought I’d write up a quick post on my experiences upgrading to a pre-release build of the new Mac OS X, Snow Leopard. We were given pre-release copies of the OS and WWDC this year. Because the details of the OS are covered by NDA, I won’t talk about any of the cool new stuff I’ve been playing with (I’m such a tease). But I when I started thinking about doing the install, I couldn’t find any good resources on how to do it properly.
This all started because of a bug in Xcode. There’s a fun bug that sometimes crops up when you assign different project names to different build configs. I do this so that I can call my debug build “DappleDebug” and my release build “DappleRelease”. This is useful when you’ve got 4 different builds on your test device and you’re trying to remember which is which. At any rate, when OS 3.0 came out, something broke in Xcode and now sometimes when you change the project name, Xcode tries to use something else. Very annoying!
I had found a work around which involves duplicating your build config, deleting the old one, then renaming the duplicate. It makes the problem go away temporarily (but it always comes back at some point!) Yesterday this problem cropped up again, except this time my work-around didn’t work, for some reason. The result being that I couldn’t deploy my build to my device at all! After a few hours of trying to get things to work (and rebooting, etc), I said “Screw it! I’m installing Snow Leopard!”
My biggest concern with updating to a pre-release OS was preserving my data, so I spent a couple of hours backing up all my important data to my server and offsite repo. My next biggest concern was making sure that all my user settings trasnfered over. This was the part I couldn’t find any information on. The installation instructions didn’t mention anything, so I assumed it would “just work”. Ha!
I started the installation process off the disk from WWDC (the dev site has a newer release, but it’s a massive download, so I figured I’d just use software updates to update after installing). After it installed, it asked me to create a user account. Huh? Where’s my old one? Hmmm…bizarre. Maybe it will pull it across when I create a new account? Maybe I’d better call it the same thing as the old one. What was the old one called? Crap! I can’t remember!
I created a username that I thought was what I had before. Oops. Wrong. It created a new user. So now I’m in a new OS with no data. Crap! Trying to avoid panicking, I decided to poke around the hard drive and look for my files. Ok, they were still there in the Users folder. Ohhhhh, that’s what my username was. Hmmm…
The installation instructions had mentioned using a tool called “Migration Assistant” to move old user profiles over. I booted that up and discovered that this tool only works to retrieve a user account from another computer or partition, not the same computer and drive.
Trying not to panick, I decided I’d run software update to get the latest build of the OS. Hmmm…and again…and again…and again…and again. Maybe I should have just bit the bullet and downloaded the newest build to start with. 4 or 5 or 10 updates (I lost count) later (each one 500MB to 1.5GB), I ran out of updates.
At this point, I was running out of options. I figured, what the hell, I might as well try creating a new user with the same name as the old one. Maybe that will work? I tried it and it did! When it detected a user folder of the same name, it asked me if I wanted to restore that user. Success!
So now I’m running the new OS, with all my old data intact. Hooray! So while it wasn’t that hard to get working, it certainly feels that way while you’re in the middle of it thinking all your data is gone.
To summarize, the lessons from this story are the following:
- If you’re going to install a pre-release build, just download the latest gigantic build off the dev site and start with that.
- Go into the System Prefs -> User Accounts and record your user account info so that you can just restore it properly the first time.
Other than those two mistakes, things went smoothly. So if you’re going to do this at home, good luck to you!
Owen
Graphics on the iPhone 3G S
Sunday, June 21st, 2009
Noel Llopis, over at Snappy Touch, has written up a great article at Mobile Orchard on the new iPhone 3G S graphics hardware (the PowerVR SGX) and what it means to us as iPhone game developers. If you’ve been curious about what’s under the hood and what it can do, give Noel’s article a read:
- A Huge Leap Forward: Graphics on the iPhone 3Gs (www.mobileorchard.com)
For those who are curious, I will be writing up a post on my experience at WWDC sometime this coming week. I’ve just needed some time to process it all.
Owen
Dapple 1.2 and NSString Leaks
Wednesday, May 27th, 2009
Dapple 1.2
I haven’t been posting very frequently lately, mostly because I’ve been working very hard to get this Dapple 1.2 update out the door. I’m thrilled to announced that I submitted it this afternoon! Dapple and Dapple Lite 1.2 were both submitted today. If everything goes according to plan, they should be available around this time next week.
The update is quite exciting, in that it adds support for global high scores lists! Now you can see how your top score compares to other people around the world! With the update you’ll be able to see your score and the 9 people ranked directly above you, or look at the top overall scores. It’s already been exciting watching the testers’ scores roll in. I can’t wait to see how everyone does!
Dapple Lite also allows you to submit your scores online, but you’ll be using the same scoreboards as those with the full version. If you want to get near the top of the scoreboards, you’ll have to buy the full version. 😉
There are also a few user interface tweaks that I’ve made and also a few little fixes. Finally, there’s also a feature for a top secret project I’ve been involved in. You’ll have to wait for the update to find out what it is!
Leaderboards
Now I want to talk a little bit about the technology that I used for the high scores lists and then talk about a nasty memory leak I tracked down today before submitting. If you’re not interested in this kind of thing, I’ll forgive you if you want to stop reading now…
When I started thinking about implementing the leaderboards, I first looked a few existing technologies, hoping that I might be able to just “drop something in” and it would work. There are a few technologies out there that looked like the might be able to do that. Where they fell short, however, was in my ability to completely customize how I wanted to store the data and display it. Plus, I thought it would be fun to write my own system.
When I started reading up on the Google App Engine, I was convinced it would be easy to set up a server back-end using that, and I was right. Using the App Engine, I had the server-side working in about 3 days of work (and that included learning Python and how the App Engine works). It’s really easy to get things running with their system.
What took time, as usual, was the user interface. I had considered creating an in-game web view that would load a web page that had the high scores on it, but I decided (perhaps somewhat foolishly) that I didn’t want the player to have to leave the look and feel of the game. I decided to build it using my existing UI framework. This only caused a few problems, mostly around trying to find space to fit all the data. However, I also ran into some nasty string memory stuff in the code I wrote to parse the data coming from the server.
I encountered a memory leak this morning that I want to share with you, because it’s not immediately obvious why it’s a leak. When I get my data from the server, it comes in as a long string of data. I use NSString’s handy methods to chop it up and stuff the results into a dictionary, then I shove those dictionaries into a big array. Here’s a bit of the code that had the mem leak:
NSArray* recordData = [[records objectAtIndex:i]
componentsSeparatedByString:@","];
NSString* username =[[NSString alloc] initWithString:
[recordData objectAtIndex:0]];
...
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
[dict setObject:username forKey:@"name"];
...
[mCachedGlobalHighScores addObject:dict];
[username release];
This causes a memory leak that is caused at first line of code. I was baffled by what was causing this. The memory leak seemed to be happening on the string inside [recordData objectAtIndex:0] but I couldn’t figure out why. I asked around on Twitter and many people pointed me to the method [myObj retainCount] that allows you to see how many retains have been made on an object. BunnyHero pointed out that you can run this from the GDB command prompt when stopped at a breakpoint: ‘print (int)[myObj retainCount]’.
I set breakpoints on the line where username is allocated, and on the line after. When execution halted just before username was allocated, I checked the retain count on the object at index 0. It was 1. This was expected. However, right after username was allocated, it became 2! Why was allocating a new string using initWithString (which is supposed to copy the string and create a new object altogether) increasing the ref count?!
I think it had something to do with the fact that the NSArray is storing the values as type id, not NSString. When you do an initWithString, I think it was just copying the pointer, and doing a retain, so the new object allocated memory, but pointed to the same object. So, in order to fix that, I had to do this instead:
NSArray* recordData = [[records objectAtIndex:i]
componentsSeparatedByString:@","];
NSString* username =[[NSString alloc] initWithFormat:@"%@",
[recordData objectAtIndex:0]];
Now the memory leak no longer happened when recordData was created…it happened when username was created! But that’s ok, now at least the leak is happening at a more reasonable place.
So now I looked at where I was cleaning up my big array of cached data:
[mCachedGlobalHighScores removeAllObjects];
Reading the docs, removeAllObjects is supposed to call release on each removed object. I assumed that would do a deep release. Not the case. So I tried this instead:
for (int i = 0; i < [mCachedGlobalHighScores count]; i++)
{
   NSMutableDictionary* record =
[mCachedGlobalHighScores objectAtIndex:i];
   [record removeAllObjects];
}
[mCachedGlobalHighScores removeAllObjects];
But that still didn’t work. The username string wasn’t been released! So finally, I did this:
for (int i = 0; i < [mCachedGlobalHighScores count]; i++)
{
   NSMutableDictionary* record =
[mCachedGlobalHighScores objectAtIndex:i];
   NSString* name = [record objectForKey:@"name"];
   [name release];
   [record removeAllObjects];
}
[mCachedGlobalHighScores removeAllObjects];
And it works! You might be asking why I didn’t do an autorelease on the username NSString. I tried that. It worked most of the time. But if you flipped back and forth on the screen quickly (causing everything to delete and recreate quickly), I found that I could get into situations where sometimes that string wouldn’t release at all, causing a leak. I decided that if I managed the memory myself, at least I’d know what’s going on.
So, the lessons are:
1) If you are dealing with a string that’s stored as type id, make sure you’re careful about how you use it to init new strings.
2) When you alloc (or retain) you have to release. (Yes, I know that should be obvious by now, but sometimes you think a built-in method is releasing or retaining when it isn’t). 🙂
Well, apparently I’ve decided to make up for a lack of posts with one gigantic post. Look for more news on the Dapple 1.2 update when it gets approved. And keep your eyes and ears open for news on that secret project I may or may not have mentioned…
Owen
Meet-Up Thoughts and Dapple Update
Friday, May 15th, 2009
Is everyone (in Canada) ready for the long weekend? I know I am! I thought I’d write a post before I head up to my parents’ cottage for the weekend, where I won’t have a computer, let alone access to the internet.
Wednesday night we had the 2nd Toronto iPhone Developer Meet-Up, which was a great success in my mind. We had two great presentation by local iPhone devs. James Eberhardt was able to secure us space at the Rich Media Institute, which turned out to be perfect. There was beer that could be bought, and we had a great conference room with a projector for the presentations. I’m extremely pleased with where the meet-up seems to be heading. I think everyone who came, that I talked to, enjoyed the format. I think we’ll try to stick with the format of two short (15 mins or less) presentations, followed by socializing and drinks.
If you missed the meet-up, the presenters have graciously posted their presentation slides on their sites.
- Rules You Should Break in the App Store (www.xinsight.ca – PDF) – Jason Moore from xinsight gave a presentation on tips and tricks for making the most of the App Store.
- iPhone + Box2D (blog.zincroe.com) – Luke Lutman from zinc Roe Design gave a presentation on using the open source Box2D physics engine with iPhone. He has posted his short presentation slides (that we saw) as well as a longer presentation and an example source code project.
A big thanks to both presenters! It was a great way to kick off the new format for the meetings. I think everyone enjoyed it.
If you haven’t already, please join the Facebook group for the meet-up. It’s called Mobile Developer & Designers of Toronto. I look forward to seeing you in June!
As I mentioned previously, I had be writing a chapter for a book on iPhone development. I’m pleased to say that I submitted my first draft to the publisher this week. As such, I was able to do some coding towards the end of the week. Yay!
I started working on stuff for the Dapple 1.2 update. The biggest new feature will be the inclusion of world-wide leaderboards for the game. I’ve decided to use Google’s App Engine as the server-side technology, as it scales nicely and it means that if I did somehow introduce some kind of server-side security flaw (which I don’t think I have), it’s not running on my own web server.
I’m two days into things and I’m quite impressed with the App Engine stuff. Sure, I had to learn Python pretty quickly, but at this point the server-side stuff is pretty much done. I’m sure it will need some tweaking once I actually hook the game up to it, but all my tests are working. I can add high scores to the database and I can retrieve them in a few different ways. Today I’ll be starting to work on the game-side implementation, which will probably be more work than the server-side. I have to make a few UI changes in the game to support local vs. global high scores and handle all the error cases around connecting to the server. I’m hoping to have something I can send out to testers by mid-late next week.
It feels good to get back to coding again. There are a few other things I want to put into the 1.2 update as well. I’m hoping to submit to Apple by the end of the month. After that, I’ll actually be able to start working on my next project. Shocking!
Have a good (long) weekend everyone!
Owen
Dapple Update (pun intended)
Friday, March 27th, 2009
I haven’t really been posting much this week because I’ve been working on Dapple. I thought I’d provide a little update on what I’ve been doing. I’m working on a Dapple Update. Yes, the word update is confusing in this first paragraph. Let me try that again: I’m working on version 1.1 of Dapple and Dapple Lite.
There were a couple of key things I wanted to address with Dapple’s first update:
1) Reduce app footprint to under 10MB
When an app is over 10MB, users with a cell phone connection aren’t able to download the app directly. Apps that are over 10MB require a Wi-Fi connection to download. I think this is potentially costing me quite a few downloads, mostly of the Lite version. I want the Lite version to be something that people can download on a whim. If they have to wait until they have Wi-Fi access, they might never download it.
Dapple and Dapple Lite were both about 13.1MB, so I needed to reduce them by nearly a 1/3. When I looked at the sizes of my various directories, it became clear that the music and SFX files were the ones causing the problem. Dapple has about 13-14 minutes of music that plays in the game. That amounts to significant file sizes. There are also 14 SFX files.
I’m going to get a little bit technical here, so if you don’t care for this stuff, just skip ahead a bit. The iPhone does have a hardware audio decoder. You can use it to hardware accelerate your MP3, AAC, etc decoding. However, it can only operate on one file at a time. This means if you’re ever going to play more than one sound at once, you have to use two forms of audio decoding. For Dapple, I use AAC compression for the background music, but all the SFX files need to be in a non-encoded format: Linear PCM. This means that the SFX files are huge, because they’re basically 16-bit uncompressed audio data.
For v1.0 of Dapple, all the music was encoded as AAC, 128kbps, constant bit rate, so that it sounded really good. I decided I would try to reduce the bit rate as much as possible without sacrificing the audio quality too much. By switching to a variable bit rate, I was able to compress the files much more without too much of a quality reduction. By doing this, I was able to reduce the music file sizes from 8.7MB to 6MB! Almost there already!
That was the easy part, now I needed to deal with my SFX. The iPhone documentation infers that if you’re not using a hardware decoded format you should be using Linear PCM or IMA 4:1. IMA4 (which I’ll refer to is as from now on) is a compression format for audio that results in a nearly 4:1 compression (it’s actually closer to 3.75:1). I converted all of my SFX files to IMA4 and was surprised to discover that none of them played. This is because there’s no native support for IMA4 on the iPhone. You can use it, but you have to write your own decompression code. I ended up scouring the net looking for information on the IMA4 data format, how the compression works, and how to decompress it. I found several partial resources, none of which were complete enough to just let me implement it. It took me two solid days of some pretty hardcore coding (it was really fun, actually), but I finally got IMA4 working in Dapple. The nice thing about IMA4 is that the file gets decompressed when it’s loaded into memory, and is then played as Linear PCM. This means there’s no performance hit, except on file load. (I’ll link to some of the IMA4 resources I found at the bottom of the post)
IMA4 was a huge win. My SFX files totaled about 2.2MB on disk before compression. After compression, they’re just over 600KB, with almost no loss in sound quality.
So, after rebuilding and zipping, both Dapple and Dapple Lite sit at about 9.5MB! Hooray! What’s next?
2) Fix an audio bug
This is the only bug that’s come up more than once since the game launched. If a user is playing their own iPod music while playing Dapple, then locks their device, their iPod music stops playing. This was a problem in how I was managing audio sessions in the game. This has now been fixed.
3) Add a Feedback Button
I want to make it easier for people to provide me with feedback on the game. I changed the Main Menu around so that the “Credits” button now says “Extras”. This opens a new menu which has “Feedback” and “Credits” in it. The feedback button, when clicked, launches the Mail program and lets the user send me an email. Hopefully people will use it to request features, complain about things they don’t like, etc.
4) Add a computer-controlled opponent for 2 Player Mode
This is a request I’ve had from a lot of different players, so I decided to try to put it into the first update. As of last night, I can actually play a game against a computer opponent. It’s pretty cool. However, the computer opponent is really stupid right now.
The way Dapple already works is that when a player starts their turn, the game searches the board and picks a random spot that will make a match. This is stored for when the hint arrow needs to be displayed. I figured I could make use of that code and just make a computer opponent execute a move there on its turn, instead of displaying a hint arrow.
This lead me to an interesting discovery: it works, the computer can play, but the computer opponent sucks at the game. In 10 games I played against the computer last night, I won every single game. What I found interesting about this is that this proves that making random moves in Dapple isn’t nearly as effective as thinking your plays through. I’ve had a small number of negative reviews (either on the web, or on the App Store), and the complaint is usually that they feel like playing randomly yields the same results as thinking their moves through. With a random computer opponent pitted against a player who thinks their moves through, the human player will almost always win. To me, this is proof that the game can be learned, that you can get better at it, and that strategy will win over randomness. Up until now, that’s what I believed, but I couldn’t prove it…
So today my goal is to try to encode some of the choices I make when looking for the best play to build a heuristic that the AI opponent can use. Then I’ll need to tune how much it uses that heuristic for various difficulty levels. Should be fun!
With any luck, I should be able to submit Dapple and Dapple Lite v1.1 by Monday.
Owen
IMA4 Resources:
- www.wooji-juice.com – A good starting point, as this article walks through the high-level steps required to get IMA4 decompression working. However, this article leaves out a lot of steps that are necessary to get things working. It is also not very clear on the differences between mono and stereo audio streams and how that affects the packets.
- wiki.multimedia.cx – Contains useful optimizations that allow you to avoid doing byte->float conversions to do floating-point math.
- wiki.multimedia.cx – Has some useful information on byte/nibble structure of the packets
- www.koders.com – Actual C code for doing the decoding. This is for quicktime IMA4, so some of the logistics of how you extract chunks of data won’t work, but the actual math for decoding each nibble into a 16-bit frame should be the same for you.





