Taxes!

Whew, the process of creating an iOS binary and uploading was not as simple as I thought! Uploaded now though and waiting for review. :) A bit of annoying news, we can’t release the game in the Canadian app store until our Canadian tax form is approved. I’m not sure how long that will take, but I’m thinking it won’t be quick! I believe it should be available in the US as soon as they give the ok!

:wq

Done and done! We’re going to package this up and send BlockHopper out to Apple tomorrow! I still have to run through all the documents and find out what needs to be done for submission. Hopefully not too much!

The sound effect saga had one final chapter. It turned out when I used the second method, only one sound effect at a time could play haha. Not so good! I finally did what I should have done in the first place: added OpenAL. (thanks Raj!) It really only took about an hour to get it all up and running and the hardest part was actually converting all the wavs to caf files in the correct mono format. :D Ben Britten explains the process of setting up OpenAL and playing a sound better than I ever can!

We’re pretty excited right now. :D

Sound Effects Revisitied

Ok, so it turns out building a sound effects class around AVAudioPlayer is a bad idea as it’s pretty slow. The game became really choppy as the sound effects were getting fired off and I’m not the world’s best programmer, so I need all the help I can get! :D System Sound Services to the rescue! There are a few drawbacks to using System Sounds like the sounds can’t be over 30 seconds and they must be in WAV, AIF, or CAF format. Not really a big deal for sound effects and I’m still using AVAudioPlayer for the background music.

The reimplemented SoundPlayer class looks much the same as before:


#import "SoundPlayer.h"

@implementation SoundPlayer

@synthesize isFinished;

void audioDidFinish(SystemSoundID sound, void *isFinished);

- (id)init
{
    self = [super init];
    isFinished = true;
    return self;
}

- (void)play:(SystemSoundID)sound
{
    isFinished = false;
    AudioServicesAddSystemSoundCompletion(sound, NULL, NULL, audioDidFinish, &isFinished);
    AudioServicesPlaySystemSound(sound);
}

void audioDidFinish(SystemSoundID sound, void *isFinished)
{
    *(BOOL *)isFinished = true;
    AudioServicesRemoveSystemSoundCompletion(sound);
}

- (void)dealloc
{
    [super dealloc];
}

@end

Something to note: The callback function that fires when the sound finishes is just straight C, not Objective-C. You’ll have to pass any class variables you want to manipulate as the last parameter of the AudioServicesAddSystemSoundCompletion function.

And to initialize the System Sound IDs:


for (i = 0; i < MAX_SOUNDS; i++)
{
    NSString *filePath = [[NSBundle mainBundle] pathForResource:[soundPaths objectAtIndex:i] ofType:@"wav"];
    AudioServicesCreateSystemSoundID((CFURLRef)[NSURL fileURLWithPath:filePath], &soundEffects[i]);
}

One last little thing, the System Sounds will play through the phone’s speaker out of the box, but to get them also to play through headphones, you’ll have to add this bit of code. I just stuck it in the viewDidLoad function.


AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryAmbient error:NULL];

Meeeemories.

Yesterday, I was checking our game for memory leaks and leared a few things! It is the most terrible of sins to create an uninitialized object such as:


connection = [NSURLConnection alloc];

The game kept crashing in iOS4.2 as I was trying to release an uninitialized object. Unfortunately, iOS5 had no problem with this, so I’m glad we have an iPhone and an iPod Touch to test different versions!

I also had a pretty major memory leak in the object that plays the back ground music. My initial plan was to alloc once and keep the instance around so I was calling initWithData each time the music changed. It never ocurred to me that initWithData might also be creating objects and I was never releasing the instance. Oops. All fixed now.

Regarding total progress, I have a small list of about 10 things left to do and then give it some last-minute testing and we should be ready to roll!

We Be Networking

Oh man, writing network code is the pits haha. This is pretty much my first time and it can be overwhelming at times trying to make sure you’ve covered all the exceptions and errors. We have a level editor built into our game and you can upload the levels you create. In order to keep some kind of quality control, we thought it would be best if everyone had to have a username to upload. Extending this idea, we thought it would be good to tie the username into the greenpixel.ca forums. It all works fairly smoothly, but the downside is I had to add logins and registration to the app. This spawns a number of cases like:

Try to upload -> is there a saved username?
      Yes -> upload and handle errors
      No -> go to login screen. Do you already have a username pass?
           Yes -> Upload and handle errors save credentials
           No -> Register new user. Is the username or email unique? etc etc.

So much sending and receiving and popping up error messages. :D

Looks like it all works now, but I will have to test it all strenuously later and try to simulate the phone losing its network connection.

The networking portion is fairly straightforward, in order to talk to a server, you just need to create an NSURLConnection object. I chose to make a global object that keeps getting initted as I wanted the ability to cancel it.


NSURLConnection *connection = [NSURLConnection alloc];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://www.website.com/php/dothings.php?id=%d", id]]];
[request setHTTPMethod:@"GET"];
connection = [connection initWithRequest:request delegate:self];

Which sends a GET request. You can attach simple variables to the URL itself, but for more complicated variables, like strings that contain spaces or &, you’ll need to use the function “stringByAddingPercentEscapesUsingEncoding” in the NSString class. Maybe it’s best to use it anyway!

There are 4 delegate methods you have to implement if you want to get anything back from the server. They are:
…didReceiveResponse
…didReceiveData
…didFailWithError
…didFinishLoading

(the exact method names are found in the NSURLConnectionDelegate class)

You’ll need a global NSData object to collect the data as it is returned from the server. This should be reset each time:


NSData *serverData = [[NSMutableData alloc] initWithCapacity:0];

...didReceiveResponse
{
    [serverData setLength:0];
}

The data is received in chunks if the total size is more than the iPhone can handle. Our map data has this problem, so just keep appending to the serverData object:


...didReceiveData:(NSData *)data
{
    [serverData appendData:data];
}

This will keep getting called until it is done. When everything is finished, either …didFinishLoading or …didFailWithError will get called. Then it is time to decide what to do with the data.

Something to keep in mind! The minimum timeout for an iOS app is 240 seconds regardless of what you set it to. 240 seconds is a ridiculously long time to realise the server isn’t going to talk to you. Instead of setting a timeout, I just run a counter in the gameloop. If the counter exceeds a timeout limit, I pull the plug and run the connection fail code.

Sorry if this is hard to read or unhelpful, my brain is fairly fried right now! Time for some Mega Man 2 and then sleep.