teaching machines

Custom Activity in Unity

June 26, 2012 by . Filed under public, reader animated.

Goal: I want to trigger Android activity-level code in my Unity 3D application. Here’s a minimal example of doing so:

  1. Create a new Android project in Eclipse. We won’t be using many of the ADT plugin features. We mostly leverage the compiler and editor.
  2. Override UnityPlayerActivity with a custom activity:
    package org.twodee.sitstill;
    
    import android.os.Bundle;
    import android.widget.Toast;
    
    import com.unity3d.player.UnityPlayerActivity;
    
    public class CustomActivity extends UnityPlayerActivity {
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Toast.makeText(this, "onCreate", Toast.LENGTH_LONG).show();
      }
    }
  3. Add C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar to the classpath. I did this in Eclipse via Project properties, Java Build Path, Libraries, Add External JARs.
  4. Export a JAR file containing the bytecode for the custom activity.
  5. Add the JAR file to the assets of the Unity project in folder Plugins/Android.
  6. Add a custom AndroidManifest.xml to point to your custom main activity:
    <?xml version="1.0" encoding="utf-8"?>
    <manifest
      xmlns:android="http://schemas.android.com/apk/res/android"
      package="org.twodee.sitstill">
      <application
        android:icon="@drawable/app_icon"
        android:label="@string/app_name">
        <activity
          android:name=".CustomActivity"
          android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen"
          android:label="@string/app_name">
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
        </activity>
      </application>
    </manifest>

When you build and run, your activity will get loaded instead of the Unity default.

Triggering Custom Activity Methods

Now suppose you have a custom method in your activity that you want called on some Unity event. Suppose this is your method:

public void startListening() {
  Toast.makeText(CustomActivity.this, "starting to listen", Toast.LENGTH_SHORT).show();
}

Let me be the first to warn you that this solution won’t work. Unity will call this from its own thread, not the UI thread under which activity code should generally be run. We can force execution on the UI thread with this approach:

public void startListening() {
  runOnUiThread(new Runnable() {
    @Override
    public void run() {
      Toast.makeText(CustomActivity.this, "starting to listen", Toast.LENGTH_SHORT).show();
    }
  });
}

On the Unity side, where we make the call, we’ve got to employ the JNI helper functions to get the current activity, which is stored as a static field in the UnityPlayer class. We can invoke methods on that current activity. We do so in a C# script with something like:

AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); 
AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); 
jo.Call("startListening");

Good luck.