Recently, I was trying to write some native Android plugins for Unity 3D and I was looking for a tutorial but I couldn’t find a complete one so I decided to write one. This tutorial will explain how to write a native Android plugin for Unity 3D from scratch, A to Z. We will be using Java as our native language and creating a JAR file which we will later be referred from Unity 3D using a C# script. First of all the thing you have to understand is that it s very easy to write this kind of plugin. The tutorial will be incremental and if followed carefully you will be able to see great results.
Setup Directory Structure
To develop this plugin I will be using Eclipse for Java/Optionally Android Studio also could be used, Mono for C# and Unity 3D. I personally prefer Eclipse because you can export a JAR file right off the UI of Eclipse.
Directory Structure in Unity 3D
Create a new Project in Unity with the following directories among other directories in the project.
1. Project Root -> Assets -> Plugins -> Android
2. Project Root -> Scripts
Eclipse Project
Create an Android Project with no Activity we will name it NativeUnity for the sake of this tutorial.Now in the src directory of the project you can create a package named com.nativeplugin.unity and create a basic java class called Plugin.
package com.nativeplugin.unity;
public class Plugin
{
}
We will be using this class and adding onto it as we move forward. You can write click on the project and choose export. Select JAR and choose the folder it needs to be exported into. You will have to place it inside the Project Root -> Assets -> Plugins -> Android Directory. Unity will automatically pick this file up and add it to the Android APK we are building.
C# Script
In Unity navigate to the Scripts folder in the Project and add a new C# class. We will name it NativePlugin for the sake of the tutorial. We will be using this script and adding on to it as we progress through this tutorial. We need to then add this script as a component to our project. You can add it to the main camera. Click on the main camera and then on the right pane click add components and add this new script that was created to the main camera.
You can not build this project and check if the apk is getting created. You will have to have a device attached to you computer as native pre will work on only devices not even on emulators for both Andorid and IOS. if everything goes well you’ll be able to see the new apk being started on your Android device. If you have started this tutorial from scratch and you have not worked on Unity 3d before, you will have to set the APK distribution ID on Unity 3D.
1. Hello, World! – Writing the most basic plugin to print a hello world log
This is the first function we are going to write in our plugin and the most basic one. We will create a log function in JAVA which will print a debug log to the logcat. Let’s go ahead and create this function in our Java class.
package com.nativeplugin.unity; import android.util.Log; public class Plugin { /** * Prints a debug message to the android Log Cat */ public static void logDebug() { Log.d("NativeUnity", "Hello, World!"); } }
This function when invoked should print Hello, World! on the Android Log Cat. Lets go ahead now and create the C# function that will make this call to native android. We will write our function to call Android in the Start() method which is called automatically by Unity 3D.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NativePlugin : MonoBehaviour { // Use this for initialization void Start () { // Create a reference in C# to the native class in Java AndroidJavaObject nativePlugin = new AndroidJavaClass("com.nativeplugin.unity.Plugin"); // Call the method in Java from C# nativePlugin.CallStatic ("logDebug"); } }
Go ahead and build and run the project to check if the Android function is being called. If you see the log Hello, World! in the logcat viewer you have completed this step successfully. Let’s see what actually is happening. Unity executes the Start function automatically when it starts it’s engine. Our pre first creates a reference to the Java class that we have. Notice that our function is a static function and thus doesn’t need a instance of the class to be created for it to be called. There exists away to call a method after a class has been instantiated which will be discussed later in this tutorial. Once the reference to the class is create in C# the reference can be used to call the function in Java statically. We can use the CallStatic method with the one parameter which is the name of the function in Java. You have successfully written your first plugin you can have a chocolate now.
2. Message from C# – Passing a message from C# to Android and printing it as a log
Let’s move on to the next step to see how we can pass arguments to the Java function we created. We wil add a new function to take two arguments, a tag and a message. We will now be able to pass two strings from C# to Java and then use those string to print out a log message in the logcat. We will first add a method to our Java class to take two arguments.
package com.nativeplugin.unity; import android.util.Log; public class Plugin { /** * Prints a debug message to the android Log Cat */ public static void logDebug() { Log.d("NativeUnity", "Hello, World!"); } /** * Prints a debug message to the android Log Cat * @param tag * @param message */ public static void logDebug(String tag, String message) { Log.d(tag, message); Log.d(tag, message); } }
Now we will update our method call in C# to pass two String arguments to Java when this function is called.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NativePlugins : MonoBehaviour { // Use this for initialization void Start () { // Create a reference in C# to the native class in Java AndroidJavaObject nativePlugin = new AndroidJavaClass("com.nativeplugin.unity.Plugin"); // Call the method in Java from C# with two arguments nativePlugin.CallStatic ("logDebug", new string[] {"NativeUnity", "I am an argument from C#!"}); } }
You can now go ahead and build and run your project. Look out for our log in the logcat. If you see it you have successfully managed to pass two arguments from C# to Android. YAY! If you look at the C# code you will see we are passing an array of string when the CallStatic function is called in the above code.
3. Static Calls VS Instance Calls
In our previous examples we have been using a static context to call the Java class. We are now going to see how we can invoke the same method using a instance of the Java class created in C#. We will keep our Java method the same but remove th static keyword so that it can be access from an instance.
package com.nativeplugin.unity; import android.util.Log; public class Plugin { /** * Prints a debug message to the android Log Cat */ public static void logDebug() { Log.d("NativeUnity", "Hello, World!"); } /** * Prints a debug message to the android Log Cat * @param tag * @param message */ public void logDebug(String tag, String message) { Log.d(tag, message); } }
Okay, as shown in the code above you can see that the only difference is that this method doesn’t have the static keyword. Although it will still work even with the static keyword we are removing it eliminate any confusion. Let’s look at how we can call the same method from C# but through an instance of the Plugin class rather from a static context.
using System.Collections; using System.Collections; using System.Collections.Generic; using UnityEngine; public class NativePlugins : MonoBehaviour { // Use this for initialization void Start () { // Create a reference in C# to the native class in Java AndroidJavaObject nativePlugin = new AndroidJavaObject("com.nativeplugin.unity.Plugin"); // Call the method in Java from C# with two arguments nativePlugin.Call ("logDebug", new string[] {"NativeUnity", "I am an argument from C#!"}); } }
Now, notice here that instead of AndroidJavaClass we are using AndroidJavaObject which will create an instance of the class. With the instance created we can treat the nativePlugin variable as instance of the Plugin class in java and call the logDebug function. Once you build and run this update you will be able to see the new debug log that we have set in your logcat monitor.
4. Toast Example – Using the main UI thread of Android to show a toast message
Now let’s see how we can do UI related tasks using a plugin. For the sake of this tutorial let’s build a plugin to show a toast message on the UI. Let’s first implement a Java class which is able to do that. In the world of Android, we need a context of an Activity to start a new one or update it so we will have to provide the context in which the Unity 3D engine is running on to Android.
package com.nativeplugin.unity; import android.content.Context; import android.util.Log; import android.widget.Toast; public class Plugin { /** * This variable will store the context of unity */ private Context _context; /** * Sets the context of the mobile SDK * @param context */ public void setContext(Context context) { this._context = context; } /** * Prints a debug message to the android Log Cat */ public static void logDebug() { Log.d("NativeUnity", "Hello, World!"); } /** * Prints a debug message to the android Log Cat * @param tag * @param message * @param message */ public void logDebug(String tag, String message) { Log.d(tag, message); } /** * Shows a toast message for a period of Toast.LENGTH_LONG time * @param message */ public void showToast(String message){ Toast.makeText(this._context, message, Toast.LENGTH_LONG).show(); } }
In the code above we have a few updates to our plugin class. We have created a private variable called _context to store the context of Unity 3D. This context will be used to show the toast. we have added a method called setContext which accepts a context and sets the _context. This method will be used to pass the Unity 3D context to android and store it as a reference in our Plugin class. Finally we have added a function to show the toast to the player called showToast which accepts a message as String. Let’s see how we can call this method from C#.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NativePlugins : MonoBehaviour { // Use this for initialization void Start () { // Get the android context and store it in activityContext AndroidJavaObject activityContext; using (AndroidJavaClass activity = new AndroidJavaClass ("com.unity3d.player.UnityPlayer")) { activityContext = activity.GetStatic("currentActivity"); } // Create a reference in C# to the native class in Java AndroidJavaObject nativePlugin = new AndroidJavaObject("com.nativeplugin.unity.Plugin"); // Set the context in Android nativePlugin.Call("setContext", activityContext); // Call the showToast method in the UI thread activityContext.Call("runOnUiThread", new AndroidJavaRunnable(() => { nativePlugin.Call ("showToast", new string[] {"Hello, I am a Toast!"}); })); } }
In the above code, we first create a context of the Unity3D activity. We can obtain this by accessing the com.unity3d.player.UnityPlayer class and getting the currentActivity static variable within that class. Then we use that activityContext object and pass a new thread to it as AndroidJavaRunnable object with our call to the showToast method within it. This approach is used as we are manipulating the UI thread to show are toast message. Once you build and run this, you should be able to see a toast message appear with the text Hello, I am a Toast!.
5. Message from Java – Sending a message from Java to C#
We have explored ways of communicating from C# to Android, but how about communicating from Android to C#. Let’s see how this can be achieved. We will first update our plugin class to dispatch a message from Android.
package com.nativeplugin.unity; import com.unity3d.player.UnityPlayer; import android.content.Context; import android.util.Log; import android.widget.Toast; public class Plugin { /** * This variable will store the context of unity */ private Context _context; /** * Sets the context of the mobile SDK * @param context */ public void setContext(Context context) { this._context = context; } /** * Prints a debug message to the android Log Cat */ public static void logDebug() { Log.d("NativeUnity", "Hello, World!"); } /** * Prints a debug message to the android Log Cat * @param tag * @param message */ public void logDebug(String tag, String message) { Log.d(tag, message); } /** * Shows a toast message for a period of Toast.LENGTH_LONG time * @param message */ public void showToast(String message) { Toast.makeText(this._context, message, Toast.LENGTH_LONG).show();; } /** * Send an event back to unity * @param gameObjectName * @param message */ public void sendDeviceEvent(String gameObjectName, String message) { UnityPlayer.UnitySendMessage(gameObjectName, "receivedNativeEvent", "From Android : "+message); } }
We have added a new method called sendDeviceEvent which accepts two parameters gameObjectName and message. To be able to use the UnityPlayer object to send a message we need to use a library that Unity 3D provides us. This library is automatically added when Unity 3D builds the apk. For us to use in our plugin class we need to import it to Eclipse. You’ll be able to find this JAR file in Install Dir\Unity\Editor\Data \PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar You can import this jar file into Eclipse and add it to your build path. With this done you should be able to send messages from Android to C#. let’s see how we can capture this message. We have to specify which game object this message needs to go to and the name of the function it should call and the message it should pass to Android. We are prepending a text From Android to the message we are receiving from C# and sending it back to C# by calling the receivedNativeEvent method in C#.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NativePlugins : MonoBehaviour { // Use this for initialization void Start () { // Create a reference in C# to the native class in Java AndroidJavaObject nativePlugin = new AndroidJavaObject("com.nativeplugin.unity.Plugin"); // Call the showToast method in the UI thread nativePlugin.Call ("sendDeviceEvent", new string [] {"Main Camera","Hello!"}); } // Print a message to the Debug Log public void receivedNativeEvent(string message) { Debug.Log(message); } }
We have updated the C# script and added a new function. receivedDeviceEvent. Thi function will be the function invoked from Android. It will print a message which can be viewed in the Android logcat. In the Start function we are calling the sendDeviceEvent in our Java Plugin Class. The Java Plugin class will call the receivedDeviceEvent in C# which will print a Debug Log. Go ahead and build and run you project and you should the log From Android : Hello! in your logcat.
If you do have any questions, please feel free to ask them in the comment section below.
Cheers!
Leave a Reply