Sample - Portable Mobile Game Code

Posted by jiGGaK on April 26th, 2011

I’ve decided I want to try and break into the market of mobile gaming but I needed a way to evaluate how practical it was to target multiple platforms at once. Android and iOS are the main players right now but keeping myself open to others seems like it would be a prudent step. And writing everything from scratch multiple times is not an option.

If we look at all of the popular mobile platforms, they each provide some sort of native development kit all based around a GCC toolchain with OpenGL ES 2.0. So obviously we want to use as much portable C/C++ as possible, but what about all the little (and not so little) differences between the platforms? I needed to get my hands dirty and write a proof of concept.

The result is a simple bouncing ball simulation and it runs on Android, iOS, and desktop (Mac, Windows, Linux). Box2D is used for the physics engine, and the rest of the code is licensed under the permissive beerware license; please respect the license and buy me a beer… if we ever meet.

The code: what it is and is not

This is 2D only. This is not a framework. This is not everything needed to make a game. The purpose of this demo is to get an idea for how one might structure a project to maximize the use of portable code.

As it turns out using C/C++ for our game engine, logic, and graphics accounts for quite a lot. But several platform specific aspects are worth mentioning.

The obvious first difference is the fact that Android is Java/Dalvik and iOS is Objective C/Cocoa Touch. The basics for application development differ significantly from platform to platform so we have to just grin and bear it. For Android we need to use JNI (when targeting Android 2.3 and up the NDK supports writing all code in C using native activies) to call into our C/C++ code (avoid JNI inside render loop due to overhead). On iOS Objective C is a pure superset of C so it mixes nicely. You can also tell XCode to use the Objective C++ compiler which lets you freely mix C++ and Objective C by simply making sure your implementation files have a .mm file extension.

The second is the way OpenGL context is setup. On Android we have GLSurfaceView, on iOS EAGLContext/CAEAGLLayer (see GLES2Sample), and desktop freeglut.

The third is asset loading. To handle this in the demo I defined a basic set of function declarations that need to be implemented on each platform (see assets.h).

/// Structure containing raw asset data and length of data.
typedef struct asset_t {
   uint8_t *data;
   int size;
} asset_t;

/// Load asset from relative path.
asset_t * loadAsset(const char * path);

/// Deallocate an asset structure returned by loadAsset() function.
void freeAsset(asset_t * asset);

On iOS the implementation is simple: resolve the path relative to the applications bundle and read the file into memory.

Android was a bit more complicated. I had to do some JNI voodoo to call back into the Dalvik VM to open and read files. The Java code itself was simple, and the JNI code isn’t that bad either, but there is some serious implications when it comes to JNI state. Basically in order to call back from C/C++ into Dalvik we need a valid pointer to the JNI interface and the JNI pointer to the instance of Activity but these pointers are only guaranteed to be valid within the scope of a JNI call. I ended up just saving the JNI related pointers into global variables before calling any engine code that will call the loadAsset() function (when targeting Android 2.3 and up the NDK provides a native asset loading API).

The last platform difference I want to mention is user input handling. In the demo a simple struct is defined to abstract out the type of action and the related (x,y) coordinate:

/// Action structure containing the type of action and x,y coordinates.
/// Touch actions use x,y coordinates in screen space. Tilt action uses
/// x,y values to represent a force vector in the range -9.18 to 9.18.
typedef struct action_t {
   action_t(action_type_t t_, float x_, float y_) : type(t_), x(x_), y(y_) { }
   action_type_t type;
   float x, y;
} action_t;

The render loop extracts these actions from a queue and handles them. The threading model varies from platform to platform so it’s important to make sure that actions are queued on the same thread as the renderer to avoid potential race conditions.

Running the demo on desktop

To build on the desktop a few libraries are used to simplify the use of OpenGL: freeglut for creating the window and OpenGL context, and GLEW to check for OpenGL 2.0 (OpenGL ES 2.0 is a subset of OpenGL 2.0) support and include the various OpenGL headers.

Use your distro’s package manager to install freeglut and GLEW:

$ sudo pacman -S freeglut glew

On Mac OS X an implementation of GLUT is provided with XCode. To install GLEW first install MacPorts and then run:

$ sudo port install glew

On Windows MinGW needs to be installed and configured. Download the Automated MinGW Installer package (at time of writing the mingw installer was mingw-get-inst-20110316.exe) to simplify things and select C++ Compiler and MinGW Developer ToolKit during setup.

Once installed download the freeglut and GLEW source packages and compile/install them from the MinGW Shell:

cd glew-1.5.8
make install GLEW_DEST=/mingw
tar -zxvf freeglut-2.6.0.tar.gz
cd freeglut-2.6.0
./configure --prefix=/mingw
make install

To build the demo just run make in the desktop directory. The asset loading implementation looks for assets in the path ../assets so be sure to run the balldemo executable from inside the desktop directory.

Rafael July 4th, 2011

Works now . Thanks !

jiGGaK July 4th, 2011

Sorry about that. For some reason I disabled the git vhost on my server and another vhost was interfering. Should be fine now.

Rafael July 4th, 2011

It’s asking a password! Maybe your daemon is configured incorrectly ?