Lexigon: From iPhone to Android (Pt.2)

In part 1 of this posting I started to talked about porting my game Lexigon from iOS to Android.  Go and have a quick squint at part 1 to get an overview of porting from cocos2d to cocos2d-x.   Now I continue on with some of the technical points of porting.

STRINGS

Most things in life are held together with string.  My game is no exception.  However, converting from iOS NSString to C++ various string implementations was where I got myself tied up in quite a few knots.

Before I start, I’ll give you a bit of my background.  I have been, and mostly still am, an old skool Unix hack.   Most of my life has been writing ‘C’ code, typically on the command line in vi.   I have dabbled in most languages, but usually return to ‘C’ if I want to quickly get a demo up and running.   I have been known to call some object orientated languages ‘girlie pointy clicky nonsense’.  This is changing.  I’ve been using Objective-C for a few years now, and think it is pretty darn good.   A testament to the quality of Xcode and Objective-C is that I have managed to create a few apps with relatively high level of complexity.  So now you know where I am coming from, and going, back to the article.

Old school ‘C’ strings (char *) are just a bit of memory where we only consider we are at the end of the string when we get a NULL, (zero) character.  ‘C’ strings are fast to work with, but you have the possibility of buffer overruns and program crashing if your program doesn’t handle them correctly.

Under iOS we typically use NSString for strings, (see Apple’s Documentation for NSString).  This is an object, and you can convert from ‘C’ string to an NSString.

With C++ we have the object type ‘string’.

With the port of cocos2d-x from iOS to C++, the boffins made a type ‘CCString’ which I assume is meant to mirror NSString.  Sadly, when I ported there are some class methods in NSString not available in CCString.

So we have 3 languages, Java, C++ and C.  We also have 4 string types, char *, NSString, C++ string and CCString.   For now, I will ignore Java and Java string types.

The object CCLabelAtlas lets you display some text (or image) from a sheet of letters (or images).  Under Objective-C the labelWithString method uses NSString as the parameter while on C++ it uses char *.   You can of course, call the method c_str() to get a (char *) from a CCString.  But with CCString not supporting all the methods of NSString you will no doubt add more code to your port than just symbol for symbol changes.  At times I found myself converting to C string, doing some character manipulation, then converting back to an object string.    I am sure this will improve over time, but perhaps you should scan over all your string usage before porting.

CALLBACKS

In my game Lexigon it does typical game type things, like high scores, saving settings, etc.  Some of these need to be done from the Java code rather than within the cocos2d-x C++ code.  This means we need to call methods/functions from one language to another.    For this we use JNI, Java Native Interface. Typically, you define ‘native’ methods in your Java code like this.

public native int initLexigon(int initLevel); 

Then you run the command line tool to make a header file which gives a C definition like this.

/* 
 * Class: uk_co_lazybrush_example_Example 
 * Method: initLexigon 
 * Signature: (I)I 
 */ 
JNIEXPORT jint JNICALL Java_uk_co_lazybrush_example_Example_initLexigon (JNIEnv * env, jobject obj, jint i) 
{ ... 

With the JNIEnv and the jobject you can then call back from the C code back to Java. However, what you can’t do is save these as global variables and use them later to call back. This is because these are temporary variables which only exist for as long as the call from Java to C.

However, all is not lost. You can use these temporary variables to get a pointer to the Java Virtual Machine. Which you can then use to get back to your Java object and then the Java object methods.

  ... 
  jenv = env; 
  jobj = obj; 
  ret = (*env)->GetJavaVM(env, &cached_jvm); 
  if (ret!=0) 
    cached_jvm =NULL; 
  ... 

With this you can then asynchronously call Java from your C code. However, you then get another problem. The thread which is running the GUI is a different thread to the Java code, and causes your app to crash. So within your Java callback you need to make it run in the correct context. This is done by :-

  public void hideAds() { 
    Log.e("LEX", "Hide Ads"); 
    mHandler.post(new Runnable() { 
      public void run() { 
        // Hide 
        _ad.setVisibility(View.INVISIBLE); 
        _ad.stopLoading(); 
      } 
    }); 
  } 

With this you can make callbacks from your C code into Java without those pesky crashes.

If you would like to try my game go to the Apple AppStore or to Google Play or to www.lazybrush.co.uk. Thanks again and I hope you have fun!

Advertisements
Comments
One Response to “Lexigon: From iPhone to Android (Pt.2)”
Trackbacks
Check out what others are saying...
  1. […] Part2 of this tutorial I will cover more differences between Obj-C and C++ which I found during the port. Share […]



Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: