Thursday, September 17, 2009

Lady Monet Gets Unskrambled


We used the artwork of old masters in the public domain since we didn't want to deal with copyrights for the first version of Skrambler. Normally copyright on the artwork exists for 70 years after the artist's death. Even then, museums and galleries can hold a copyright on the reproduction of these images. It was a lot of work trying to track down if a particular image is under copyright.

For future versions of Skrambler, we hope to work with museums and galleries to bring their collections into the game.

Un-skrambling infront of the real paintings is kind of fun. The National Gallery of Art in Washington DC is amazing, and free!

Sunday, September 6, 2009

Removing the Default Shine from Icons

Apple makes it easy to create a good looking app icon. I just needed to specify a square PNG image file named Icon.png and the OS takes care of creating a rounded edge and overlaying it with a consistant shine over it.

I wanted to make my icon have a distinctive hex shape. I created a PNG with a transparency background but found that it didn't quite work because the default shading was ruining the effect. In order to remove the shading, I needed to modify the Info.plist file. This file holds the app's basic settings and is usually prefixed with the app's name, e.g. Skrambler-Info.plist. The setting for removing the shine is not available by default so it is not available in the Xcode editor. I needed to open the file in a text editor and add the following lines:
 <key>UIPrerenderedIcon</key> <true/>
Another useful setting is to hide the status bar when the app is starting.
 <key>UIStatusBarHidden</key> <true/>

Saturday, August 29, 2009

Telling a Story in a Puzzle Game


People play puzzle games because they want to solve puzzles. While an adventure game is kind of like a puzzle game, the main focus is the story. In a game like Skrambler, the main focus is putting the images together. While I believe that having a back story can enrich the gaming experience, putting too much 'in-your-face' story elements can also disrupt gameplay, especially in a puzzle game.

The approach I have taken in Skrambler is to inject story elements around the game to indicate that there is a larger context to the game. For example, the Skrambler device is designed to look like an ancient mechanical artifact with a subtle marking that says "Property of Chronus" that perhaps might give some clues to the origin of the device. The splash screen also gives some indication of the world surrounding Skrambler.

These elements provides hints at the world around the game, and gives clues to why the player might be putting images together. For the player that is so inclined, he or she may delve more into the story, but for the casual player that is only interested in the puzzle elements, there are no distractions.

Friday, August 21, 2009

There Can Be Only One--Using Singletons to Organize Your Program


In a game, or in any other program, it is customary to store game session data in a common place so that different views and controllers access them. The data might be game state, player score or user preferences. One place to store them is as global variables. This is acceptable for a small program, but as your program grows larger, it is easy to loose track of them.

I've been using the singleton pattern to store common variables. For those that are new to singletons, it is simply an object that exist as a single instance. It is instantiated at the start of the application (or when first accessed) and is retained in memory until the end.

I'm naming these singleton classes Managers in my program. For example, there is a GameManager class for holding the game state, a ViewManager that knows about all the views, and a SoundManager that is used to play game sounds.

I'm using the singleton template written by Matt Gallagher for simplicity, but you can certainly write your own. You can find more information about singletons on his blog post titled Singletons, AppDelegates and top-level data.

Monday, August 17, 2009

Choosing the Appropriate Framework

I decided to use UIKit, which is the basic framework in the iPhone SDK for managing the user interface elements. The Interface Builder is a great tool for laying out UI components. I can drag-and-drop elements, such as buttons, onto the view and re-skin them to fit the look of the game. Using OpenGL ES is an alternative option but that has a steeper learning curve. For something 2D and not graphics intensive, UIKit works very well.

The UIView has built in functionality to perform key-frame animation on the various properties. For example, we can change the alpha value to fade-in and out view elements, or even provide transformation matrices for more complex movements.

There are also a few third-party frameworks that I looked at. I was impressed with Unity 3D, especially with the GUI development environment. You can do all your game logic in scripts and would not have to touch Objective-C at all. In the end I felt that it was a bit of an overkill for a simple 2D game, but I might revisit it later for future projects. The ability to target multiple platforms (i.e. web, desktop) is very attractive.

I also looked at Torque 2D but found it a bit confusing. Unity definitely has better documentation and tutorials to get you started, and feels like a more polished product.

There is also coscos2d which is an open-source 2D framework if you want to go the free route.

Sunday, August 16, 2009

Making Tiles


Creating hexagonal tiles from the image involves applying a tile mask and cutting up the image into smaller UIImageView objects. A layer of shading is also applied to add a little depth to the tiles.

I created a class called Puzzle that is derived from the UIImageView class. The makeTileAtPoint method creates a Tile object (another UIImageView class) at that location, and adds it to an array of tiles.

- (void)makeTileAtPoint:(CGPoint)point {

CGImageRef maskRef = [UIImage imageNamed:@"shading.png"].CGImage;

CGImageRef flatMaskRef = [UIImage imageNamed:@"mask.png"].CGImage;

int width = CGImageGetWidth(maskRef);

int height = CGImageGetHeight(maskRef);

UIImage* tileRectImage = [self imageFromImage:self.image inRect:CGRectMake(point.x, point.y, width, height)];

// Clip the tile (the mask is not clipping the image properly)

CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

// create a bitmap graphics context the size of the image

CGContextRef mainViewContentContext = CGBitmapContextCreate (NULL, width, height, 8, 0, colorSpace, kCGImageAlphaPremultipliedLast);

// free the rgb colorspace

CGColorSpaceRelease(colorSpace);

if (mainViewContentContext==NULL)

return;

// Define the mask for the tile

CGContextClipToMask(mainViewContentContext, CGRectMake(0, 0, width, height), flatMaskRef);

// Draw the tile image

CGContextDrawImage(mainViewContentContext, CGRectMake(0, 0, width, height), tileRectImage.CGImage);

// Draw the shine and shadow for the tile

CGContextDrawImage(mainViewContentContext, CGRectMake(0, 0, width, height), maskRef);

// Create CGImageRef of the main view bitmap content, and then

// release that bitmap context

CGImageRef mainViewContentBitmapContext = CGBitmapContextCreateImage(mainViewContentContext);

CGContextRelease(mainViewContentContext);

UIImage* tileImage = [UIImage imageWithCGImage:mainViewContentBitmapContext];

CGImageRelease(mainViewContentBitmapContext);

Tile* tile = [[Tile alloc] initWithImage:tileImage];

tile.frame = CGRectMake(point.x, point.y, width, height);

[tiles addObject:tile];

}


- (UIImage *)imageFromImage:(UIImage *)srcImage inRect:(CGRect)rect {

CGImageRef sourceImageRef = [srcImage CGImage];

CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, rect);

UIImage *newImage = [UIImage imageWithCGImage:newImageRef];

CGImageRelease(newImageRef);

return newImage;

}

Saturday, August 15, 2009

Parsing CSV files

I've been looking for a simple CSV file parser in the SDK and couldn't find one, so I decided to write one so that I can use it to read in level data into the game. I like working with CSV files because I can use a spreadsheet program to maintain the data. The method takes a line of text and splits it up into an string array of phrases. The method handles double-quoted phrases as well as commas that appear within the phrases.

Update: I've updated the code based on feedback by Rich and KaleidoBalls, as well as fixed a memory leak. Thanks, all!

+ (NSMutableArray*)readLine:(NSString*)fileLine {

NSMutableArray* partsData = [[NSMutableArray alloc] init];

NSMutableArray* phrase = [[NSMutableArray alloc] init];


NSArray* tempPartsData = [fileLine componentsSeparatedByString:@","];

for (NSString* origPart in tempPartsData) {

NSString* part = [[NSString alloc] initWithString:[origPart stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]];

if ([part length] == 0) {

[part release];

continue;

}

if ([part characterAtIndex:0] == '"' && [part characterAtIndex:[part length]-1] != '"') {

// Starting phrase

[phrase addObject:[part substringFromIndex:1]];

} else if ([part characterAtIndex:0] == '"' && [part length] == 1) {

// Just a lonely quotation mark--a phrase started or ended with a comma

[phrase addObject:@""];

if ([phrase count] == 0) {

// phrase begins with a comma

} else {

// phrase ends with a comma

[partsData addObject:[phrase componentsJoinedByString:@","]];

[phrase removeAllObjects];

}

} else if ([part characterAtIndex:0] == '"' && [part characterAtIndex:[part length]-1] == '"') {

// Complete quoted phrase

NSRange inner;

inner.location = 1;

inner.length = [part length] - 2;

[partsData addObject:[part substringWithRange:inner]];

} else if ([part characterAtIndex:[part length]-1] == '"') {

// Ending phrase

[phrase addObject:[part substringToIndex:[part length]-1]];

[partsData addObject:[phrase componentsJoinedByString:@","]];

[phrase removeAllObjects];

} else if ([phrase count] > 0) {

// Continuing phrase

[phrase addObject:part];

} else {

// Complete phrase

[partsData addObject:part];

}

[part release];

}

[phrase removeAllObjects];

[phrase release];

return [partsData autorelease];

}

Friday, August 14, 2009

The Jigsaw... Re-imagined


I've always loved the jigsaw puzzle. There's something uniquely satisfying about putting together small indistinguishable parts into a unified whole, where the final product is something much more than the sum of its parts. In a way, it is something like creating a software program--little bits of logic that are combined together in a specific pattern to emerge as an entity that has a specific structure and function, as well as beauty.

I set a goal to design an app that has the qualities of a jigsaw puzzle, but one that is more suited for the iPhone. I had two constraints:
  • all the pieces should be visible
  • no scrolling of the image should be necessary
Given the relatively small screen real estate, I did not want to split the screen into a holding bin and a picture area. That meant that the pieces and the picture would occupy the same space. The obvious solution, given these constraints, is tile swapping, i.e. the image will be formed by swapping the tiles amongst themselves.

I played around with various tile shapes and settled on hexagonal tiles. Not only do they make a nice pattern, but I felt that hexagonal tiles would be novel for a jigsaw puzzle.

Why...?


There are really a few WHYs to cover. Why the blog? Why the iPhone?... Why code at all? The last is pretty simple. I love to create things, and coding is just one form of creation that is easily accessible and also melds art and science. The iPhone is such a neat and sexy device for us coders because of its simplicity and possibilities to express our creativeness. It wasn't an easy start. When the SDK first came out, information was hard to come by, and I struggled with Objective-C, the programming language that is used for the iPhone (and Macs). I wrote some Hello World type apps and stopped. After about a year of inactivity, I decided to take a stab at it again. With the release of iPhone OS 3.0 with new features such as peer-to-peer networking, support for subscription models and integration with hardware devices, it was too tempting to ignore. This blog is meant as a personal diary, and also to help others who may be starting their own iPhone projects, so that we can share techniques and ideas.