FacebookTwitterGoogle+Share

Modifying NME

So as you know, I recently released an Android game. (It now has like 10 downloads, just saying.. #prettyproudofthat)

Javi got a chance to play it, and kindly offered to make some background music. Ten short minutes later, I had an MP3.

The music began by fading in, after which it could be looped as long as playback started at 5336ms on each subsequent play. Looking at the API documentation for nme.media.Sound, and nme.media.SoundChannel, this seemed fine. Turns out, that was not the case. But Javi had made this music for me, so what choice did I have but to make it work?

In case I decide to make further changes to NME, this is how to do it:

tools/command-line/android/template/src/org/haxe/nme/GameActivity.java defines a nice class which extends android.app.Activity and exposes a number of static functions which are convenient for access via the JNI.

Since music playback for android is handled in Java, We’ll start by editing that. (If you’re wondering how I found that it’s handled in java, basically I went through this process backwards.)

In the first version I wanted to add vibration to my android game, but decided that, at that point, it wasn’t worth the effort. Since I was doing this stuff for music anyway, It was a good time to implement vibration as well.

Here’s a simple example of adding to GameActivity.java


static public void vibrate(int duration) {
	Vibrator v = (Vibrator)activity.getSystemService(Context.VIBRATOR_SERVICE);
	v.vibrate(duration);
}

For the music changes, I modified some existing functions as well as making a new one.

You may have noticed some calls in the GameActivity.java code that look like this:

Log.v("GameActivity","stop MediaPlayer");

This sort of call is super useful for testing! You can use a program from command-line (found in Android SDK/platform-tools) to show you that output as it occurs.

In this case, I use the call adb logcat GameActivity *:s, which only shows entries tagged with GameActivity.

So now let’s assume the Java is handled.

In some situations, like many of the music changes, it won’t be necessary to explicitly call the Java functions — because they are already being called. In others (like for vibration) it’s necessary to get NME to call them, which is often done with C++.

In project/android/System.cpp you can add a function that calls a static function in GameActivity.java like this:

void HapticVibrate (int duration) {
	JNIEnv *env = GetEnv();
	jclass cls = env->FindClass("org/haxe/nme/GameActivity");
	jmethodID mid = env->GetStaticMethodID(cls, "vibrate", "(I)V");
	if (mid > 0)
		env->CallStaticVoidMethod(cls, mid, duration);
}

So we are finding the class, and the appropriate method in the class, and then just calling it.

The “(I)V” argument defines the parameters the method takes, and its return type. You can find out more about these in project/android/JNI.cpp, specifically in the JNIParseType function. If you are dealing with an object like a string, its entry looks like this Ljava/lang/String;.

Now that it’s in System.cpp, we want to be able to call it from Haxe.

This requires editting project/common/ExternalInterface.cpp, as well as project/include/Utils.h.

In ExternalInterface.cpp, we add the method we will be calling from haxe. In this case, it just calls the method in System.cpp.

void nme_vibrate(value inDuration)
{
	#if defined(ANDROID)
	HapticVibrate (val_int(inDuration));
	#endif
}
DEFINE_PRIM(nme_vibrate,2);

DEFINE_PRIM apparently let’s you access the native method from haxe.

In Utils.h, we just add the method signature:

void HapticVibrate(int period, int duration);

When I’m programming, I like to see output to make sure things are happening as and when I expect them to. In haxe, this is made easy with a simple trace call. In Java, we can use Log.e, Log.v, Log.d, or Log.i. But what about C++?

Well, it turns out that at the top of some of these files are helpful defines:


#define LOGV(msg,args...) __android_log_print(ANDROID_LOG_ERROR, "NME::System", msg, ## args)
#define LOGE(msg,args...) __android_log_print(ANDROID_LOG_ERROR, "NME::System", msg, ## args)

Which we can make use of in our code and watch for with just a few small changes to our logcat call.

LOGV("Hihi");

So. Now that we’ve changed the C++ code, we have to compile it. This was the part I was stuck on for the longest time.

To compile for android, at command line first navigate to the project directory of your NME install. Once there, you should be able to run this command: haxelib run hxcpp Build.xml -Dandroid

Bad news, it may not work1. Could be that you need to update your Android NDK, I had to do that today. But most importantly, you need sdl-static two directories back (in my case it’s in nme/ because i’m currently in nme/3,3,0/projects).

Once it’s compiling correctly, It’s just a matter of calling the C++ method from haxe. You can gain access to this function using Loader.load

private static var myfunction = Loader.load ("nme_vibrate", 1);
...
myfunction( 100 );

The numeric argument (in this case 1) is the number of arguments the function expects.

And that’s all!

Some other things I wanted to note for future reference:

1. Generated C++ files for your project seem to appear in bin/android/obj/src

2. To use your own android manifest, instead of the one at tools/command-line/android/template/AndroidManifest.xml, you can copy that file into your project, make any changes necessary, and add this line to your project’s nmml file:

<template path="AndroidManifest.xml" if="android" />

3. As it says in spec.nmml (though I wish it was on the nmml spec page)

When pointing to a directory, you can use the include or exclude attributes to specify patterns for including files automatically. Wildcards are supported. To include all the files under the directory, for example, use an include value of “*”. You can separate multiple patterns using “|” characters.

4. versionCode and versionName seem to be hard-coded into nme’s AndroidManifest template. I overwrote it with my own so I could update versions (which was required for updates on google play).

5. I made a simple NME extension example, which manually loops music through calls to Java (Android). Which might be helpful for anyone looking to get started with that sort of NME extension?

 

Comments

You must be logged in to post a comment.