A while back it occurred to me that it might be nice to use the iPhone’s native UI for the non-game UI part of my Unity iPhone games.
It’s taken a few weeks and some climbing of the dreaded learning curve, but after much high pitched whining (climbing learning curves can do that), I managed to put together a simple Unity project that does what I set out to do. Turns out that it’s not too difficult, ALC (After Learning Curve).
Before I get started I want to mention that this won’t be a detailed tutorial. I will focus on what I did to integrate iPhone native UI with Unity. In later entries I will discuss other aspects of the project like the TouchManager class and perhaps some of the Objective-C code I wrote for the native UI.
I used Unity Pro / iPhone Advanced 3.0.0b4 and iOS 4.0.1 iPhone SDK to develop the project. It currently won’t run in the simulator, maybe someday it will. Until then, you will need to deploy it to an actual device to run the project.
You can download the Unity project here: iPhoneNativeUIExperiment.zip
Of course, before I could go very far with this experiment I needed to learn more about Objective-C and the iPhone SDK, specifically the UIKit framework.
Here are a few links to some of the books, articles, and forums I found to be helpful.
- Beginning iPhone 3 Development: Exploring the iPhone SDK (most helpful so far)
- iPhone for Programmers: An App-Driven Approach (Deitel Developer Series)
- OpenGL And UIKit Demo (several other very good articles are here as well)
- View transitions
- iPhone Dev SDK (forum)
- Learning Objective-C: A Primer
- Introduction to The Objective-C Programming Language
- iOS Reference Library
I spent the better part of two weeks going through Beginning iPhone 3 Development: Exploring the iPhone SDK and typing in the example programs before I started having thoughts like, Oh, that’s what I need to do to make my app do what I saw that other iPhone app doing. It may take some time to climb this learning curve if you’ve never done any iPhone SDK programming, but I think it’s worth it.
I’ve been programming for almost 30 years. I’ve programmed in C/C++, Java, and C# (and a few others I don’t care to mention). The previously mentioned programming languages are somewhat similar and I have been able to go from one to the other without too much mental effort.
But Objective-C? Bless its heart (or is that my heart), in the beginning it just made my brain hurt to look at it. To help deal with my brain pain, I took the advice of my first computer science professor and employed the monkey see, monkey do learning technique, i.e. just do what I tell you to do and you will figure out what it means later. It still works.
So take a deep breath, start typing in the examples and before long you’ll be coding in Objective-C and leveraging the iPhone SDK in your Unity iPhone games.
My first goal was to create a Unity iPhone application that seamlessly starts up with iPhone native UI in control.
Secondly, I wanted to be able to add the Objective-C code and supporting files to the Unity generated Xcode project with minimal impact to the project and the Unity generated code, just in case I wanted to regenerate the project from scratch instead of appending it from Unity.
The app demonstrates several different things available in the iPhone SDK that I was interested in learning. Most of them are in the UIKit framework. I wanted to try out iAd, so I tossed that in too.
I wont try to explain any iPhone SDK concepts here. The book Beginning iPhone 3 Development: Exploring the iPhone SDK is a much better place to go for that. Suffice it to say, I still have a lot to learn and I don’t want to mislead anyone with a poor explanation of what I did. Besides, the truth is in the code, right?
I did make an effort to comment my code and hopefully that will help as you study it. One thing you might also do is fire up the debugger and step through the code so you can see what it does.
The Main View
The code for handling this view is in RootViewController.m , LevelSelectionViewController.m, and LevelStartViewController.m. The view and its components are defined in RootView.xib and LevelStart.xib.
Tap the (i) button to see the view transition to the splash screen. Tap the splash screen again to go back. (Demonstrates a view transition with an animation block.)
The Enable / Disable Ads button will toggle iAd. If ads are enabled, an ad will drop down after the level starts. You have to tap the ad and view it to get rid of the ad banner. AdBannerViewController.m handles iAd integration, AdBanner.xib defines the UI components.
Tap the image to load the level. After the level is loaded the native UI view will slide down and the level will start or just unpause if it was already loaded. (Demonstrates another view transition with an animation block.)
Tap the Settings table cell to drill down to the level settings. (Demonstrates hierarchical view navigation.)
Swipe the screen to page over to the next level. (Demonstrates paging with UIScrollView.)
The Settings View
The code for handling Level Settings is in LevelSettingsViewController.m. The table cells are defined in LevelSettings.xib.
Swipe to Rotate and Auto-Rotate change the way the cube gets rotated.
Angular Drag determines if any drag gets applied to cubes rotation.
Clear Highest Spin Speed pops up an alert box and resets the value to zero if you answer yes.
These settings get stored in the standard user defaults via PreferencesBinding.m and are retrieved by Unpause() in GameManager.cs when the level is started.
The “Game” View
The code for the game view is handled in C#. There are several modules in the Assets/Scripts folder. GameManager.cs is the main module.
If Swipe to Rotate is on you can swipe the screen to spin the cube. Touching the screen again will stop the rotation. If Angular Drag is greater than zero the rotation will slow down and eventually stop, the slow-down rate depends on how high the drag is set.
At the bottom of the screen, the top number is the current spin speed, the bottom number is the highest spin speed.
Tapping the || graphic will active the native UI and pause the game.
The Unity Project
The Unity project consists of 5 scenes, the StartupLevel scene and four game scenes.
The StartupLevel scene has no visible GameObjects and is only used when the application starts.
A game scene has a cube that rotates and displays other “vital” game information.
I created a couple of C# modules, PreferencesBinding.cs and UIBinding.cs and placed them in the Unity projects Assets/Plugins folder. Their native code counterparts, PreferencesBinding.m and UIBinding.m are located in the Assets/NativeCode directory with the rest of the native code modules.
You can find more information about writing Unity plugins here. Unity – Plugins.
Adding Native Code to the Xcode Project
I took the following steps to add my native code to the Unity generated Xcode project.
- Create a folder called NativeCode in the Unity project Assets folder for all of the Objective-C code and supporting files.
- Generate the Xcode project from the Unity Build Settings dialog and open it in Xcode.
- Add the NativeCode folder to the Xcode project by dragging the folder into the Xcode project tree.
- Make sure “Copy items into destination groups folder” is unchecked so the files will remain in my Unity project folder.
As I added more Objective-C code and XIBs I just right-clicked on the NativeCode folder in the Xcode project tree and told it to create the files in the Assets/NativeCode folder. Doing it like this made sure all my custom Objective-C code stayed in my Unity project folder.
Note: You will also need to add the iAd Framework to the Xcode project after you generate it with Unity. To add the iAd Framework, right click on the Frameworks folder in the Xcode project tree. Select Add/Existing Frameworks. Select iAd.framework from the list.
UIBinding.cs provides two static methods ActivateUI() and DeactivateUI().
Calling ActivateUI brings up the native UI on top of the view Unity uses to render the scene.
StartupLevel is the first scene to be loaded when the application starts. It has a GameObject called Startup, with a corresponding script called Startup.cs. Startup.cs calls ActivateUI() from its Awake() method. ActivateUI() calls the native C function _ActivateUI() which is located in UIBinding.m.
The first time _ActivateUI() is called it will initialize the native UI views and components then display the splash screen, which will be hidden after a slight delay.
The splash screen is the same image that I designated as the Splash Image in the Unity player settings. Setting it up like this is how I seamlessly hand off control to the native UI. It’s “seamless” from the users perspective anyway.
Once the native UI is up and running you can select a level, run the selected level, change the settings, etc.
PreferencesBinding.cs and its corresponding PreferencesBinding.m provide methods to store and retrieve level settings and application state using [NSUserDefaults standardUserDefaults]. Unity provides access to this through the PlayerPrefs class but I decided to write my own code to handle it so I could use the same functions in Objective-C as I use in C#.
MessageReceiver.cs provides methods that are called from Objective-C via UnitySendMessage. By using UnitySendMessage I can load levels, pause, and unpause the game from Objective-C.
I’m pleased with the results from my experiment. By activating the native UI in the StartupLevel scene, I was able to seamlessly start the application with the iPhone native UI in control.
Adding the Objective-C code and supporting files to the Unity generated Xcode project turned out to be a simple matter of dragging the NativeCode from the Unity projects Assets folder and dropping it into the Xcode project tree.
Don’t forget: Because I also included iAd in this project you will need to add the iAd Framework to your Xcode project. See the note above for more information.
Another thing that I need to experiment with down the road is game performance within this setup. Hopefully when the other views are hidden it wont be an issue.
Here’s the link to the Unity project: iPhoneNativeUIExperiment.zip
Here’s another link to a more generic and most excellent approach to integrating native UI with Unity. Thanks Mike! Unity plugin to handle loading native Cocoa UI’s with ease
Thanks for stopping by.