Playhaven, Unity and Snowferno

September 23, 2009 - benbritten | 8 comments

There is a new player in the iPhone-support website game: PlayHaven. They are similar to Openfeint and Agon in that they provide some 3rd-party support for your game. Unlike OF and Agon, Playhaven does not provide leaderboards and that sort of thing, instead they are trying to build a gaming community around guides, hints, and forums.

In any case, back in the early days of the iPhone game development (like half a year ago) there were far fewer choices for adding leaderboard/achievement support to your app via 3rd party. So after looking at the early OF and others and not being very happy with what they offered (at the time) we decided to go ahead and build our own leaderboards.

So, why playhaven then?

Well, ultimately it comes down to them emailing me and asking me to try it out. We dont currently have any of the features they offer, so the fit seemed good. Also, one nice thing about playhaven is that they are basing their entire 'api' (if you could call it that) around the UIWebView. In other words there is no actual SDK to add to your code, you just whip open an in-app web view and there is the community.

For a programmer this is very nice. I really really do not like to add 3rd party code to my apps unless i really really need to (less code == less bugs). Especially in cases like OF and Agon, who (much to their credit) are releasing new patches fairly quickly. This is great from a feature standpoint, but bad from a trying-to-keep-my-code-up-to-date standpoint.

Once you get two or three games going, just keeping up with the iPhone SDK changes is hard enough (not to mention we need to be generating new content and add polish to our apps every couple of weeks to keep sales up) adding yet another chunk of software that I have to keep up to date is yet another thing I have little time for. From the start with our own leaderboards we tried to offload as much of the process as possible to the webservers so that we could easily and quickly update and change things without having to change the game code, Playhaven fits well with this line of thinking.

With Playhaven, I just add a webview and a URL. And we are going to put a redirect script on our site and call that URL instead, so if the playhaven URL api gets changed or updated, then we just have to update our redirector and all of our games will continue to work.

Ok, now for the bad news: opening up a UIWebview from within Unity is a non-trivial task. You cant actually do it from the unity engine. (well, not yet in any case, those Unity guys are adding new stuff constantly) Well, OK that is not entirely true. If you have Unity iPhone Advanced then you can add a plugin that calls out to the ObjC/cocoa stuff and does all your web ui view stuff from there. but you still need to write some ObjC/Cocoa.

So how do we do this? How do we get the Playhaven page to open overtop of the unity view?

The simplest way is to use the tried and true 'user prefs' hack. Since the advent of Unity iPhone Support, there have been a few enterprising developers who pioneered the technique of using the User Prefs as a communications link between Unity and Cocoa.

(There have been quite a few people who discussed this technique in the unity forums and I dont want to make light of their contributions, but I want to make a special shout out to blipRob aka stinkbot, who produces the fantastic Unity iPhone Enhancement pack, which I recommend as it allows you to do so many more things than just open a UIWebView, and makes integrating the playhaven stuff as simple as adding a dozen or so lines to the enhancement pack code. In fact he probably already has stuff in there to do it, I have an early version of the EP that I have hacked into my own thing so I havent updated recently. But really, buy a copy of the EP and this becomes much much simpler.)

OK, so how does this work?

From unity we set a value to the PlayerPrefs, like so:

   PlayerPrefs.SetString("bbcommand", "BBWebUI|"+fullUrl);

Note: you will need to have some well-formed URL in the fullUrl variable.

The PlayerPrefs are actually stored in the NSUserDefaults dictionary, so we can get that out from the Cocoa side like so:

 
 
NSString* commandString = [[NSUserDefaults standardUserDefaults] objectForKey:@"bbcommand"];
 

So that is how we can get information back and forth between the Unity world and the Cocoa world. But how do we use this to our benefit? We need to be looking for that command to show up in the NSUserDefaults dictionary and then act on it when we see it.

For this to happen we need to have some extra code running besides the standard Unity stuff. There are a few ways of handling this but the lease destructive and easiest to add into your game is to add a category extension to the AppController object in the unity code.

For our purposes I am going to presume that you are updated to the 1.5 unity iPhone and if you havent, then why not? go do that now.

Here it is: the absolute simplest way to insert your own code into the unity code:

 
#import "AppController.h"
#import "BBUnityHandler.h"
 
void UnityInitApplication(const char* appPathName);
void UnitySetAudioSessionActive(bool active);
void UnityPause(bool pause);
 
@interface AppController (BBUnity)
// our two 'public' methods
- (void)pauseUnity;
- (void)resumeUnity;
 
@end
 
@implementation AppController (BBUnity)
 
// override the app did finish launch method and add our single line of code
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
	[self startUnity:application];
	// start our custom code here
	[[BBUnityHandler sharedHandler] startHandler:self];
}
 
// this will resume the unity engine after we have paused it
-(void)resumeUnity
{
	[[UIApplication sharedApplication] setStatusBarOrientation:
                 UIInterfaceOrientationLandscapeRight];
 
	UnitySetAudioSessionActive(true);
	UnityPause(false);
}
 
// this will pause the unity engine
- (void)pauseUnity
{
	UnitySetAudioSessionActive(false);
	UnityPause(true);
}
 
@end
 

OK, what is this doing? Basically this is putting a hook into the unity startup process that also starts up our custom code. In this case it calls my BBUnityHandler object and starts it up with a reference to the app controller.

This method is a bit different from the other unity AppController addons I have seen. I like this way because it is clean and simple. It keeps my code nicely separated from the AppController code.

So lets have a look and see what the BBUnityHandler is doing:

Lets us look at the first two methods we have seen: the class method: sharedHandler and the instance method startHandler:

 
// Singleton accessor.  this is how you should ALWAYS get a reference
// to the sound controller.  Never init your own.
+(BBUnityHandler*)sharedHandler
{
  static BBUnityHandler *sharedHandler;
  @synchronized(self)
  {
    if (!sharedHandler)
      sharedHandler = [[BBUnityHandler alloc] init];
 
    return sharedHandler;
  }
	return sharedHandler;
}
 
- (void)startHandler:(AppController*)aController;
{
	self.unityController = aController;
	[[NSUserDefaults standardUserDefaults] addObserver:self
             forKeyPath:@"bbcommand"
             options:NSKeyValueObservingOptionNew context:nil];
}
 

First up we have a simple singleton accessor method, this creates a new object if there is not one and stashes the reference in a static variable so that you always get the same one every time you call it.

Second we have our own start method. This simple retains a reference to the AppController so that we can make calls against it later. Then we hook up our key value observer to keep an eye on the NSUserDefaults dictionary so we will get a notification if our command ever changes.

In order for the KVO to work we need to add our callback method:

 
- (void)observeValueForKeyPath:(NSString *)keyPath
               ofObject:(id)object
               change:(NSDictionary *)change
               context:(void *)context
{
	NSString* commandString = [[NSUserDefaults standardUserDefaults]
                       objectForKey:@"bbcommand"];
 
	if ((commandString == nil) || ([commandString lenght] < 1))return;
 
	[self performSelectorOnMainThread:@selector(handleCommand:)
                      withObject:commandString waitUntilDone:NO];
}
 

This is pretty simple: grab the command string from the dictionary, check to make sure it is legit and dispatch it. The last line is important, we jump off this thread and jump to the main thread. Mostly we want to do this because we are going to be doing UI stuff and that is best handled on the main thread. Why do we need to do this? The KVO stuff is handled straight away on the same thread that the change was made, and I have no insight into the Unity black box, so I dont really know what thread I am on when this gets called so I want to get off of it and onto a known thread as quickly as I can.

OK, now we move to the handleCommand method which is being called on the main thread.

 
-(void)handleCommand:(NSString*)commandString
{
	NSArray *args = [commandString componentsSeparatedByString: @"|"];
 
	if (([[args objectAtIndex:0] isEqualToString:@"BBWebUI"]) && ([args count] > 1)) [self openWebUI:args];
 
	[[NSUserDefaults standardUserDefaults] setObject:@"" forKey:@"bbcommand"];
}
 

Pretty simple, just parse the args into a nice array, check to make sure our command is roughly formatted right (ie there is at least one argument) and pass it on to the web ui method. Finally we reset our command back to a blank command. (note: this will actually cause our KVO method to get called again, but it will shunt out since there is no command there)

 
-(void)openWebUI:(NSArray*)args
{
	[unityController pauseUnity];
 
	// alloc a new window for my web view
	overlayWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
 
	// make a toolbar and add a button right in the center
	UIToolbar * toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 431.0, 320, 49.0)];
 
	UIBarButtonItem * okButton = [[UIBarButtonItem alloc]
                   initWithTitle:@"Back to Snowferno"
                   style:UIBarButtonItemStyleBordered
                   target:self action:@selector(closeWebUI)];
	UIBarButtonItem * flexiSpaceItem = [[UIBarButtonItem alloc]
                   initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
                   target:nil action:nil];
	UIBarButtonItem * flexiSpaceItem2 = [[UIBarButtonItem alloc]
                   initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
                   target:nil action:nil];
 
	toolbar.items	= [NSArray arrayWithObjects:flexiSpaceItem,okButton,flexiSpaceItem2,nil];
	[okButton release];
	[flexiSpaceItem release];
	[flexiSpaceItem2 release];	
 
	// add the toolbar to the new window and release it
	[overlayWindow addSubview:toolbar];
	[toolbar release];
 
	// make my webview, assign self as the delegate
	UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 431.0)];
	webView.scalesPageToFit = YES;
	webView.delegate = self;
	[overlayWindow addSubview:webView];
 
	// add a spinner so the user is not waiting looking at nothing
	spinner = [[UIActivityIndicatorView alloc]
              initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
	spinner.center = CGPointMake(160.0, 240.0);
	spinner.autoresizingMask = UIViewAutoresizingFlexibleRightMargin |
             UIViewAutoresizingFlexibleLeftMargin |UIViewAutoresizingFlexibleTopMargin |
             UIViewAutoresizingFlexibleBottomMargin;
	[spinner startAnimating];
	[overlayWindow addSubview:spinner];
 
	// show the window
	[overlayWindow makeKeyAndVisible];
 
	//Create a URL object.
	NSURL *url = [NSURL URLWithString:[args objectAtIndex:1]];
 
	//URL Requst Object
	NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
 
	//Load the request in the UIWebView.
	[webView loadRequest:requestObj];
 
	[webView release]; // the overlay window owns it now
}

Finally, the meat of our code: the actual web ui view. The first thing we do in the longish method is pause unity. Here is where our reference to the app controller is required. This causes the unity engine to basically just freeze. Now we can safely overlay our views without worrying what is going on in the engine.

In order to do this with minimal effect on the Unity code we are actually going to make a new window (which is fairly rare on the iPhone, you really should not need to do this very often)

OK, so we make a new window, then we make a toolbar with a back button and add that to our window. Finally we add on our UIWebView and kick off the initial request. (which is the URL that we get from the args array).

Since the web view will take some time to load the content, we also add a spinny indicator so that the user knows something is happening. Otherwise they have to sit and stare at a blank view for a few seconds while the URL loads.

OK, great! nearly done!

The first thing we need to do now is remove the spinner after the loading is done. Handily we told the webview that we would be the delegate, so we get told when it has finished loading.

- (void)webViewDidFinishLoad:(UIWebView *)webView;
{
	[spinner removeFromSuperview];
	[spinner release];
	spinner = nil;
}

Ok, that was easy. Now the user is happily surfing the web inside our game. We added a nice 'Back to the game' button so that after they are done reading tips on how to slay that big bad dragon or solve that hard puzzle or whatever, they can get back into Unity.

 
- (void)closeWebUI;
{
	[overlayWindow release];
	overlayWindow = nil;
	[unityController resumeUnity];
}

That is it. We release the window, which makes it go away and clean up all of it's own memory, then we resume the unity engine.

Here it all is in one big file:
AppController_BBUnity.mm.zip

This is all going to be in the Snowferno 1.1 release which we are submitting in the next few days, so if you want to see this in action, then pick up a copy of Snowferno! :-) (or if you find it useful, then help up out by picking up a copy and tell your friends :-)

UPDATE: we had a bit of a burp with our wordpress so you could not download the file. That should be all fixed now! Thanks Brent!

Cheers!
-ben

While we wait: Leaderboard Twitter integration

August 8, 2009 - balord | No comments yet

(Three business days into the v1.0.1 resubmit and counting...)

In the meantime, I've been working on integrating the Snowferno Leaderboard with Twitter. Of course, that has been complicated by the DoS attack against Twitter over the last few days. But, it's coming along nicely and might even be ready to go live by the time we get our approval.

Our Twitter integration works basically like this:

  1. Players who have setup their online leaderboard account (Settings > Score Board > Edit Leaderboard Settings) can click the "Sign in with Twitter" button and allow the Snowferno Leaderboard to access their Twitter account. At the same time, we ask the bit.ly API for a shortened URL to points to that player's Leaderboard profile.
  2. Twitter sends us back OAuth tokens for that player's account, and we store those for future use. (OAuth incidentally is a really cool system. It's way too creepy these days to ask someone to give you their actual Twitter login and password. Using OAuth authentication, they only login to Twitter, whom they already trust. We avoid the security risks inherent in storing passwords, and players can revoke our app's permission whenever they want. Win/win/win (except for when Twitter throttles down OAuth to defend against DoS!))
  3. During regular online gameplay, the Leaderboard API logs certain achievements (such as getting a Top 5 score for a level... more to come).
  4. A cron script crawls the achievements log every minute and posts tweets on each player's behalf to the accounts that are Twitter-enabled. The reason I do the tweeting as a separate process was so that we could return global leaderboard data back to the game as quickly as possible.

The foundation of our integration is the PHP Twitter API wrapper twitter-async. The rest are custom additions to the Snowferno API and my Snowferno WP plugin.

Alot of the work on this has gone into getting the structures in place to gather and store authentication and making my cron script and achievements logger modular. (Also, my ongoing education in talking to APIs from inside and outside of the WordPress core.) Once the Twitter stuff is humming along nicely, I plan to delve into the Facebook Connect code and do something similar with Facebook statuses.

Leaderboard bug fix: screenname changes should work again

July 28, 2009 - balord | No comments yet

Oops. I was doing some housekeeping on the Leaderboard code today and found that I had forced an error during testing and forgot to remove it. This has effectively been preventing screenname changes sometime since robc managed his change.

I had added a host name check to prevent some external script access, and it was still set to === instead of !==.

Fixed! So now, change away. Details how-to are on the support page. (I wondered why nobody was changing their names... thought maybe the Inferno character names were just oh so desirable.)

BTW, no activity yet from any Apple IP addresses. Tick tock...

Dev tools

May 18, 2009 - benbritten | No comments yet

Thought I would jump in here and talk really quick about the tools we are using to build Snowferno.

We are using Basecamp for the central location of files and calendar events and to-dos and things of that nature. Basecamp is pretty good, and I think it is a useful tool, but it doesnt blow my socks off. One thing that annoys me to no end is the writeboards.. ack! the formatting is terribly hard to deal with.

To get around the terrible writeboards in basecamp, we use a private install of MediaWiki (the same software used for wikipedia) to handle all our documenting needs. It is much easier (i think) to use than the basecamp writeboards.

As for desktop tools Brent did a post on the sound stuff he is using, so that is covered :-).

I am using Unity3D to build the game. We currently have only a single indie license, but we are thinking of upgrading to pro before we go live (although truthfully, the indie is a very very capable license)

For all the 3D art assets I am using Cheetah3D which is a surprisingly fantastic 3d modeler. I used to have a copy of Maya back during my time in the film industry (still have a seat that is a few versions old laying around here somewhere...) but I find cheetah to be much simpler to use. (and for what we need, Maya is a bit overkill anyhow)

For 2d stuff, textures and whatever, I am using the industry standard Photoshop/Illustrator pair. Although I must say, I love illustrator, but I kinda hate photoshop these days. I really dont like the CS3 interface changes. (I actually just recently upgraded, so I am still getting used to it) but to be honest, I am looking around for something photoshop-like that I can switch to. (while I probably use %15 - %25 of illustrator, i maybe use %5 of photoshop, I could really get away with much less... and before you say "Gimp", I will admit to not having tried it recently (and by recently, i mean in the last 8 years or so) SO I should really bust it out and see how it has changed... but I will say, that the x11 stuff turns me off a bit. (I know, I know...) I am even a unix guy from way back, but I just like to keep things simple.

As for version control, I use beanstalk and that has worked just fine for me for awhile now.

I think that is about it..
Cheers!
-B

Development Process

May 14, 2009 - benbritten | No comments yet

First off, let me say that this is our first game. All three of us are veterans of various creative environments, and building a game is surprisingly similar to putting on a theatrical production of making a movie. That said, some people might wonder just what our general process has been for this game.

Well, first off, we decided that we wanted to build a game, and decided to build it for the iPhone. (mostly because I had already done a few apps for the iPhone and I have wanted to build some games for awhile now, but did not have any viable distribution platform, the iPhone solves that problem :-)
Next we all sat down and talked about what kind of game we wanted to make and started brainstorming ideas. Most of these ideas were quite good, but not really feasible for our first game outing. We finally chose to go with the rolly-ball genre because it is a nice simple gmae mechanic with lots of room for creative level/world design.

I built a prototype game, this was very basic and was mostly just a game mechanic demo really. the 'levels' in the original prototype look nothing at all like the ones in the current release. We probably spent three or four weeks just working out the best way to have the character respond to the inputs.

We originally had a two-axis accelerometer control, and the original snowball was fully physics based. This turned out to be too realistic, and it was sorta frustrating to control. One thing we didnt like was that you had to tip the phone away from you to move forward, this made it hard to see the screen. SO we moved away from the forward-back for the throttle and added an on-screen control.
Then we slowly moved the character away from a purely physics based movement model to a more controlled pseudo-realistic model. This way we can control via code how and when the character reacts to things. Now you can speed up and slow down easier, and the turning is much smoother, giving the user a feeling of more control.
Once we had a good character control mechanic we moved onto some level designs.

We started off a bit crazy and had all sorts of ideas and sketches for level designs and artwork. Ultimately, the reality of the iPhone and the scope of the project began to put limitations on what we could and could not do. At this point we tried to standardize on a handful of puzzle mechanics. (like switches, boxes, moving platforms, snowpiles and fires). Once we had a good feel for the pieces of the puzzles we could start designing levels in earnest.

As one of us would design a level, I would mock it up with Cheetah3D, and build the level in Unity. then we would play the level and modify it until we were pretty happy with the puzzle or feat of dexterity required to pass that level.

Now that we have most of the levels finished, I am going back through each one and adding proper textures, remodeling parts that need it and generally giving them all a once over in preparation for beta testing. During this time Mike and Brent have been writing all the music.

The next step, which I am just starting on, is to get the user interface under control. Up to now it has been very minimal, just enough to start the levels and do testing. I want to go into beta testing with the UI at 95% if possible. the UI sets the mood of the game so much that it is imperative (i think) to have a representative UI to go along with the game play. (and just to be clear, by UI I mean all the screens that you have to wade through to chenage settings, the opening screen, the score screens, the 'fall out' screens, and the game elements that are part of the HUD during gameplay.)

Once we have a halfway decent UI, we will go ahead and do some wider beta testing and tweak the levels and gameplay based on the feedback we recieve from that, then it will be time to add all the final bits n pieces and submit!

Cheers!
-B