The game I’m currently working on is a color-matching game, where the puzzle tiles are arranged on a grid. Obviously, the data structure you’d use for such a game is an array. While you could use a regular C array, NSMutableArray has a few advantages. It’s true that NSArray/NSMutableArray are slower than regular C arrays, but in most (or all) cases, you won’t see any performance issues because of it.
First of all, there are lots of convenience methods in the NSArray/NSMutableArray classes that make it easy to be lazy. If you’re used to working with arrays in scripting languages, you probably don’t want to go back to using C-style arrays. NSArray allows you to easily search/sort your data. Second, NSArray can be easily serialized, which allows you to save/restore your data in order to support multitasking. Nonogram Madness uses an NSArray to store the player’s game when the app moves to the background.
Anyway, here are a few tips that I’ve found whilst mucking around with NSMutableArray… hopefully you can use them in your next puzzle game.
The – removeObjectAtIndex: method will remove the specified object from your array, but then will also shift the indices of the elements beyond index down by one, filling the gap that was created. So, to perform logic that behaves like PHP's array_shift, you could do something like this:
id myObject = [myMutableArray objectAtIndex:0];
[myMutableArray removeObjectAtIndex:0];
You can easily use just one array to simulate a 2D array. This has an advantage when working with NSMutableArray, because the nested message passing syntax can get pretty hairy when querying an array inside an array. Just store the size of your grid as integers, and derive the x/y position of an object based on its' index.
// Size of grid - 10x10
int rows = 10;
int cols = 10;
int index = 83;
int x = index % cols;
int y = floor(index / rows);
// Results in x, y == 3, 8
Since removing objects from an NSMutableArray actually shortens the array, what do you do when you want to remove objects but preserve the "empty space" they should leave? Use NSNull of course. NSNull is specifically used in arrays/collections that can't store a nil value. Instead of removing the object from your array, do one of these numbers:
[myMutableArray replaceObjectAtIndex:0 withObject:[NSNull null]];
if ([myMutableArray objectAtIndex:0] == [NSNull null])
{
NSLog(@"That index is empty!");
}
Have any other tricks or advice for using NSArray? Let me know in the comments!
Sorry for the recent blog silence... things have been pretty busy as of late, and of course the first thing to get cut is the blog. Don't worry, I've still been game makin'. Revolve Ball just got updated with Game Center leaderboards and a few other tweaks, and I've started work on a new game. It's not an original concept, but presents some interesting programming problems that I've been working through in the past week. Hopefully I'll be able to show some progress in the coming weeks.
In other news, my crap Samsung Alias 2's A/C adapter died and I decided to finally get another iPhone instead of pony up $30 to keep using a phone I totally hated. My original plan was to wait until iPhone 5, but my resolve was weakened after learning that it wouldn't be released in June. So I nabbed a white Verizon iPhone 4. C'mon, I had to get the white! It's like the Loch Ness Monster or something... a mythical, unseen legend. I'm very pleased with it so far (of course).
OK, so you know about the great jQuery utility method serialize(),
which, when applied to a jQuery object that represents a form, will turn all the
form data into a key/value serialized string. Randomly, I was asked if it was
possible to do the reverse. jQuery doesn’t have a method like that baked in, so
here’s my implementation.
function loadSerializedData(formId, data)
{
var tmp = data.split('&'), dataObj = {};
// Bust apart the serialized data string into an obj
for (var i = 0; i < tmp.length; i++)
{
var keyValPair = tmp[i].split('=');
dataObj[keyValPair[0]] = keyValPair[1];
}
// Loop thru form and assign each HTML tag the appropriate value
$('#' + formId + ' :input').each(function(index, element) {
if (dataObj[$(this).attr('name')])
$(this).val(dataObj[$(this).attr('name')]);
});
}
Is there a better way to do this? Let me know in the comments.
I'm wrapping up development on the first update of Revolve Ball, which means I'm also starting to look ahead to what I'm going to work on next. I'm also looking back, in order to gain some understanding about what problems I had with my previous project, and how to try to fix them.
The number one problem that I have right now is that I'm still treating game development as a hobby instead of as a (side) business. Now, there's nothing wrong with that, but if I ever want to go full time, it will be much easier if I have good development practices in place. Practices such as design documents and scheduling/deadlines. Part of the reason that Revolve Ball took such a long time to make was that I never had a clear goal from the beginning, just a vague idea that I kept adding to. Because of that, I never really set deadlines or milestones to keep myself on track for a quicker release.
Another problem that I've seen in both Revolve Ball and Nonogram Madness is that creating game content is very time consuming. It's possible to sit and tweak levels for hours, and since the "fun factor" of a level is kind of arbitrary, it's hard to know when to stop. Since I only get maybe one hour of development time every day, such time consuming tasks would be best cut out, if at all possible.
So, because of these considerations, I'm going to try a new two-month development cycle for my next few projects. These games will be smaller in scope, and won't require level creation. The basic timeline would look something like this:
Two weeks - design document, wireframes, screen comps, icons
Two weeks - music and sound effects
Four weeks - programming and quality assurance
Seems pretty aggressive. However, I'd rather set a narrower schedule due to Parkinson's Law, which says that any task will take as long as the time limit you have to complete it. Since I'm still a novice game maker and am not anticipating any high sales for a particular game, my best strategy at this point is to focus on the $0.99 price point and ship, ship, ship. As always, I'll keep this blog updated with the results of my game makin'.