In case that you haven’t read my previous post, let me give you a brief introduction to Dungeon. In this demo, the player takes control of a Knight trying to escape from a dungeon full of zombies. The gamplay resembles Blizzard’s Diablo series, using the mouse to move and attack. This kind of controls are perfect for touch devices.
Today I’m going to talk about the process of porting the Dungeon demo to iOS. The goal was to create a universal application running on both the iPhone 3Gs and the iPad, reusing as much as possible of the existing code base. What follows is a postmortem of the porting process, focusing on some of the problems I encountered on the way, the decisions I made and their outcomes.
What went wrong
I spent about an hour dealing with project settings before I could actually have something running on the iPad. When I created the Xcode project, I forgot to indicate that I wanted an Universal Application. When I realize it, I tried to use the target upgrade process as it is indicated in the documentation, but that didn’t work out, so I had to re-create it from scratch. Plus, I found several linking errors related with C/C++ runtime libraries as well, caused by not setting the iOS Deployment Target property correctly in the project configuration section. I’m going to have these things in mind when creating a CMake template for iOS projects later.
After I finished with project configuration, building the project was pretty simple considering that I reuse most of the code from the desktop demo (more on that below). But to my surprise, the game was running too slow on the iPad (and it was completely unplayable on the iPhone).
The cause for such a low performance on the actual devices was the scenario itself. Although I was using vertex buffers to reduce memory footprint on wall geometries, each wall was still being stored as a GeometryNode object, which resulted dozens of drawing calls per frame just to render the scenario. So, I modified the dungeon creation process in order to use a single GeometryNode object, reducing the drawing call to just one. As expected, that changed alone put the demo running back at ~35fps on the iPhone 3GS and at 60FPS on the iPad.
Performance was never an issue on the desktop version so I didn’t pay too much attention to it at that time. I definitely learned my lesson here.
Rendering & Picking bugs
One bug that kept me awake late at night was related with how the iPhone renderer implementation handles camera transformations. It turns out I was miscalculating the modelview matrix for the scene and nothing was drawn on the screen. I never had that problem before in the other iPhone demos just because none of them is using dynamic cameras. The desktop version doesn’t have this problem either because the renderer is implemented in a completely different way.
In addition, implementing object selection by using the touch screen was a lot harder than expected. The main issue here was that I was rotating the view in order to achieve the landscape orientation, which ended up causing problems when calculating picking rays from the camera. In the ended, I rotated the camera instead, avoiding not only the picking bugs, but also the use of CoreAnimation to rotate the view (which is something that Apple discourages when using OpenGL ES).
What went right
So, the goal was to reuse as much code as possible, which I did. About 90% of the code for the iOS Dungeon Demo was taken “as is” from the desktop version. Only a couple of classes needed to be modified or written from scratch in order to support the iOS features.
On the other hand, the modifications I had to make in order to improve performance on the devices are reusable on the desktop example as well.
Using CMake to create the iOS library project
The Crimild.iPhone extension is an Xcode project that is used to build the engine as a static library that can be used on iOS projects. The problem with that project was that it had to be maintained manually every time something changed on the engine’s core libraries, adding new files or removing those that are no longer needed. This process was tedious and very error prone.
So, after some research, I managed to create a CMake template that automatically creates the Xcode project for me. Now, I just need to run CMake if something changes, saving a lot of time and effort.
The Resource Manager
On the desktop version, all resources were located in the “Resources” folder. But on the iDevices, resources are usually stored at the root of the bundle, so I have to change all paths. Fortunately, I foresaw this problem when I was coding the original demo and created a ResourceManager class, which lets me specified the root directory for resources. This class has been recently promoted to the Crimild.Simulation library since I’m considering using it in future examples.
Although it took me a lot more than I was expecting, the result of the porting process was positive because I ended up reusing most of the code, which was my goal from the very beginning. After all, Crimild is supposed to be a multiplatform engine.
On the down side, I wasn’t expecting performance issues at this point, considering the simplicity of the demo (there is no interface nor special effects). I definitely need to work more on that.
Although there was a lot code that was recycled from the desktop version of Dungeon, I still had to clone that project in order to build it for the iOS. The reason is that I’m keeping the iPhone version of Crimild as a separated extension. But this is about to be changed. My plan is to include the iPhone extensions in the main code branch, using a single CMake configuration to handle all platforms.
After that, the next step will be to create a CMake template for iOS projects. That way it will be easier for developers to create Crimild-based games and applications.