Accessing the Android Compass through Unity 3D.

OK, let’s go ahead and get this out of the way. This post is not for beginners or the faint of heart. There is no way to access Android’s Magnetic Field Sensor (aka the compass) directly from Unity, so in order to do that we’re going to have to write a custom plugin. This is not an easy task, and I honestly don’t know how well I can explain it – so… stick with me and hopefully we’ll get through this together. (Note: At the time of this writing I’m using Unity 3.4 so if you’re a traveler from the distant future, I don’t know how much of this will still be valid)

Quick Overview of custom plug-ins:

Writing a custom plugin is very poorly documented, and anyone who does it pretty much has to figure it all out by themselves. For Android, here’s the process:

1. Write a custom Java jar file.
2. Write a C(++) library using JNI to access the jar file and compile it into a .so file using Android NDK
3. Import the DLL (.so file) into C# script inside unity and access all the magic.

Now, I’m not that great of a C developer, so I opted to do the following instead:

1. Write a custom Java jar file.
2. Use the AndroidJNI helper function embedded in Unity to access the jar file.

The latter method is the one we’re going to use today.

Step 1: Setting up your environment

Here is a list of things that you should already have installed on your computer:

– Java
– Android SDK

You should also have your version of Java’s bin directory in your path – that way you can call different java commands from anywhere.

After you’ve created a new Unity Project, you’ll need to create the following folders:

Plugins\Android\src\com\yourcompany\yourgamename

* The namespace here is incredibly important. It doesn’t matter what you call com.yourcompany.yourgamename, it just matters that we use it everywhere in this game and plugin. So with that in mind, in Unity go to File->Build Settings -> Player Settings and put your namespace in the Bundle Identifier field. Last note – Case totally matters.

Now that we’ve got everything set up it’s time for…

Step 2: Writing our Java plugin

In the Plugins\Android\src folder create a new file called CompassActivity.java. Open this with your favorite text editor.

Copy and paste the Java code below but know that you’re going to have to change the package to the namespace we created in step 1:

package com.yourcompany.yourgamename;

import com.unity3d.player.UnityPlayerActivity;

import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.util.Config;
import android.util.Log;
import android.app.Activity;

public class CompassActivity extends UnityPlayerActivity {
private static final String TAG = "Compass";

private SensorManager mSensorManager;
private Sensor mSensor;

static public float xmag;
static public float ymag;
static public float zmag;

private final SensorEventListener mListener = new SensorEventListener() {
public void onSensorChanged(SensorEvent event) {
if (Config.DEBUG) Log.d(TAG,
"sensorChanged (" + event.values[0] + ", " + event.values[1] + ", " + event.values[2] + ")");

xmag = event.values[0];
ymag = event.values[1];
zmag = event.values[2];
}

public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};

@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
}

@Override
protected void onResume()
{
if (Config.DEBUG) Log.d(TAG, "onResume");
super.onResume();

mSensorManager.registerListener(mListener, mSensor,
SensorManager.SENSOR_DELAY_GAME);
}

@Override
protected void onStop()
{
if (Config.DEBUG) Log.d(TAG, "onStop");
mSensorManager.unregisterListener(mListener);
super.onStop();
}

public static float getX() {
return xmag;
}

public static float getY() {
return ymag;
}

public static float getZ() {
return zmag;
}
}

Once you’ve changed the package namespace and saved the file, open a command prompt and cd to the src directory. We’re going to run the following java commands to compile, sign, and jar our plugin: (note: You may need to alter the classpath and bootclasspath to match your system)

1.> javac CompassActivity.java -classpath C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar -bootclasspath C:\android-sdk-windows\platforms\android-8\android.jar -d .

2.> javap -s com.yourcompany.yourgamename.CompassActivity

3.> jar cvfM ../Compass.jar com/

Once you’ve finished those three steps you should now have a file called Compass.jar in your Plugins\Android\ folder.

Step 3: Setting up our AndroidManifest.xml

The AndroidManifest.xml is very important. When your Unity App launches on the Android device, this file will tell it to load our .jar file instead of the UnityPlayer.jar file. Our jar file takes over because it extends the UnityPlayer Activity (which loads the UnityPlayer Activity). You’ll want to create the file AndroidManifest.xml in our \Plugins\Android folder. (Note: Don’t forget to change your package here as well)

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.yourcompany.yourgamename"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />

	<application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".CompassActivity"
                  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>

Step 4: Accessing the Plugin from Unity
At this point we could build our application and push it to our phone and everything would work correctly, but we haven’t pulled any information out of it. If you look at the Java code I’ve written three methods called GetX, GetY, and GetZ. We’re going to call those methods from Unity and get the output (which in turn will give us the X, Y, and Z values from the Magnetic Sensor).  For this example we’re going to output the values to the GUI. First, create a new C# file (it doesn’t matter what you call it). Here’s what we need to put in it: (don’t forget to change the namespace)

using UnityEngine;
using System.Collections;
using System;

public class CompassJNI : MonoBehaviour {
	static float 	xValue;
	static float	yValue;
	static float	zValue;

	// Use this for initialization
	void Start () {
		AndroidJNI.AttachCurrentThread();
	}

	void Update() {
		using (AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {

			using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic("currentActivity")) {

				AndroidJavaClass cls_CompassActivity = new AndroidJavaClass("com.yourcompany.yourgamename.CompassActivity");

				cls_CompassActivity.CallStatic("Init", obj_Activity);

				xValue = cls_CompassActivity.CallStatic("getX");
				yValue = cls_CompassActivity.CallStatic("getY");
				zValue = cls_CompassActivity.CallStatic("getZ");

			}
		}

		Debug.Log("Compass values are " + xValue.ToString() + "," + yValue.ToString() + "," + zValue.ToString());
	}

	void OnGUI() {
		GUI.Label(new Rect(Screen.width / 2 -200, Screen.height / 2, 400,100), "xmag = " + xValue.ToString() + " ymag = " + yValue.ToString() + " zmag = " + zValue.ToString());
	}
}

Attach this script to your camera and build your application. You should see the 3 Values change as you move your Android Device around.  And there you go – we’re now accessing the compass.

Hopefully these steps will give you a better understanding of how Unity custom plugins work. There’s a lot of information in here and plenty of places for things to screw up, so if you have any issues leave a comment and I’ll do my best to help.

If this tutorial helped you out, why don’t you go buy one of my video games =)

58 Responses to “Accessing the Android Compass through Unity 3D.”


  1. 1 BLiTZWiNG August 20, 2011 at 11:47 pm

    This is very close to what I had, but I still can’t get it working, granted I am using Eclipse with my source in a different folder. I managed to get it so the app doesn’t crash but something is still amiss because my values are all 0, yet the java app I write shows them all just fine.

    What is the importance of the Plugins/Android/com/company/app foider structure, ie. what files actually go in there?

    Is it important to have the source located in the plugin folder too?

    I’m a little concerned that you create and destroy the activity class every update frame. I can only imagine that this doesn’t allow anything to happen, because you create it, register the sensors, read the values and immediately destroy it before the system even gets a chance to fire the events.

    I’m not a java expert, I’ve only been doing a crash course in it for a couple of days now, so I’m afraid I have no idea what I’m doing wrong. Like most others, my attempts to get help from UT have gone unanswered.

  2. 3 Timothy Johnson August 21, 2011 at 3:37 am

    Hey Blitzwing, the CallStatic function can return anything actually. Sometimes you need to specify the return type, but in this case I didn’t need to. You can do this:

    xValue = cls_CompassActivity.CallStatic("getX");
    

    or

    xValue = cls_CompassActivity.CallStatic(float)("getX");
    

    *In the (float) above substitute the parenthesis for Greater Than and Less Than. For some reason wordpress isn’t letting me type that out correctly.
    As to your first question, the folder path is important when you’re compiling your .java file – all of the .class files are created in there. Later when you use the jar command it’ll put all of the .class files in your folder path into the jar file.

    I tried using Eclipse at first when doing this and I got all sorts of errors and ended up just doing it all manually like the steps I mentioned.

    Also, are you using logcat to get the logging off of your phone after you compile and start Unity? That’s how I found out all of my errors and worked my way from there. If you are getting errors, please tell me what they are and I’ll try to help.

    • 4 BLiTZWiNG August 21, 2011 at 3:45 am

      Thanks Timothy,

      I am aware of the generic versions of CallStatic yes, and that’s what I am using, I just wasn’t sure if that is what Joshua intended.

      I’m like a total n00b with java and I have no idea how to make a project without using Eclipse yet, but I had a go with putting code in the right folders, and the code is a pain to edit with wordpad too :/. I also downloaded Tapjoy plugin to see how they are doing it, and it mostly looks the same. When I put the manifest file in I got compile errors in Unity.

      I also notice Joshua talks about signing the jar (I’ve heard others say they needed to sign it to get it to work), but I don’t see any commands there to sign it.

      I’m not using logcat no, but I think I had better start.

      • 5 BLiTZWiNG August 21, 2011 at 4:42 am

        Seems the call to the constructor is failing

        I/Unity ( 7051): (Filename: /Applications/buildAgent/work/842f9557127e852/Runt
        ime/Export/Generated/UnityEngineDebug.cpp Line: 34)
        I/Unity ( 7051):
        E/Unity ( 7051): getMethodID(“Init”, “(Lcom.unity3d.player.UnityPlayerActivity
        ;)V”) FAILED!
        I/Unity ( 7051): JNI: Unable to find method id for ‘Init’ (static)
        I/Unity ( 7051): UnityEngine.Debug:Internal_Log(Int32, String, Object)

  3. 6 BLiTZWiNG August 21, 2011 at 7:21 am

    I’ve moved my eclipse project into Plugins/Android and I compile my .jar to the bin folder now. At best I can stop errors in logcat if I remove the call to the constructor, but of course, nothing happens then. Ive tried Init, init and but it never seems to be able to find a constructor.

  4. 7 BLiTZWiNG August 21, 2011 at 7:54 am

    I have succeeded in getting it working (with Eclipse!). I had to remove the call to the constructor, and use static variables in the java (as in the example). I tried to use instance variables.

    The main thing I am not happy with now is the garabage that will be generated by creating new Android interface objects every update.

  5. 8 BLiTZWiNG August 21, 2011 at 8:44 am

    I am noticing however that the app freezes after a few minutes, every time.

    I also switched out the using clauses and only created the objects once, but the error still occurs.

    I put the reading of the sensors in the onSensorChanged event in a synchronized block like I saw IBM do in their IBMEyes app, and that seems to have cured the problem.

  6. 9 Timothy Johnson August 23, 2011 at 7:53 pm

    So glad you got everything working BLiTZWiNG. The C# I posted was what I used when I first got everything working. I re-worked it to be much less processor intensive in my final project. Hope to see your project/game once you’ve completed it!

  7. 10 prefrontal cortex August 23, 2011 at 9:55 pm

    Hey, I wrote a complete android sensor library for Unity3d (in a quite similar aproach to yours) – it provides access to every single sensor android has to offer, but has many more advantages, like automatic fallback for less accurate sensors and so on. It also creates zero garbage :-). You can see it in action here: goo.gl/oLkOQ. This is the link to the forum thread about GyroDroid, available from the asset store: goo.gl/8ZIM7.

  8. 11 Trent August 23, 2011 at 11:35 pm

    Thanks Timothy, it was a massive help in getting me to where I am. Sadly now I have to wrestle with conversion of android axis to unity! I do pick the hard projects…

  9. 12 Lorenzo August 25, 2011 at 4:31 pm

    its not working for me..but thanks a lot for your tutorial!
    can you please post files?
    or just explain better the steps for compiling class-file?
    thanks

  10. 13 steffan September 6, 2011 at 12:37 pm

    +1 to upload files idea 🙂

  11. 14 Timothy Johnson September 8, 2011 at 2:14 am

    I guess I could post the files, but they would be exactly what I have posted above. To compile class files you have to run the java commands posted above

    – javac is the command you use to turn a .java file into the compiled class files

    – javap -s prints out the type signatures

    – jar cvfM compresses all of the .class files into a .jar file.

    If you guys are still confused I’ll see what I can do. This is all pretty much Java 101 stuff, so perhaps some Java tutorials might help you out.

    • 15 Samuel September 11, 2011 at 4:52 pm

      Hey, I really appreciate the tutorial.

      When I run the above scripts on my phone, it crashes immediately without an error. By trial and error I determined that the crash is caused by the following line:

      new AndroidJavaClass(“com.company.game.CompassActivity”);

      I’ve taken this to mean that the java class file was not found in the jar file. I had to change the directories for the javac, javap, and jar cvfm commands, but the jar was created without errors and all my packaging seems to be correct. I’ve tried several times without any success.

      So, I took a step backwards and downloaded the JavaScriptExample unity file from the unity plugin official page. (there is a rar you can download that has source code very similar to that from this tutorial.) Using their .jar file the program runs just fine. Using a jar file I generate using –their .class files and directors– the program crashes on my phone.

      So my problem is definitely caused by the jar creation process. Here are my guesses as to why;

      – I do not have the Android NDK installed, only the SDK
      – Your javac command usies the “android-8” platform, which I changed arbitrarily to 9 because my SDK install does not include 8.

      Any help would be greatly appreciated.

  12. 17 Samuel September 12, 2011 at 9:33 pm

    Yes,

    The App runs correctly when using the .class included in the plugin example source code. It is only when using a .class file that I compiled myself that I encounter a problem. I’m using a .java file that was included, and the package is correct.

    The following are the lines I use to create the .class and .jar files. The namespace and class are changed to reflect that of the plugin example, and not the ones from this tutorial.

    >javac JavaClass.java -classpath D:\Programs\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar -bootclasspath D:\Programs\AndroidSDK\platforms\android-8\android.jar -d .

    >javap -s org.example.ScriptBridge.JavaClass

    >jar cvfM ../JavaClass.jar org/

    The .jar created using the INCLUDED .class file works just fine. So the problem is caused by something in the first two lines. My only guess is that the androidSDK I have installed is corrupt.

    Thanks for the reply

  13. 18 Samuel September 15, 2011 at 3:02 am

    Alright, I got it working! I’ll try to summarize the problems I found.

    1.) Every “GetStatic” method needs to have a return type specified. For example;

    AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic(“currentActivity”)

    should be replaced by

    AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic(“currentActivity”)

    2.) One line in the manifest file will cause a “repackage” error that is caught by unity. This is fixed by changing the following line;

    to

    3.) If your app crashes with no error when running on the actual phone, it could be because you are trying to use the newest version of java. You need to be using jdk1.6.0_27 and jre. This tripped me up for a while.

    Hope this helps

  14. 20 Ina Infinity October 30, 2011 at 9:01 am

    Okay so Unity can pull data from the Android Java side… but how do you send data from Android Java side to Unity?

  15. 22 Ina Infinity November 5, 2011 at 6:42 am

    Got this to work… but am hzing Blitzwing’s problem of the app freezing after about 10-20 seconds…

    11-04 23:39:01.455: D/WindowManager(23630): call unlinkToDeath, this=Window{410c2e58 Starting com.company.app paused=false}

    • 23 Ina Infinity November 5, 2011 at 10:09 am

      Figured it out… app not freezing anymore! 🙂 – apparently AndroidJavaClass variable should be a global, newe AndroidJavaClass called once in Start(), and not called in Update (though its value may be updated!)

  16. 24 BLiTZWiNG November 5, 2011 at 10:40 am

    Er yes I should have mentioned that…

  17. 25 Stephen Zhou November 15, 2011 at 4:41 am

    Hi. Thank you for telling us how to DIY plugins.
    But I don’t know how to combine more than one plugins. For example, I downloaded two or three android plugins but the Manifest.xml will be overwritten if put all of them together. Could you teach me how? Thank you!

    • 26 Timothy Johnson November 15, 2011 at 4:45 pm

      That sounds like a good focus for a separate blog post. As soon as I get some time I’ll get an example up…

      • 27 Daniela January 10, 2013 at 12:23 pm

        Any ideas on how to get this to work? Otherwise a great tutorial – thanks 🙂

      • 28 Timothy Johnson January 10, 2013 at 12:48 pm

        I haven’t touched this code in a while so I’m not sure if it still works on the latest version of Unity. Are you getting an error?

      • 29 Daniela January 10, 2013 at 4:37 pm

        I think I got the Manifest OK, which was my original problem. I just included my new activity with a fully qualified name (and kept the original package as the qualcomme one).

        I’m actually not using your CompassActivity, but rather trying to use the Android Gallery in a plugin. I’m not getting any errors, but the app doesn’t seem to do anything. Also it’s kind of hard to debug, because I can’t check if pictures are saved while the device is plugged in (which I need it to be to use the adb, if I’m not mistaken). Also sorry for the unrelated spam – feel free to delete my spam comment 😀

  18. 30 Leo November 26, 2011 at 3:44 am

    Can unity set a value and send to jar, then jar file send that value back to unity?

  19. 32 sriram90 January 16, 2012 at 11:14 am

    Hello,

    The tutorial is good. but i couldn’t get the answer yet.

    you said that, after created CompassActivity.java and drag into Assets->Plugins->Android->src->CompassActivity.java

    after this step in command prompt need to set path of project directory. isn’t ? now i got this error. can u say where am missing the step?

    and i set bundle identifier correctly.

    AndroidPluginJan16-Me sriram$ javac CompassActivity.java -classpath /Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/bin/classes.jar -bootclasspath /Users/sriram/Android/android-sdk-macosx/platforms/android-14/android.jar -d
    javac: file not found: CompassActivity.java
    Usage: javac
    use -help for a list of possible options

    am using mac os, unity 3.4.0.

  20. 33 Alvaro March 2, 2012 at 8:48 am

    Hello, I have been trying to make this works with some version updates:

    – Unity 3.5
    – Android-15
    – Java jdk1.7.0_03 jre7

    Here are my findings so far:

    I needed to substitute the line:

    using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic(“currentActivity”)) {

    with:

    using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic<AndroidJavaObject>(“currentActivity”))

    It also seems that the name of the cs does matter now. I needed to rename it as the name of the class: “CompassJNI”.

    I get the APK, but it breaks as soon as it starts. I have read that it may be caused because of the java version and I shoud use a lower one, but I would like to have it running with the current version.

    Can anyone give me a hand with this?

    Thank you very much in advance.

    • 34 floatingcoder March 21, 2012 at 6:21 am

      WOOT! I got it working! Heres is what I found I had to do…

      1. Insure the Plugins folder is in the Assets directory, as it will be if you create them through the Unity interface. (I created them through explorer, and put them in the base project folder… 😦 )

      2. As already mentioned, add the return types to the calls in the unity c# file. As that has been discussed, I won;t give the details here, as word press apparently likes to eat lines with angle brackets in them.

      3. Use Java 1.6.0_27. It’s possible others would work, but I tried 7 after I got it working, and it broke it again, so I went back to 1.6.0U27.

      4. In the android manifest file, change drawable/icon to drawable/app_icon (line 8ish) I think this is what Samuel was saying before, but his lines got eaten. This must be due to a change in the newer versions of unity.

      5. Lastly, in CompassActivity.java I had to comment out the if (Config.DEBUG) line in the onSensorChanged method (line 12ish). No idea why, but that was the final fix for me.

      Thanks again for the tutorial! Its a bummer things keep changing to break it, but I learned a lot! 🙂 Very happy, and yes, Ill definitely be buying your games in support!

      • 35 Timothy Johnson March 24, 2012 at 6:03 pm

        Glad to hear you got it working! Unity and Java both are in a constant state of change/update so it’s difficult to keep this article current and 100% correct. Thanks for adding how you got it working!

      • 36 Alvaro April 3, 2012 at 8:51 am

        Hello!

        Thank you very much, floatingcoder, I finally got it running!

        My problem now is that I always get 0 values for x, y and z.

        Any ideas about what can be happening?

        Thank you very much in advance!

  21. 37 Alvaro April 3, 2012 at 10:18 am

    Ok. Now I am really confused. After a lot of trials I have ended with just this code:

    public class CompassActivity extends UnityPlayerActivity
    {
    private static final String TAG = “Compass”;

    static public float xmag = 2;

    @Override
    protected void onCreate(Bundle icicle)
    {
    super.onCreate(icicle);
    xmag = 3;
    }

    @Override
    protected void onResume()
    {
    super.onResume();
    }

    @Override
    protected void onStop()
    {
    super.onStop();
    }

    public static float getX()
    {
    return xmag;
    }
    }

    When I call the getX method from Unity, I find out that ¡¡¡Xmag=2!!!. I don’t know why but it is not even calling the oncreate method.

    Any help would be very appreciated.

  22. 38 unityplayer May 18, 2012 at 1:13 pm

    great tutorial! its the best one on the interne so far!! it helped alot!! thanx!!

  23. 39 jlodom40774 August 16, 2012 at 4:40 pm

    Hey, for some reason the code is cut off for me on step 2 and 4. Is there another website that I could view this tutorial or is there a way that you could email it to me?

    • 40 Timothy Johnson August 16, 2012 at 5:04 pm

      Odd… Here you go!

      Step 2: Writing our Java plugin

      In the Plugins\Android\src folder create a new file called CompassActivity.java. Open this with your favorite text editor.

      Copy and paste the Java code below but know that you’re going to have to change the package to the namespace we created in step 1:

      package com.yourcompany.yourgamename;

      import com.unity3d.player.UnityPlayerActivity;

      import android.content.Context;
      import android.hardware.Sensor;
      import android.hardware.SensorEvent;
      import android.hardware.SensorEventListener;
      import android.hardware.SensorManager;
      import android.os.Bundle;
      import android.util.Config;
      import android.util.Log;
      import android.app.Activity;

      public class CompassActivity extends UnityPlayerActivity {
      private static final String TAG = “Compass”;

      private SensorManager mSensorManager;
      private Sensor mSensor;

      static public float xmag;
      static public float ymag;
      static public float zmag;

      private final SensorEventListener mListener = new SensorEventListener() {
      public void onSensorChanged(SensorEvent event) {
      if (Config.DEBUG) Log.d(TAG,
      “sensorChanged (” + event.values[0] + “, ” + event.values[1] + “, ” + event.values[2] + “)”);

      xmag = event.values[0];
      ymag = event.values[1];
      zmag = event.values[2];
      }

      public void onAccuracyChanged(Sensor sensor, int accuracy) {
      }
      };

      @Override
      protected void onCreate(Bundle icicle) {
      super.onCreate(icicle);
      mSensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
      mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
      }

      @Override
      protected void onResume()
      {
      if (Config.DEBUG) Log.d(TAG, “onResume”);
      super.onResume();

      mSensorManager.registerListener(mListener, mSensor,
      SensorManager.SENSOR_DELAY_GAME);
      }

      @Override
      protected void onStop()
      {
      if (Config.DEBUG) Log.d(TAG, “onStop”);
      mSensorManager.unregisterListener(mListener);
      super.onStop();
      }

      public static float getX() {
      return xmag;
      }

      public static float getY() {
      return ymag;
      }

      public static float getZ() {
      return zmag;
      }
      }

      Once you’ve changed the package namespace and saved the file, open a command prompt and cd to the src directory. We’re going to run the following java commands to compile, sign, and jar our plugin: (note: You may need to alter the classpath and bootclasspath to match your system)

      1.> javac CompassActivity.java -classpath C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar -bootclasspath C:\android-sdk-windows\platforms\android-8\android.jar -d .

      2.> javap -s com.yourcompany.yourgamename.CompassActivity

      3.> jar cvfM ../Compass.jar com/

      Once you’ve finished those three steps you should now have a file called Compass.jar in your Plugins\Android\ folder.

      Step 3: Setting up our AndroidManifest.xml

      The AndroidManifest.xml is very important. When your Unity App launches on the Android device, this file will tell it to load our .jar file instead of the UnityPlayer.jar file. Our jar file takes over because it extends the UnityPlayer Activity (which loads the UnityPlayer Activity). You’ll want to create the file AndroidManifest.xml in our \Plugins\Android folder. (Note: Don’t forget to change your package here as well)

      Step 4: Accessing the Plugin from Unity
      At this point we could build our application and push it to our phone and everything would work correctly, but we haven’t pulled any information out of it. If you look at the Java code I’ve written three methods called GetX, GetY, and GetZ. We’re going to call those methods from Unity and get the output (which in turn will give us the X, Y, and Z values from the Magnetic Sensor). For this example we’re going to output the values to the GUI. First, create a new C# file (it doesn’t matter what you call it). Here’s what we need to put in it: (don’t forget to change the namespace)

      using UnityEngine;
      using System.Collections;
      using System;

      public class CompassJNI : MonoBehaviour {
      static float xValue;
      static float yValue;
      static float zValue;

      // Use this for initialization
      void Start () {
      AndroidJNI.AttachCurrentThread();
      }

      void Update() {
      using (AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass(“com.unity3d.player.UnityPlayer”)) {

      using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic(“currentActivity”)) {

      AndroidJavaClass cls_CompassActivity = new AndroidJavaClass(“com.yourcompany.yourgamename.CompassActivity”);

      cls_CompassActivity.CallStatic(“Init”, obj_Activity);

      xValue = cls_CompassActivity.CallStatic(“getX”);
      yValue = cls_CompassActivity.CallStatic(“getY”);
      zValue = cls_CompassActivity.CallStatic(“getZ”);

      }
      }

      Debug.Log(“Compass values are ” + xValue.ToString() + “,” + yValue.ToString() + “,” + zValue.ToString());
      }

      void OnGUI() {
      GUI.Label(new Rect(Screen.width / 2 -200, Screen.height / 2, 400,100), “xmag = ” + xValue.ToString() + ” ymag = ” + yValue.ToString() + ” zmag = ” + zValue.ToString());
      }
      }

  24. 41 Ghost August 31, 2012 at 6:54 pm

    Hey all, seriously… these posts that you all made was great!
    But as everybody here… i had my problems too.
    So reading and re-reading all posts again and again… i finally got my own stuff working great! (hours after… sleep time lost… :P)

    I’m using:
    – Unity 3.5.5
    – Android-15
    – Java jdk1.6.0_17

    So, here goes my solution to get this working:

    1º – Do all the things as Timothy Johnson said at the beggining (using the last CompassActivity.java posted above).
    -Remember to put Java’s Path in system enviroment Path.
    -Put the “Pluging” folder inside the “Assets” folder, but i left a copy outside too, on the project folder.

    2º – As floatingcoder said: In the android manifest file, change drawable/icon to drawable/app_icon (line 8ish) I think this is what Samuel was saying before, but his lines got eaten. This must be due to a change in the newer versions of unity.

    3º – To use the Pluging and not crash, declare globals and iniciate them on Start. The values keeps updating still.

    4º – Declare types, with “greater then” and “less then”, like in example below (which i used “[” “]” in place of “greater and lesser then”):

    using UnityEngine;
    using System.Collections;
    using System;

    public class ViewControl : MonoBehaviour {

    private static float CompassX;
    private static float CompassY;
    private static float CompassZ;
    private AndroidJavaClass cls_UnityPlayer;
    private AndroidJavaObject obj_Activity;
    private AndroidJavaClass cls_CompassActivity;

    void Update ()
    {
    CompassX = cls_CompassActivity.CallStatic[float](“getX”);
    CompassY = cls_CompassActivity.CallStatic[float](“getY”);
    CompassZ = cls_CompassActivity.CallStatic[float](“getZ”);

    Debug.Log(“Compass values are ” + CompassX.ToString() + “,” + CompassY.ToString() + “,” + CompassZ.ToString());
    }

    void Start ()
    {
    AndroidJNI.AttachCurrentThread();
    cls_UnityPlayer = new AndroidJavaClass(“com.unity3d.player.UnityPlayer”);
    obj_Activity = cls_UnityPlayer.GetStatic[AndroidJavaObject](“currentActivity”);
    cls_CompassActivity = new AndroidJavaClass(“com.Seiva.WalkThrough.CompassActivity”);
    cls_CompassActivity.CallStatic(“Init”, obj_Activity);
    }
    }

    5º – Finally, remember old school times: http://en.wikipedia.org/wiki/Euler_angles

    • 42 Ghost August 31, 2012 at 7:02 pm

      Iniciate = Initiate (portuguese-english messing up with me :D)
      “com.Seiva.WalkThrough.CompassActivity” is my Company.GameName, remeber to change to yours….
      Hope this help you guys!
      Cya

      • 43 Ghost August 31, 2012 at 7:40 pm

        Only one problem now…
        When i turn my phone to horizontal (rotate the screen), the game crashes….
        Anyone having the same? any fix ?
        Ty

  25. 44 Romain October 4, 2012 at 5:40 pm

    Hi,
    I just wrote a article on my website:
    http://romainpedra.fr/?p=149
    It’s in french(yes i have translated your article but i made some code modifications on the Unity part) but i put link to your website and your games.

  26. 47 Romain October 12, 2012 at 1:09 pm

    Hi, I updated my Unity to 3.5.6f4 and i have now an error on each frame:
    10-12 13:57:18.731: E/Unity(2099): getMethodID(“Init”, “(Lcom.mycompany.mygame.CompassActivity;)V”) FAILED!
    Do you know what is the problem?

  27. 48 Arslan khan October 16, 2012 at 1:25 pm

    y are you using cvfm it requires manifest file too where should i get it

  28. 49 Arslan khan October 16, 2012 at 2:43 pm

    hi, forget about my previous post i resolved the issue but its still not working its

    i am using this code right now
    using UnityEngine;
    using System.Collections;
    using System;

    public class CompassJNI : MonoBehaviour {
    static float xValue;
    static float yValue;
    static float zValue;

    // Use this for initialization
    void Start () {
    AndroidJNI.AttachCurrentThread();
    using (AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass(“com.unity3d.player.UnityPlayer”)) {

    using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic(“currentActivity”)) {

    AndroidJavaClass cls_CompassActivity = new AndroidJavaClass(“com.Mango.BurkaMadness.CompassActivity”);

    /*cls_CompassActivity.CallStatic(“Init”, obj_Activity);

    xValue = cls_CompassActivity.CallStatic(“getX”);
    yValue = cls_CompassActivity.CallStatic(“getY”);
    zValue = cls_CompassActivity.CallStatic(“getZ”);*/

    }
    }
    }

    void Update() {

    Debug.Log(“Compass values are ” + xValue.ToString() + “,” + yValue.ToString() + “,” + zValue.ToString());
    }

    void OnGUI() {
    GUI.Label(new Rect(Screen.width / 2 -200, Screen.height / 2, 400,100), “xmag = ” + xValue.ToString() + ” ymag = ” + yValue.ToString() + ” zmag = ” + zValue.ToString());
    }
    }

    but its giving me an error in logcat

    FATAL EXCEPTION: GLThread 10
    java.lang.ClassNotFoundException: com.Mango.BurkaMadness.CompassActivity
    at java.lang.Class.classForName(Native Method)
    at java.lang.Class.forName(Class.java:234)
    at java.lang.Class.forName(Class.java:181)
    at com.unity3d.player.UnityPlayer.nativeRender(Native Method)
    at com.unity3d.player.UnityPlayer.onDrawFrame(Unknown Source)
    at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1363)
    at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1118)
    Caused by: java.lang.NoClassDefFoundError: com.Mango.BurkaMadness.CompassActivity
    … 7 more
    Caused by: java.lang.ClassNotFoundException: com.Mango.BurkaMadness.CompassActivity in loader dalvik.system.PathClassLoader[/mnt/asec/com.Mango.BurkaMadness-1/pkg.apk]
    at dalvik.system.PathClassLoader.findClass(PathClassLoader.java:240)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:551)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
    … 7 more

    as you can see my package name is correct i have crossed check it in java file and in player settings its same
    thanks

    • 50 Arslan khan October 16, 2012 at 2:49 pm

      forget about this too i created a folder in unity editor by the name of assets instead of it i should have moved the plugins folder to assets folder in directory structure in the first place.

      Thanks for the great article its really helpfull

  29. 51 TiMuS December 23, 2012 at 5:36 pm

    After completing Step3 i tried to build my app but received and error. 😦

    Failed to re-package resources with the following parameters:
    package -v -f -m -J gen -M AndroidManifest.xml -S “res” -I “C:/Users/sumit/Documents/android-sdk-windows/platforms/android-16\android.jar” -F bin/resources.ap_

    I followed each step correctly.. Anyone having same issues…

  30. 52 TiMuS December 25, 2012 at 7:16 pm

    Hey Thanks ……….Got it working……
    Is there any way to build this plugin on latest JDK and android version….
    Thanks again.. 🙂

  31. 55 Imran Khalil July 4, 2013 at 5:54 am

    i just want to check how it works, and I have done all and working fine only if i first export my project to eclipse and then build. Can anybody explain why this is so?

  32. 56 park.heesoo July 9, 2013 at 2:12 am

    D:\UTIL\Unity3d\Grow\Unity\Assets\Plugins\Android\src>javac CompassActivity.java
    -classpath C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplay
    er\bin\classes.jar -bootclasspath D:\adt-bundle\sdk\platforms\android-17\android
    .jar -d .
    javac: invalid flag: (x86)\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\c
    lasses.jar
    Usage: javac
    use -help for a list of possible options

    –>
    D:\UTIL\Unity3d\Grow\Unity\Assets\Plugins\Android\src>javac CompassActivity.java
    -classpath “C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplay
    er\bin\classes.jar” -bootclasspath D:\adt-bundle\sdk\platforms\android-17\android
    .jar -d .

    i attached “C:\Program Files ~~~~”

    wow im so silly …
    have a nice day~~ ^^ Thanx

  33. 57 Louis August 11, 2013 at 12:51 pm

    Hi great tutorial,

    i’ve done your steps without compiling errors or issues, but on the eclipse emulator it crashes at the start (on my phone too) and logcat says:

    unable to instantiate activity componentinfo {…}: java.lang.classnotfoundexception

    Where could be the mistake?

  34. 58 Jason December 12, 2014 at 3:32 pm

    GREAT tutorial~~ I’ve tried it and it really works, but there are some tips if you want to run it under Unity 4.x
    1. I guess the code was designed for Unity 3.x when the classes.jar still work. For Unity 4.x, you have to find a classes.jar from Unity 3.x and use that one to compile.
    2. In Unity 4.x, the GetStatic should have return type specified, GetStatic()


Leave a reply to BLiTZWiNG Cancel reply