Context for this Tutorial
Carnival Craze is a 2D iPhone game we're submitting to the app store this week. It's programmed in Unity's 3D engine with an orthographic 2D camera. In the game, the Fortune Teller gives you any of a number of fortunes. You can post your favorite ones to your Facebook wall for all you friends to see. This tutorial shows how to do this, with all the code you need.
Writing to Player Prefs
In the game, each fortune has a button saying “POST THIS FORTUNE TO YOUR WALL.” In script, we write to PlayerPrefs when that button is pressed. We're naming this PlayerPref “Fortune” and the associated value is whatever the string currentFortune is holding. When the game is running on device, this adds a key-value pair to NSUserDefaults. The key is “Fortune” and the value is “Plan for many pleasures ahead.” in this instance.
var invisible : GUIStyle;
private var currentFortune : String = "Plan for many pleasures ahead.";
function OnGUI()
{
if( GUI.Button( Rect( 100, 260, 120, 40 ), "", invisible ))
{
PlayerPrefs.SetString("Fortune", currentFortune);
}
}
Observing NSUserDefaults in Objective-C
Set up KVO
Next, do some Key-Value Observing (KVO) as explained so well by TinyTim Games. First, build your Unity 3D project so you have an Xcode project to work with. In your Xcode project, open AppController.mm. This is where we hook into Unity's startup process. Find applicationDidFinishLaunching and insert KVO initialization code. The function now looks like this, since we're listening for the NSUserDefault (PlayerPref) labeled “Fortune”:
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
printf_console("-> applicationDidFinishLaunching()\n");
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"Fortune" options:0 context:nil];
[self startUnity:application];
}
Right under applicationDidFinishLaunching, add the function (void)observeValueForKeyPath. This gets called when a NSUserDefault is set for “Fortune”. Since it will be fun to see this working, just log that we've gotten this far:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
NSLog( @"The PlayerPref: %@ was set form Unity script.\n", keyPath );
}
So to summarize, you have
- written to PlayerPrefs in script,
- added a NSUserDefaults observer and
- added the function to log confirmation that you've access objective-c from inside Unity. Run your project with the Xcode debugger console open and push the button that writes to PlayerPrefs. You should see the message you logged.
Read NSUserDefaults
If you haven't read from NSUserDefaults before, I like the simple, robust example from CocoaDev. Now the observer function looks like this:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
//Read NSUserDefault for this Key-Path
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString *val = nil;
if (standardUserDefaults)
val = [standardUserDefaults objectForKey:keyPath];
NSLog( @"The PlayerPref: %@ was set from Unity script with the value: %@.\n", keyPath, val );
}
Run your program again and check what is logged in the debugger console.
Adding Facebook
Next you need to setup a facebook application. It literally takes 5 minutes if you've done it before. If not, I really like Dan Grigsby's walkthrough on Mobile Orchard. After you have a Facebook application set up, you need to add the Facebook iPhone SDK to your Xcode project.
1. Download the Facebook iPhone SDK and unzip. Open the Xcode project in facebook-connect/src/.
2. Drag the folder “FBConnect” from that project into yours, but do not check the option to “Copy items...”. Make sure that is unchecked.
3. In Xcode, select from the menu Project > Edit Project Settings. Find the line to add a Header Search Path under the section “Search Paths”. Add the path to your Facebook iPhone SDK /src/ directory. If this is tricky for you, you can reference my setup:
- My Xcode project is in Documents/carnivalCraze/Carnival iPhone/Xcode Carnival/
- The path to my Facebook iPhone SDK is Documents/facebook-connect/
- So my new header search path is http://www.cnblogs.com/../facebook-connect/src
4. Add the CoreGraphics framework to your project.
5. Add #import "FBConnect/FBConnect.h" to AppController.h and build your project. If you did everything right so far, you will be error free. If not, double-check your work so far.
Integrating Facebook
Next, you need to prompt the user to log into facebook. This is a one-time prompt that you can put anywhere in the game, and the login is cached. Since I want to wait to log the player into facebook until they attempt their first wall post, I'll fire the login code when they push the button to post.
First, you need the controller to implement the FBSessionDelegate and FBDialogDelegate protocols. You should create NSStrings for your Facebook API key and app secret. Also declare an FBSession pointer. Now the interface looks like this:
In AppController.h
@interface AppController : NSObject<UIAccelerometerDelegate, UIApplicationDelegate, FBSessionDelegate, FBDialogDelegate >
{
UIWindow* _window;
FBSession* session;
NSString* apiKey;
NSString* appSecret;
}
To keep your code clean, you should also add the function declaration:
- (void) postToFacebookWithKey: (NSString*) key andValue: (NSString*) val;
In AppController.mm
Define your API key and app secret. You find these through the Facebook Developer App by clicking on the app you setup earlier. You can sssign these in applicationDidFinishLaunching to keep everything in one place. The final version of this function looks like this:
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
printf_console("-> applicationDidFinishLaunching()\n");
apiKey = @"YOUR API KEY HERE";
appSecret = @"YOUR APP SECRET HERE";
[[NSUserDefaults standardUserDefaults] addObserver:self
forKeyPath:@"Fortune" options:0 context:nil];
[self startUnity:application];
}
Log into Facebook
After catching NSUserDefault in observeValueForKeyPath, call your new function:
[self postToFacebookWithKey: keyPath andValue: val];
That function definition looks like this:
- (void) postToFacebookWithKey: (NSString*) key andValue: (NSString*) val
{
session = [FBSession sessionForApplication: apiKey secret: appSecret delegate:self];
if ([session resume] == NO)
{
FBLoginDialog* dialog = [[[FBLoginDialog alloc] initWithSession: session] autorelease];
dialog.delegate = self;
[dialog show];
}
}
This code checks whether the users has logged into facebook yet, and if not, presents him with the dialog to do so.
Another requirement is to handle the callback when a login is successful. This is necessary since AppController implements the FBSessionDelegate protocol. Add the following right below postToFacebookWithKey...
- (void)session:(FBSession*)session didLogin:(FBUID)uid {
}
The last thing you need to do is cleanup, so add this to (void) dealloc in AppController.mm:
[session.delegates removeObject: self];
Run your project and enjoy seeing the Facebook login dialog pop up over the Unity canvas.
Post to Your Facebook Wall
You'll want to get very familiar with the documentation for FBConnect to understand better how the post works. If you're logged in, this sample code prompts you to post something to your wall. After adding you're own content, press run and see the successful culmination of all your work! Please add your comments about additions you'd like to see and any question you may have!
- (void) postToFacebookWithKey: (NSString*) key andValue: (NSString*) val
{
session = [FBSession sessionForApplication: apiKey secret: appSecret delegate:self];
if ([session resume] == NO)
{
FBLoginDialog* dialog = [[[FBLoginDialog alloc] initWithSession: session] autorelease];
dialog.delegate = self;
[dialog show];
}
else
{
NSString* name = @"Carnival Craze for iPhone";
NSString* itunesURL = @"http://itunes.com/apps/CarnivalCraze/";
NSString* caption = @"The Fortune Teller Says:";
NSString* description = val;//This holds the fortune to be posted.
NSString* imageSrc = @"http://store.chapbros.com/images/carnival/FT_128.jpg";
NSString* publishersName = @"Chapbros";
NSString* publishersURL = @"http://chapbros.com/";
FBStreamDialog* dialog = [[[FBStreamDialog alloc] init] autorelease];
dialog.delegate = self;
dialog.userMessagePrompt = @"What do you think of this Fortune?";
dialog.attachment = [NSString stringWithFormat: @"{\"name\":\"%@\","
"\"href\":\"%@\","
"\"caption\":\"%@\",\"description\":\"%@\","
"\"media\":[{\"type\":\"image\","
"\"src\":\"%@\","
"\"href\":\"%@\"}],"
"\"properties\":{\"brought to you by\":{\"text\":\"%@\",\"href\":\"%@\"}}}",
name, itunesURL, caption, description, imageSrc, itunesURL, publishersName, publishersURL];
[dialog show];
}
}