nathandemick.com

Intermission: The Legend of Zelda: Skyward Sword

As you can see, since announcing that I was going to start dabbling in Unity, I actually haven't done anything! The cause of this is the fact that I'm currently ~20 hours in to the new Zelda game, Skyward Sword. Basically I'm trying to recharge and remind myself why I like games in the first place by actually playing one.

Fortunately, Skyward Sword is pretty good. While it isn't as purely groundbreaking as Ocarina of Time, it does add a number of new gameplay mechanics that make the game feel fresh. My only criticism so far is that parts of the game are a bit padded (i.e. to progress, you need to fetch an item located in an area you've previously completed), but that is almost inevitable with any large-scale adventure game.

I've been talking the game up to co-workers, and even though I haven't completed it yet, I can wholeheartedly recommend that you dust off your Wii and start swinging the remote like a crazy person. It's a true "Nintendo" game, which is about as high of praise as I can give anything these days. Once I finish, I've got a few ideas in mind for new projects to start, so we'll see where those lead.

· 0 comments

Unity Challenge

It's about time to learn something new. After publishing a few iOS projects using cocos2d as a programming framework, I've decided that I'm finally going to learn how to use Unity. I've always been on the cusp of using Unity since I first found out about it. At first, it was the $100 price tag for the "indie" version that kept me learning/using Actionscript with the free Flex compiler. After the base Unity package became free (and a Windows version was released), I toyed with the program: ran through a few tutorials before I had a Macintosh to start iPhone development. Once I got the Mac though, I dove straight into cocos2d instead.

cocos2d is great, but it has some limitations that I've been thinking about a lot lately. The main one is that your games run only on iOS devices. While it's true that there is a cocos2d-mac port, it's exactly that: a port. I did some investigation into making Mac versions of some of my games, and right now it's actually a decent amount of work make that conversion. And let's be honest: if a person owns a Macintosh, chances are they own an iOS device as well. As a part-time developer, one of my goals is (or at least should be) to create content in the shortest amount of time possible, and have it be playable on as many platforms as possible.

I think that Unity has reached a point where it is a viable option for aspiring game developers. The software is mature, it has a pretty sizable community, and there are a large number of helpful resources to get up and running. The added benefit of being able to publish to multiple platforms from the same project is what's driving me to finally take the plunge.

Nothing is perfect, and I'm sure there will be things I dislike about Unity. Learning a new development platform is daunting, and it's kind of depressing to start over with something new after investing a lot of time into cocos2d and Objective-C. However, the perceived benefits outweigh the costs in my mind right now, and I'm going to try to make the transition. If you're interested in learning Unity along with me, keep coming back in the months ahead: I'll be posting some introductory tutorials as I learn the basics (and you'll be able to play the finished results in your browser with the Unity plugin!).

· 2 comments

A defense for motion control in games

Last night I read a screed on The Verge forums from a guy who lamented the fact that games have become too accessible. He remembers fondly the time spent as a youth when he played difficult games, and calls out the new Zelda game (Skyward Sword) as example of a "soft, hit-detection-free experience."

Even though I've just started playing the game, I don't feel this way about Skyward Sword at all. In fact, I'm finding it more difficult than other Zelda games I've played. There are a few reasons for this: mostly because of the precision motion control required, but also due to other changes, such as a shield that wears down over time, and fewer randomly found hearts. Playing this morning, I actually died to the first dungeon boss. While it could have been that my sleep-deprived mind couldn't recognize patterns effectively, it's also true that I played through Ocarina of Time and Majora's Mask without ever coming close to dying.

Some Wii games certainly do have "floaty" controls, but these are games that have a broad audience (such as Wii Sports). However, most of the other games I've played on the Wii use the remote/nunchuck combo for a more traditional control scheme; perhaps they use the remote for pointing a cursor on the screen as well. While the Wii made broad strokes into a "blue ocean" of non-gamers, it still has a lot to offer to those who were raised on the original NES.

· 0 comments

Introducing BreakApp

Despite the relative silence of the blog recently, I've still been programming. After updating Nonogram Madness and Revolve Ball, I wanted to do something new, so decided to make a small productivity app. The result is BreakApp, a simple application for iPhone and iPod Touch which tries to remind you to take breaks from sitting at your desk. Most of my life is spent sitting, sadly, and there seems to be lots of research that says that prolonged sitting will take years off your life. However, if you can get up and move around regularly, it can help offset the negative effects of being a desk jockey.

Such "take a break" apps exist for the desktop already, but I wanted to make something for my phone. It's all too easy for me to ignore warnings on my desktop, but when I get a notification on my phone, it breaks my flow/concentration more, because I have to spend more effort to look at the phone's screen. Plus, I figured I'd be more likely to actually use the app if I was the one who wrote it.

The app is still written with cocos2d. I toyed with the idea of learning Interface Builder, but decided to prototype with something I already knew. By the time the functionality of the app was done, I had little interest in going back and re-writing the display code. The only real downside of using cocos2d instead of UIKit was the jankiness of the "slider" controls. I used some freely available code front the cocos2d-extensions project, and while it works, it's not as good as what Apple provides.

I published the app as a way to make it more permanent, not necessarily to make any money. You can check it out on iTunes, and if it seems like the sort of thing you'd use, send me a note and I'll give you a promo code.

· 6 comments

cocos2d + Game Center Achievements

If you've been following along on the blog, I posted a few months ago about how to easily integrate Game Center leaderboards into your cocos2d game. Game Center is pretty great, because Apple writes basically all of the code for you, and they also provide a web-based interface to create your content.

My previous tutorial was just about setting up leaderboards. Today I'm going to expand on that, and add in some methods to the Game Center singleton that will allow you to report achievements. Achievements are a bit different than leaderboards in that when you connect a player to Game Center, that player's current achievement status is returned from Apple's servers. Sending leaderboard scores are just fire and forget, but for achievements you have to figure out their completion status and increment that value, then send it back. Like I said, Apple writes all the GameKit classes that deal with interacting with Game Center, and they also provide example code which basically works right out of the box. In fact, I only had to write one method of my own when implementing achievements, and that was for my own convenience.

Bust out your GameCenterManager class and add some properties and methods to the header file:

// Store unsent Game Center data
NSMutableArray *unsentAchievements;

// Store saved Game Center achievement progress
NSMutableDictionary *achievementsDictionary;

// Achievement methods
- (GKAchievement *)getAchievementForIdentifier:(NSString *)identifier;
- (void)reportAchievementIdentifier:(NSString *)identifier percentComplete:(float)percent;
- (void)reportAchievementIdentifier:(NSString *)identifier incrementPercentComplete:(float)percent;
- (void)showAchievements;
- (void)achievementViewControllerDidFinish:(GKAchievementViewController *)viewController;

These properties and methods should look pretty self-explanitory if you've already read through the previous tutorial. The unsentAchievements array is used to store achievement reports that can't be sent to Apple for some reason (e.g. network timeout). The next time the user is authenticated, the manager class tries to resend the data. achievementsDictionary stores a player's existing achievements locally, so we can quickly check completion status without having to use a network connection each time. The first method is only used internally by the other methods as an easy way to query the achievementsDictionary. The next two methods are used to report achievement progress, while the last two deal with displaying the achievement progress.

The first thing we have to do is load a player's existing achievement completion percentage from Game Center after they are successfully authenticated. In the - (void)authenticateLocalPlayer method, after the if (localPlayer.isAuthenticated) conditional, add the following code to load achievements into the achievementsDictionary object you added to the singleton.

// Load player achievements
[GKAchievement loadAchievementsWithCompletionHandler:^(NSArray *achievements, NSError *error) {
	if (error != nil)
	{
		// handle errors
	}
	if (achievements != nil)
	{
		// process array of achievements
		for (GKAchievement* achievement in achievements)
			[achievementsDictionary setObject:achievement forKey:achievement.identifier];
	}
}];

Next let's look at the implementation of the methods we defined earlier. These are all taken from Apple's example documentation, except for - (void)reportAchievementIdentifier:(NSString *)identifier incrementPercentComplete:(float)percent which is just a simple modification that I thought was convenient.

#pragma mark -
#pragma mark Achievement methods

/**
 * Get an achievement object in the locally stored dictionary
 */
- (GKAchievement *)getAchievementForIdentifier:(NSString *)identifier
{
	if (hasGameCenter)
	{
		GKAchievement *achievement = [achievementsDictionary objectForKey:identifier];
		if (achievement == nil)
		{
			achievement = [[[GKAchievement alloc] initWithIdentifier:identifier] autorelease];
			[achievementsDictionary setObject:achievement forKey:achievement.identifier];
		}
		return [[achievement retain] autorelease];
	}
	return nil;
}

/**
 * Send a completion % for a specific achievement to Game Center
 */
- (void)reportAchievementIdentifier:(NSString *)identifier percentComplete:(float)percent
{
	if (hasGameCenter)
	{
		// Instantiate GKAchievement object for an achievement (set up in iTunes Connect)
		GKAchievement *achievement = [self getAchievementForIdentifier:identifier];
		if (achievement)
		{
			achievement.percentComplete = percent;
			[achievement reportAchievementWithCompletionHandler:^(NSError *error)
			{
				if (error != nil)
				{
					// Retain the achievement object and try again later
					[unsentAchievements addObject:achievement];

					NSLog(@"Error sending achievement!");
				}
			}];
		}
	}
}

/**
 * Send a completion % for a specific achievement to Game Center - increments an existing achievement object
 */
- (void)reportAchievementIdentifier:(NSString *)identifier incrementPercentComplete:(float)percent
{
	if (hasGameCenter)
	{
		// Instantiate GKAchievement object for an achievement (set up in iTunes Connect)
		GKAchievement *achievement = [self getAchievementForIdentifier:identifier];
		if (achievement)
		{
			achievement.percentComplete += percent;
			[achievement reportAchievementWithCompletionHandler:^(NSError *error)
			 {
				 if (error != nil)
				 {
					 // Retain the achievement object and try again later
					 [unsentAchievements addObject:achievement];

					 NSLog(@"Error sending achievement!");
				 }
			 }];
		}
	}
}

You can see that these reporting methods are very similar to the reporting methods used for leaderboards. Each one tries to get an existing achievement object from your local cache of achievement data, set the percentage complete (or increment it), then send to Apple. If the report fails, the GKAchievement object gets temporarily stored locally, and will attempt to be sent again later. I added the - (void)reportAchievementIdentifier:(NSString *)identifier incrementPercentComplete:(float)percent method because it was easier to just have each successfully completed level in my game add a certain percentage towards completion status. For example, you have 20 levels, so each level just adds 5% to the total. That way you don't have to find the previous completion percentage of the achievement object.

The next methods are straight from Apple's example code; the only thing changed here is that we attach the UIViewController that shows a player's achievements directly to cocos2d's OpenGL view. This will show the default "green baize" style Game Center UI; if you want your own custom UI, that's something you can do, but outside the scope of this tutorial.

/**
 * Create a GKAchievementViewController and display it on top of cocos2d's OpenGL view
 */
- (void)showAchievements
{
	if (hasGameCenter)
	{
		GKAchievementViewController *achievements = [[GKAchievementViewController alloc] init];
		if (achievements != nil)
		{
			achievements.achievementDelegate = self;

			// Create an additional UIViewController to attach the GKAchievementViewController to
			myViewController = [[UIViewController alloc] init];

			// Add the temporary UIViewController to the main OpenGL view
			[[[CCDirector sharedDirector] openGLView] addSubview:myViewController.view];

			[myViewController presentModalViewController:achievements animated:YES];
		}
		[achievements release];
	}
}

/**
 * Dismiss an active GKAchievementViewController
 */
- (void)achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
	[myViewController dismissModalViewControllerAnimated:YES];
	[myViewController release];
}

The last thing you'll have to do before you can actually use these reporting methods in your game is set up the achievements in iTunes Connect. Create a new app entry for your game and enable Game Center for it. Next, go into the "Configure Game Center" section and set up your achievements. The interface is really so simple that you should be able to figure it out easily. The most important part is to take note of each achievement ID string that you create, as these will be used in your code to reference each achievement. For example, in Nonogram Madness, when a player successfully completes an easy randomized level, I make a call with the GameCenterManager like this:

[[GameCenterManager sharedGameCenterManager] reportAchievementIdentifier:@"com.ganbarugames.nonogrammadness.random_easy" incrementPercentComplete:10.0];

And that's all there is to it! Download the example source, and if you have any questions, feel free to leave a comment or get in touch on Twitter.

· 3 comments