Category Archives: Development

OpenFeint and Unity 3.3, iOS SDK 4.2

Developers seem to be having quite a few problems trying to integrate the latest versions of OpenFeint (2.8, 2.9) with their Unity 3 iOS games. See the following for some discussions of the problems:

Unity Forum: OpenFeint-iOS-2.9-problems
Unity Forum: my-iPhone-application-is-getting-crash
Answers.Unity3d: crash-at-first-launch-with-openfeint.html

To save time as I try to put out a bug-fix for Pawns, I decided to stick with OpenFeint 2.7.5.

However, I ran into some of the problems people had with later versions of OpenFeint. Specifically, the app was crashing with one of the following errors when I closed the OpenFeint dashboard to return to the game:
mi_cmd_stack_list_frames: Not enough frames in stack.
or:
[CAMediaTimingFunctionBuiltin deactivate]: unrecognized selector sent to instance

So apparently the upgrade to Unity 3.3 and/or iOS SDK 4.2 is where this problem started, not because of changes to OpenFeint. The solution, as reported by developers using later versions of OpenFeint, was to comment out calls to UnitySetAudioSessionActive in the dashboardWillAppear, dashboardDidDisappear methods in AppController+OpenFeint.mm:

// OpenFeintDelegate //
- (void)dashboardWillAppear
{
	unityPaused = YES;
	//UnitySetAudioSessionActive(false);
	UnityPause(true);
}

- (void)dashboardDidDisappear
{
	unityPaused = NO;
	//UnitySetAudioSessionActive(true); 
	UnityPause(false);
}

Another symptom I noticed was that when my game was exited under iOS 4 and then brought back to the foreground, the game would appear but then immediately quit (though iOS still listed it as running for some reason.) Bringing it forward a second time would basically run it over from scratch. I concluded that it was crashing on resume, and the culprit was the same call to UnitySetAudioSessionActive, this time in two other delegates. Commenting those out fixed the issue:

- (void) applicationDidBecomeActive:(UIApplication*)application
{
	printf_console("-> applicationDidBecomeActive()\n");
	
    if (gDidResignActive == true)
    {
	    //UnitySetAudioSessionActive(true);
	    UnityPause(false);
    }
	
    gDidResignActive = false;
	
	[OpenFeint applicationDidBecomeActive];
}

- (void) applicationWillResignActive:(UIApplication*)application
{
	[OpenFeint applicationWillResignActive];
	
	printf_console("-> applicationDidResignActive()\n");
	// UnitySetAudioSessionActive(false);
	UnityPause(true);
    gDidResignActive = true;
}

The fact that this problem apparently persists in OpenFeint 2.8 and 2.9 isn’t good. I get the distinct impression that OpenFeint’s Unity wrapper is not very well supported.

Fixing Unity and OpenFeint iPad Rotation Issues

Unlike iPhone games, Apple requires iPad games to support orientations symmetrically. For example, if a game works in one landscape orientation, it should also work when rotated 180 degrees. And this should be true whether the iPad is rotated before the game is run, or while it is played.

There are well-known Unity scripts to address the two major rotation issues most iPad developers run into. I’ll review those solutions first, then describe how to fix new problems introduced with OpenFeint.

Basic iPad Rotation Behavior

There are two behaviors that Unity games should implement for the iPad:
• Change the game’s orientation when a new (supported) orientation occurs
• Turn off the “rotating black square” seen as iOS rotates its menu bar (the one that is normally hidden behind your Unity game.)

I take no credit for the following solution, although I combined them into one script for my own convenience:

public class iPadRotationFixer : MonoBehaviour 
{
    void Start()
    {
        // Turn off the visible iOS menu rotation
        iPhoneKeyboard.autorotateToPortrait = false;
        iPhoneKeyboard.autorotateToPortraitUpsideDown = false;
        iPhoneKeyboard.autorotateToLandscapeLeft = false;
        iPhoneKeyboard.autorotateToLandscapeRight = false;
    }
	
    void FixedUpdate () 
    {
        // In this example, only the two landscape modes are supported
        if ((Input.deviceOrientation == DeviceOrientation.LandscapeLeft) 
                && (iPhoneSettings.screenOrientation != iPhoneScreenOrientation.LandscapeLeft))
        { 	
            iPhoneSettings.screenOrientation = iPhoneScreenOrientation.LandscapeLeft;
        }
	   
        if ((Input.deviceOrientation == DeviceOrientation.LandscapeRight) 
                && (iPhoneSettings.screenOrientation != iPhoneScreenOrientation.LandscapeRight))
        { 
            iPhoneSettings.screenOrientation = iPhoneScreenOrientation.LandscapeRight;
        }
    }
}

Just put this script on an empty GameObject in every scene of your game. (I usually name the object something like “ziOSRotationFixer” so that it sorts to the bottom of the Hierarchy view.) The Start method is only needed in the initial scene, but does no harm to run it more often.

Startup Logo

In your iOS project settings you can also set the Default Orientation, but I don’t think it matters for iPad games. Unity for iPhone 1.7 and earlier had a problem where the Unity splash screen would only show in one orientation on the iPad- you can still find old discussions about how to fix that, but as of Unity 3.0 the splash screen orients correctly on startup.

OpenFeint and iOS 4.2

The above solution has worked with only minor changes since the iPad first came out. So you can imagine my dismay when a tester found rotation problems after I added OpenFeint to Pawns. The tester found that when he double-clicked the Home button during a game, the multitasking bar would sometimes be upside-down. I found the game’s OpenFeint dashboard had the same problem.

Both problems stemmed from my misuse of OpenFeint. Just as Unity needs to be told to rotate the screen orientation, OpenFeint also needs to be told. This just requires adding a couple lines to the FixedUpdate method above, which I’ve marked as new.

    void FixedUpdate () 
    {
        if ((Input.deviceOrientation == DeviceOrientation.LandscapeLeft) 
            && (iPhoneSettings.screenOrientation != iPhoneScreenOrientation.LandscapeLeft))
        { 	
            iPhoneSettings.screenOrientation = iPhoneScreenOrientation.LandscapeLeft;
            OpenFeint.SetDashboardOrientation(DeviceOrientation.LandscapeLeft); // ** NEW **
        }
	   
        if ((Input.deviceOrientation == DeviceOrientation.LandscapeRight) 
            && (iPhoneSettings.screenOrientation != iPhoneScreenOrientation.LandscapeRight))
        { 
            iPhoneSettings.screenOrientation = iPhoneScreenOrientation.LandscapeRight;
            OpenFeint.SetDashboardOrientation(DeviceOrientation.LandscapeRight);   // ** NEW **
        }

        // The above is for Landscape. Don't forget to add Portrait cases if you need them
    }

(Source: http://forum.unity3d.com/threads/50822-OpenFeint-iPad-screen-orientation-on-startup)

This solution works great when the orientation changes as the game is running.

However, the initial OpenFeint dialog can still show upside-down! This problem was discussed in the above thread and in other places, but there doesn’t seem to be widespread understanding of the cause or how to fix it. So I looked into it myself and came up with the following.

The problem is simply that, when you first set up the OpenFeint plugin for Unity, you have to specify the startup orientation. OpenFeint relies on this for the initial orientation.

(Why is the initial orientation fixed and not dynamic? I think it’s mainly because it was originally developed for the iPhone. Also note that the developer can trash OpenFeint’s Portrait or Landscape resources if the game doesn’t need them to save space. So OpenFeint can’t just orient itself dynamically to any direction it pleases. It needs to be told what orientations to use.)

Happily, we can make it check the device’s orientation by adding a few lines to the OpenFeint initialization code in the Xcode project. This is going to work like the FixedUpdate method: we’ll explicitly code for the orientations we need to support.

Here is the old code that we’ll change. It takes the Initial Dashboard Orientation setting and puts it right into the dictionary. (Note: this is the same chunk of code that gets edited when you want to enable Game Center.) Currently the code is in the file classes/AppController+OpenFeint.mm.

    NSDictionary* settings = [NSDictionary dictionaryWithObjectsAndKeys:
         [NSNumber numberWithInt:UOF_INITIAL_DASHBOARD_ORIENTATION], OpenFeintSettingDashboardOrientation,

The replacement code checks the device’s orientation dynamically. Again, I am only supporting left and right landscape modes for my game:

    id orientation = [NSNumber numberWithInt:UIDeviceOrientationLandscapeLeft];
    if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight)
    {
        orientation = [NSNumber numberWithInt:UIDeviceOrientationLandscapeRight];
    }
    NSDictionary* settings = [NSDictionary dictionaryWithObjectsAndKeys:
                orientation, OpenFeintSettingDashboardOrientation,

Now OpenFeint’s initial orientation is correct, and so is the iOS multitasking bar.

Note that this code may get overwritten if I install a new version of OpenFeint’s API into my project. That’s the downside of going in and tweaking OpenFeint’s Unity plugin or their code. Like my last tweak (adding support for percent-complete achievements) it’s possible OpenFeint will fix it themselves in a future version. While they are at it, maybe they’ll add the Game Center switch to the Unity plugin as well. Then there would be no need to edit this code at all.

OpenFeint, Game Center Integration Tips for iOS

I have been adding OpenFeint Achievements (with Game Center integration) to my game, Pawns! for iOS. Here is a collection of time-saving tips that I wish I’d had to start with, particularly the Troubleshooting section. I include links to documents and forum posts that gave me useful answers.

Some of this information is specific to games made with Unity, but most isn’t. Some details may change when new versions of the OpenFeint SDK are released.

General Resources

OpenFeint’s developer website is a little disorganized, in my humble opinion. (For example, the developer forums are not listed in the main menu of the Developer Support page; they are mentioned in an announcement further down the page. ) Here are the main links developers need:

OpenFeint Developer Support: http://openfeint.com/ofdeveloper
OpenFeint Developer Forums: http://openfeint.com/developers/forum/
OpenFeint Developer Dashboard: https://api.openfeint.com/dd

Plus there are the usual Unity resources: answers.unity.com, the Unity forums (more active than OpenFeint’s), and this humble blog. 🙂

Setting up OpenFeint in Unity

° The instructions in the UnitySupport folder in the OpenFeint SDK covers most of what you need to know.

° As instructed I added the file openfeint_offline_config.xml to the Unity editor directory “Offline Config File Goes Here“. However, it didn’t copy automatically into my Xcode project so I had to add it manually. OpenFeint’s documentation does not tell you exactly where to put it. Inside the Xcode project folder OpenFeint/resources works, though it’s possible other places work as well. (Source: http://openfeint.com/developers/forum/showthread.php?89-Offline-Mode-Setup)

Achievements and Offline Mode

OpenFeint supports offline Achievements. After the first successful OpenFeint login and connection to the network, the player can view OpenFeint’s Achievements dashboard even if the device is offline. Achievements earned offline will be synchronized with OpenFeint’s servers later. But users who refuse OpenFeint in the first place cannot see the list.

Because of this I decided to implement my own Achievements page, as well as my own notification at the end of each level when they’ve earned one or more Achievements. It was more work than relying on OpenFeint’s dashboard and notifications, but it’s nice to know that even players who don’t use OpenFeint will be able to see their achievements.

Adding Game Center

° OpenFeint’s instructions for adding Game Center can be found in the Developer Support knowledge base. Currently the document is here: http://www.openfeint.com/ofdeveloper/index.php/kb/article/000089 It’s essential reading (it doesn’t cover Unity specifics, though.)

° As of OpenFeint 2.7.5, the Unity Editor plugin does not give access to the setting that turns on Game Center integration. For now you’ll have to go into the Xcode project and add it to the OpenFeint initialization settings manually, exactly as explained in the instructions. (Currently the code to change is in the file classes/AppController+OpenFeint.mm.)

° The instructions state to add your “OFGameCenter.plist” file to your Xcode project, but as usual they don’t suggest where. Put it in the folder OpenFeint/resources.

° Some versions of the instructions call the file “OFGame Center.plist”, but the filename should NOT have a space in it. (Thank you, Pete Royle for posting this tip in the comments!)

° Note that when you define your Game Center achievements with Apple’s dashboard, there’s nothing stopping you from reusing the OpenFeint Achievement ids. This makes your plist mapping even simpler:

    <?xml version=“1.0” encoding=“UTF-8”?>
    <!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”>
    <plist version=“1.0”>
    <dict>
          <key>Achievements</key>
          <dict>
                <key>677092</key>
                  <string>677092</string>
                <key>718142</key>
                  <string>718142</string>

                <-- etc. -->

          </dict>
    </dict>
    </plist>

° For comparison, here is an achievement image I uploaded to iTunes Connect, followed by the version Game Center presents it to the player. Keep the final appearance in mind when designing your achievement icons (e.g. keep away from the borders.)

Achievement icon as uploaded to iTunes Connect


Achievement as displayed by Game Center

Testing OpenFeint and Game Center

° Before testing your OpenFeint features, go to your OpenFeint developer dashboard and register the OpenFeint user you’ll use for testing. I used my regular OpenFeint user for this, or you could create a new account beforehand using any OpenFeint-enabled app.

If you have beta-testers you’ll unfortunately need the email and password of each of their logins, and your beta testers may not wish to share their passwords with you. So you will either have to create new email accounts and associated OpenFeint users for the testers to use, or ask them to create new test accounts first and send you the particulars.

° Apple recommends you do not use your real Game Center user for testing. To set up a new test account, simply run Game Center on your iOS device, and use it to log your regular account out before you start to test. This tells Game Center to prompt you to login (or create a new user) when it starts up in your game.

° When your app is first run, OpenFeint will let you pick from a list of existing users on the device (if any) or allow you to type a new one. If your test user wasn’t already active on this device you will need to link this new profile to the test user’s account. Do this in OpenFeint’s in-game dashboard: open the user profile and give the test user’s email address and password.

Once that’s done OpenFeint should then attempt to connect to Game Center. Since you logged out from Game Center beforehand it will prompt you to login. This is where you can create a new Game Center user.

° Don’t forget to also test the offline mode. After a successful OpenFeint test, try running the app again with no network connection, and try out the OpenFeint buttons.

Troubleshooting

° If OpenFeint doesn’t mention Game Center at all at startup, double-check that you properly enabled Game Center in OpenFeint’s initialization settings.

° If you get an error that “This game is not recognized by Game Center”, try deleting your app off the device and let Xcode reinstall it from scratch. (Source: http://www.cocos2d-iphone.org/forum/topic/10528)

° When testing offline mode, if you get an error that says that offline mode was not enabled, check that an up-to-date copy of your openfeint_offline_config.xml is in your Xcode project, inside the OpenFeint/resources folder.

° OpenFeint not posting to Facebook and Twitter: this is normal when testing with an app that hasn’t yet been released. Once it is submitted to OpenFeint (to let them know the version with OpenFeint has been submitted to Apple) they’ll turn on the social media links. (Source: http://openfeint.com/developers/forum/showthread.php?273-Can-t-able-to-see-published-event-in-facebook-after-submission-got-succeded&highlight=twitter)

° I had a problem with OpenFeint 2.7.5 notifications never showing when an Achievement was unlocked. I posted a solution for this last month.
http://www.mindthecube.com/blog/2010/11/openfeint-2-7-5-not-notifying-of-achievement
Alternatively, see the section “Achievements and Offline Mode” above for reasons to write your own notifications instead.

* If OpenFeint is working but there is no Achievements dashboard for your game, or some achievements are missing, make sure your game’s bundle version is set high enough. For example, you may have configured your OpenFeint achievements to be visible with version 1.1 or higher. (In Unity, the version is set in the project’s Player settings before rebuilding the XCode project. Non-Unity developers would update directly the Bundle Version in their game’s Info.plist file.)

If the achievements still aren’t visible, then make sure you registered the test user in OpenFeint’s Developer Dashboard. Also double-check that you added the test user’s email and password on the player dashboard on the device.

Conclusion

Adding OpenFeint and Game Center wasn’t difficult, but there was some trial and error, a couple of gotchas, and I had to do a bit of searching for some of the answers. Hopefully these tips will smooth the way for others, and for myself when I start working on my next game.

OpenFeint 2.7.5 and Achievement Notifications

I’ve been adding OpenFeint 2.7.5 to my iOS game Pawns!, and ran into what I suspect is a bug. When an achievement is earned, the user is not getting notified. (I found developers on a Torque forum who ran into something similar with 2.7.5.)

This seems to have been introduced when OpenFeint added their new API for setting achievements to be partially complete. Or maybe it has something to do with their new GameCenter integration.

Poking around the OpenFeint code and its Unity wrapper, I found that when the Unity script calls

OpenFeint.UnlockAchievement(MY_ACHIEVEMENT_ID);

the following Objective-C code eventually executes in AppController+OpenFeint.mm:

// Unlock the achievement.
[OFAchievementService updateAchievement:[args objectAtIndex:1]
					 andPercentComplete:100
					andShowNotification:FALSE
					  onSuccess:success
					  onFailure:failure];

Why is notification explicitly turned off here? Was it deliberate? I have no idea. A quick fix might be to set FALSE to TRUE. But, you risk losing that change the next time you upgrade OpenFeint’s code.

I noticed just below this code that the OpenFeint plugin is prepared to handle another command from Unity, “UpdateNotification”, that takes additional arguments, namely the percent complete and whether to show notifications:

	// UpdateAchievement //
	else if(OF_CMD(@"UpdateAchievement"))
	{
		// Set up the delegates.
		OFDelegate success(self, @selector(onAchievementSuccess));
		OFDelegate failure(self, @selector(onAchievementFailure));
		
		// Unlock the achievement.
		[OFAchievementService updateAchievement:[args objectAtIndex:1]
							 andPercentComplete:[[args objectAtIndex:2] doubleValue]
							andShowNotification:[[args objectAtIndex:3] boolValue]
									  onSuccess:success
									  onFailure:failure];
	}

Looks useful! The only problem is that they haven’t yet added this command to the Unity C# wrapper.

But that’s easy to fix! Just add the following to OpenFeint.cs in your Unity project:

	// UpdateAchievement //
	//
	// Unlock an achievement.
	//  - achievementId:      The ID of the achievement.
	//  - percentComplete:  How much to unlock
	//  - showNotification:   Whether to notify
	//
	//	events				OnAchievementUnlocked
	//
	static public void UpdateAchievement(long achievementId, int pct, bool showNotif)
	{
		PerformCommand(String.Format("UpdateAchievement|{0}|{1}|{2}", achievementId, pct, showNotif));
	}

Now to unlock an achievement I call:

OpenFeint.UpdateAchievement(MY_ACHIEVEMENT_ID, 100, true);

This seems to fix the notification issue, and gives Unity scripts a way to set the percentage-complete of an achievement (I didn’t test this.) I also prefer this solution because it is very likely that OpenFeint will themselves add this to their Unity wrapper class in the future.

(If you are really paranoid, you could put this code somewhere else so that future OpenFeint wrapper changes won’t overwrite it. All it does is format a particular PlayerPref string that triggers the OpenFeint plugin.)

The Case of the Lingering Collider

Unity 3 for iPhone has some new behavior that I just ran into. When switching from one Pawns puzzle to the next, my code destroys one set of objects then creates others. This has worked for years, but in Unity 3 builds running on an iPhone, I noticed that the colliders of just-destroyed objects are now sometimes hitting objects being created afterwards.

This only happened when running on the actual device, not in the editor. Weirdly, I only see it happening for colliders on a particular type of object (the red checkers.)

My workaround was to deactivate each object just before destroying it:

	foreach (GameObject gameobj in transients)
	{
            gameobj.active = false;  // <-- NEW
	    Destroy(gameobj);
	}
        // .. create new objects, etc..

I should probably file a bug on this, but since it doesn't happen for all my objects I can't say for certain that I have the problem entirely figured out. So I'll need to find time to work up a solid example. What little time I have at the moment is spent on a couple thorny GUI issues (will post about that soon.)

Sorry this is so vague, but I figured that it was worth mentioning on the off-chance that it helps someone. If I learn more I'll post.