Tips For Working With Unity #5: Unity iPhone
filed in Open Development, Unity on Sep.17, 2010
Editor’s note: This is part 5 in a 5 part series outlining our experiences with Unity & Unity iPhone. Be sure to check out Part 1, Part 2, Part 3, and Part 4.
Our first game, Ace Attack, is an iPhone game based on the card game War. This posed a big challenge for us, because only one of us actually has a Macintosh, and so only one of us could actually use Unity iPhone and test stuff on the iPod Touches that we purchased for development purposes.
The Unity iPhone setup procedure is documented elsewhere, so I won’t get into that. However, I will discuss the process of sharing a project between the free version of Unity on Windows, and Unity iPhone Basic, and also some things we’ve discovered while developing Ace Attack.
Tip #1: Unity iPhone isn’t code-compatible with Unity
The first thing to know is that just because your code compiles on Unity in Windows, doesn’t mean that it will compile on Unity iPhone. Unity iPhone does not, for example, implement the Generic versions of the GetComponent() method. So, despite Tip #2 in yesterday’s post, you will end up writing a lot of code that looks like:
MyComponent c = GetComponent(typeof(MyComponent)) as MyComponent;
There are other similar things that don’t translate, so, especially early on, it is important that you go back and forth between Unity iPhone and Unity Windows and find all of the things that Unity iPhone doesn’t like.
Tip #2: Unity iPhone isn’t binary-compatible with Unity
When you load up a Unity project in Unity iPhone, Unity iPhone will go through a long and expensive conversion process to compress all of the textures using PVRTC, and to convert the metadata file formats to its own format. This means that you should NOT make the person with the Mac the Library Manager, or it will break everybody else. Throughout the development of Ace Attack, we had to completely clobber and recreate the Unity iPhone project about a dozen times. Be prepared for this step.
Tip #3: Instantiate() is VERY expensive
If you can avoid instantiating a prefab at runtime, do so. We found delays of up to two seconds in our code which were traced back to calling Instantiate(). We ended up pre-creating most of our objects, and, where we could not do so, moving the Instantiate()s to places where people would either not notice, or notice it less.
Tip #4: UI textures
Do not compress your UI textures, and turn off mipping on them as well. The reason for this is that PVRTC compression and mipping will mangle your nice, clean UI graphics. If you can get away with 16-bit uncompressed, go with that, but 32-bit uncompressed will always look better. We will be shipping Ace Attack with a 32-bit uncompressed UI texture. Make sure to limit the size of your UI texture to 512×512. If you go up to 1024×1024, you’ll eat up more memory than you can really afford, and your performance will suffer immensely. We went from 40-50ms/frame, down to 14-16ms/frame just by doing the work to convert (and remap all the UVs) our UI texture from 1024×1024 to 512×512.
Tip #5: Swipe Gestures
A lot of people on the Unity forums have put a lot of thought into detecting swipes. The one that we’re shipping Ace Attack with (though slightly modified) can be found at:
http://forum.unity3d.com/viewtopic.php?p=288889#288889
Tip #6: Avoid changing material properties at runtime
Unity iPhone will batch draw calls, so long as the mesh’s triangle count is less than 300, and so long as the meshes share a material. The trouble is that once you change any properties of a material at runtime, Unity will create a copy of the material, and the respective mesh is forever doomed to be in its own batch. Worse, there’s no way (as far as I can tell) to hook the material back up to the shared material once it’s been disconnected. So, where you must fade something out (perhaps using iTween!) or otherwise change some properties on its material, either be sure to destroy the object after the fade is complete, or be prepared to accept the fact that it is now permanently using its own batch.
Of course, this causes problems with the whole “Instantiate() is expensive” tip, because if you want to fade an object out, but then bring it back later with a shared material, you’ll have to destroy and reinstantiate it. In our experience, there is no good solution to this conundrum.
Tip #7: PlayerPrefs are a good way of communicating with native code
Using Objective-C, you can register a callback which triggers whenever the application writes anything to its save file. You can use this to communicate with a 3rd-party API, or with your own code. We use this mechanism to communicate with the Facebook API, and to query whether the the device is playing iPod music.
At this point, I will wrap it up. There are, of course, lots of unexplored nooks and crannies in both Unity and Unity iPhone, and many lessons that we’ve learned that I haven’t the time to share. As we at BitFlip Games finally bid farewell to Unity for unexplored territory, I hope that this series has been useful for people who are either using, or considering using, Unity.
Look out for Ace Attack in September!
September 17th, 2010 on 11:08 am
Thanks, Guy!
September 20th, 2010 on 10:36 am
Hi Paxton!
You’re very welcome. Hope things are going well with you!
December 27th, 2010 on 11:09 pm
[...] Unity iPhone [...]