I've recently started putting my Unity projects in version control. Even though my Unity work is mostly done solo, version control is still extremely useful for recovering from mistakes. It also gives me confidence to make big changes that require breaking my game for a while in order to improve it.
I chose Mercurial, for a variety of reasons that I won't go into here. A very good overview of how to get Unity projects working with Mercurial can be found on the blog of Josh Sutphin.
Like Josh I found SourceTree to be the best of the handful of Mac Mercurial clients I tried.
I own a copy of Mercurial: The Definitive Guide, which I use as a reference. You can read it online for free.
Not all the files Unity creates should be stored in version control. Josh's blog mentions how to filter out the Library and Temp folders from within SourceTree. If you are using MonoDevelop or Visual Studio, and have told Unity to generate those project files, you will probably want those files to be ignored as well.
Rather than filter from inside SourceTree as Josh describes, I simply copied an .hgignore file that someone else had posted online. If you google "Unity3d hgignore" you can find ready-to-use .hgignore files that others have posted. (Here's one.) Generally these all do the same thing, but I did run into one "gotcha". I had some scripts in a subfolder called Scripts/Library, and it was getting excluded without me knowing it (because it too was named Library.) Eventually I noticed and renamed the folder. (The .hgignore file I linked to above does not have this problem, as it carefully only filters out the Library folder from the top-most directory.)
This leads me to my most important tip.
Test Your Version Control
It's all very well adding your project to source control but unless you test it on occasion you can't be certain that you've done it correctly. If you have multiple people working on the project then you will quickly learn when someone has made a mistake; other team members will suddenly not be able to build the project. But if you are working solo you need to occasionally check out a new working directory (SVN), clone the repository (Mercurial) or do whatever is needed to make a fresh copy from scratch. Load it into Unity and run some tests.
Of the three projects I added to Mercurial recently, it turned out that two of them had simple mistakes that caused fresh working copies not to build.
In a professional setting you also want to worry about other dependencies as well. For example, which version of Unity was used, which version of Blender, Photoshop, XCode, etc. At a minimum I'd suggest recording the version of Unity in your commit log messages, or tagging the project, when you upgrade to a new version of Unity. (Before each major Unity upgrade I also archive a copy of the previous version so that I have a way to open old copies of my project in their original environment.)
Simple precautions like these are enough for a hobbyist maybe, but if you spend thousands (or hundreds of thousands) of dollars developing a game, you want to be able to build or restore the entire development environment from scratch in the future. (One way would be to archive a virtual machine-- a snapshop of the OS, tools, and source code used to build the shipping version of the game.) Disaster recovery is one reason, emergency patches are another, but a happier scenario is wanting to port a game years later to a new platform. There are plenty of stories out there about games whose source code was lost forever. (Though occasionally someone gets lucky, as when Jordan Mechner recently rescued the long-lost source code for the original Prince of Persia.)
This doesn't just apply to source code. Game textures are often reduced in size and quality for speed/space considerations, but hi-def versions can be generated as long as the original art assets are kept safe. For example, apparently the original art assets for Baldur's Gate were not kept safe so plans for an HD version had to be shelved.)
Okay, I've digressed quite a bit from my original topic (reminder: it was "using Mercurial with Unity") but I hope I've made my point: test your backups and version control repositories, and give some thought to what other tools and assets you want to save, and how.
I forgot to mention here that Pawns was updated back in May. The main change was to remove OpenFeint-- I blogged about the reasons for this decision earlier. But I also fixed some graphics glitches that were bothering me. And the keys glow now, making them stand out better on small displays.
Apple's Game Kit Programming Guide contains the following passage:
A game that supports multitasking and Game Center must take additional steps when authenticating the local player. When your game is in the background, the status of the authenticated player may change. A player can use the Game Center app to sign out. Another player might sign in before your game returns to the foreground.
Multitasking is available from iOS 4.0 onwards, though not all devices running 4.0 support multitasking. Game Center was added in iOS 4.1.
So what exactly should my game do when the Game Center player changes behind it's back? (Note: I'm concentrating on achievements here, as Pawns does not have leaderboards.)
The simplest method that also allows for offline play is:
- The iOS game tracks achievements that were earned on that device, and stores that information locally.
- If Game Center is available, achievements are also pushed to the server.
- All achievements are pushed to the server, not just as they are earned, but whenever a Game Center player is authenticated. This handles the case where the player may have earned some achievements offline.
In short: make your game work offline, then push achievements to Game Center (one-way) whenever it is available without worrying about which player is now logged in.
This method is straightforward but will lead to two glaring side effects:
- If a player logs into a second Game Center profile, the new profile will automatically get some or all of the achievements that were earned on the first profile.
- Since this is a one-way synchronization (i.e. from game to Game Center) a player with two devices may see some achievements on Game Center but not on one of the devices.
We can try to fix these issues but it quickly gets complicated. (If you aren't interested in a discussion of the complications, feel free to scroll down to the next section: The Experiment.)
For example, fixing the first requires you to be strict about which achievements are earned by which player. You could read the achievements back from Game Center after every profile switch, but then you have to handle the case where the player earned some achievements offline- this requires merging the online and offline lists. If you do that you've also solved the second problem-- a nice feature, but I personally think it's pointless unless the player's entire saved progress (saved games, list of finished levels, etc.) is also stored in the cloud using iCloud, Dropbox, or some other service.
The situation is even more complicated if the game itself supports multiple local user profiles (as opposed to games that treat everyone on the device as the same player.) Note that when the user switches to a new local profile there's no way to switch their Game Center profile for them automatically. We can only do the reverse: switch local profiles when the Game Center player has changed. This approach would work especially well for an online-only game.
In short, there are a lot of decisions to make unless we opt for the simple, "stupid" approach. Being naturally lazy, my inclination was to do this and hope that players (and Apple) would put up with the side-effects. But try as I might I couldn't find any blogs or forum posts describing how other developers handle this, and that made me very nervous.
I decided to see how other game developers handle this. I spent a happy evening playing six iOS games that I'd already earned achievements in, but as a different Game Center player. I also switched to a second device to see if any of them read the player's Game Center achievements back from Apple server.
It was a tough job, but someone had to do it.
The results? Judging from the side effects, the following five games use the simple achievements model I described above:
All of them had the side effect where the original profile's achievements get copied to the new profile. This was even true for Plants vs Zombies, which lets you switch local profiles. The local profiles are completely ignorant of which Game Center profile you are logged into.
The one game which didn't take the easy way out was Space Miner. After switching to the second Game Center profile, Space Miner warned me whenever I tried to load a saved game that had been saved by a different Game Center player. (It was my choice whether to go ahead and load the saved game or cancel.) In addition, it seemed that some but not all of the old achievements were copied to the new Game Center profile! I'm guessing that the achievements specific to a single campaign of Space Miner were NOT copied to the new profile, but achievements that you'd earn over multiple games were copied. I don't claim to understand the nuances of Space Miner's design just from this test, but clearly the developers went above and beyond to try to intelligently reconcile local saved games and achievements with Game Center's achievements.
None of the 6 games ever seemed to read achievements back from Game Center. When I played on the second device, only my Game Center account showed the achievements that had not been earned on that device. (This looked especially odd in Pit Droids, where the achievement "Hutt Flats Unlocked" was shown in Game Center, even though the Hutt Flats puzzles were still locked on that device.)
Note that none of the six games I tested store their saved games in the cloud for access on more than one device. Such a game would not take the simple approach I described here. I believe the easiest approach there would be to force the user to select local or cloud storage for each saved game to avoid having to merge them, but with effort it should be possible to synchronize the two more seamlessly.
It appears that most games simply take the straightforward approach: make and display achievements as if the game were offline, and push achievements to Game Center (one-way) whenever it is available. This wasn't too surprising for games that have no local player profiles to switch between. I wasn't so sure what to expect for games that do have them, but in retrospect it makes sense. Developers are simply counting on the fact that players just aren't likely to switch to a different Game Center profile.
I have decided to remove OpenFeint from the next release of Pawns! for iOS.
Unfortunately, continuing to use the old OpenFeint 2.7.5 SDK, the one I'm familiar with, is not an option in the long run. As many developers are aware, Apple is phasing out use of the UDID that OpenFeint used to rely on. Eventually Apple will release a version of iOS that removes the UDID entirely, breaking Pawn's OpenFeint features (and possible the game itself.)
To work around this OpenFeint requires a two-phase migration. First we must upgrade to SDK 2.12.7, which acts as a bridge between earlier UDID-based SDK's and future ones. This will migrate your OpenFeint users off the UDID, assuming they run your game at least once. Then you must upgrade the SDK again when the UDID is phased out for good.
As usual, OpenFeint's Unity plugin is lagging their SDK. They seem to be hard at work on fixing the problems-- this thread in the OpenFeint developer forums is a good one to watch. But all this has led me to review the benefits I did and didn't get by adding OpenFeint:
- I never saw a sales bump from adding OpenFeint. Other developers have reported a similar experience. Your mileage may vary.
- OpenFeint gave us Achievements across all iOS versions. But most players have moved on to iOS 4 now, which has GameCenter.
- Unity now has a built-in API for basic GameCenter features. It doesn't require editing the Xcode project that Unity generates.
Naturally there are some downsides to my decision.
- Game Center lacks dashboard metrics to let me (the developer) know things like how many players have earned each achievement. OpenFeint has this. However, this isn't critical and I am looking into alternatives.
- I will be losing access to some OpenFeint features I'm not currently taking advantage of, such as in-game forums and challenges (but apparently many of these aren't yet supported by the beta Unity plugin anyway.)
- OpenFeint of course supports more platforms than Game Center.
- OpenFeint gives players ways to share their Pawns achievements to social media, and Game Center doesn't.
My decision comes down to the fact that it's easier for me to rip OpenFeint out than to upgrade to their new SDK. I may revisit this decision in the future, after the UDID issue is settled, and/or if I write a new game with different requirements than Pawns.
I just had a look, and I see that 22 people have now earned the Grandmaster achievement (that is, they have solved all the puzzles.) Congratulations!
(If you solved the puzzles but were not logged into OpenFeint, then you are not included in that total. But congratulations to you too!)