Plugins - Pro/Mobile-Only Feature
Unity has extensive support for C, C++ or Objective-C based Plugins. Plugins allow your game code (written in Javascript, C# or Boo) to call into native code libraries. This allows Unity to integrate with other middleware libraries or legacy game code.
Note: Plugins are a Pro-only feature. For desktop builds, plugins will work in standalones only. They are disabled when building a Web Player for security reasons.
In order to use a plugin you need to do two things:
- Write a plugin in a C based language and compile it.
- Create a C# script which calls functions in the plugin to do something.
So the plugin provides a simple C interface. The script then invokes the functions exposed by the plugin.
Here is a very simple example:
The C File of a Minimal Plugin:
float FooPluginFunction () { return 5.0F; }
A C# Script that Uses the Plugin:
Desktop
using UnityEngine; using System.Runtime.InteropServices; class SomeScript : MonoBehaviour { // This tells unity to look up the function FooPluginFunction // inside the plugin named "PluginName" [DllImport ("PluginName")] private static extern float FooPluginFunction (); void Awake () { // Calls the FooPluginFunction inside the PluginName plugin // And prints 5 to the console print (FooPluginFunction ()); } }
Android
using UnityEngine; using System.Runtime.InteropServices; class SomeScript : MonoBehaviour { // This tells unity to look up the function FooPluginFunction // inside the plugin named "PluginName" [DllImport ("PluginName")] private static extern float FooPluginFunction (); void Awake () { // Calls the FooPluginFunction inside the PluginName plugin // And prints 5 to the console print (FooPluginFunction ()); } }
Desktop
Building a Plugin for Mac OS X
If you are building a plugin for Mac OS X, you have to create a bundle. The easiest way to do this is using XCode. Use
and select the Bundle - Carbon/Cocoa Loadable Bundle.If you are using C++ (.cpp) or Objective-C (.mm) to implement the plugin you have to make sure the functions are declared with C linkage to avoid name mangling issues.
extern "C" { float FooPluginFunction (); }
Building a Plugin for Windows
Plugins on Windows are DLL files with exported functions. Practically any language or development environment that can create DLL files can be used to create plugins.
Again, if you use C++, declare functions with C linkage to avoid name mangling issues.
Using your plugin from C#
Once you have built your bundle you have to copy it to
folder. Unity will then find it by its name when you define a function like this:[DllImport ("PluginName")] private static extern float FooPluginFunction ();
Please note that PluginName should not include the extension of the filename. Be aware that whenever you change code in the Plugin you will have to recompile scripts in your project or else the plugin will not have the latest compiled code.
Deployment
For cross platform plugins you have to include both .bundle (for Mac) and .dll (for Windows) files in Plugins folder. Once you have placed your plugins in the Plugins folder there is no more work required on your side. Unity automatically picks the right plugin for the right deployment platform and includes it with the player.
Android
Building a Plugin for Android
To build a plugin for Android, you first need the Android NDK. Please familiarize yourself with the steps how to build a shared library.
If you are using C++ (.cpp) to implement the plugin you have to make sure the functions are declared with C linkage to avoid name mangling issues.
extern "C" { float FooPluginFunction (); }
Using Your Plugin from C#
Once you have built your shared library you have to copy it to
folder. Unity will then find it by its name when you define a function like this:[DllImport ("PluginName")] private static extern float FooPluginFunction ();
Please note that PluginName should not include the prefix ('lib') nor the extension ('.so') of the filename. It is recommended to wrap all native code methods with an additional C# code layer. This code could check Application.platform and call native methods only when running on the actual device and return mockup values when running in the Editor. You can also use platform defines to control platform dependent code compilation.
Deployment
For cross platform plugins, your Plugins folder may include plugins for several different platforms (i.e. libPlugin.so for Android, Plugin.bundle for Mac and Plugin.dll for Windows). Unity automatically picks the right plugin for the right deployment platform and includes it with the player.
Using Java Plugins
The Android plugin mechanism also allows Java to be used to enable interaction with the Android OS. Java code can not be called directly from C#, so you'd need to write a native plugin to 'translate' the calls between C# and Java.
Building a Java Plugin for Android
There are several ways to create a Java plugin. What they have in common is that you will end up with a .jar file containing the .class files for your plugin. One way is to start off with downloading the JDK, then compile your .java files from command line with javac to create .class files and package them into a .jar with the jar command line tool. Another way is to use Eclipse IDE together with the ADT.
Using Your Java Plugin from Native Code
Once you have built your Java plugin (.jar) you have to copy it to Java Native Interface (JNI). JNI works both ways; calling native code from Java, and interacting with Java (or the JavaVM) from native code.
folder. Unity will package your .class files together with the rest of the Java code, and then call it through something calledTo find your Java code from the native side you need to have access to the Java VM. Fortunately that is quite easy; just add a function like this to your C(++) code:
jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* jni_env = 0; vm->AttachCurrentThread(&jni_env, 0); }
This is all that is needed to start using Java from C(++). Completely explaining JNI is somewhat beyond the scope of this document, but usually it includes finding the class definition, resolving the constructor (<init>) method and create a new object instance, as shown here:
jobject createJavaObject(JNIEnv* jni_env) { jclass cls_JavaClass = jni_env->FindClass("com/your/java/Class"); // find class definition jmethodID mid_JavaClass = jni_env->GetMethodID (cls_JavaClass, "<init>", "()V"); // find constructor method jobject obj_JavaClass = jni_env->NewObject(cls_JavaClass, mid_JavaClass); // create object instance return jni_env->NewGlobalRef(obj_JavaClass); // return object with a global reference }
Using Your Java Plugin with helper classes
AndroidJNIHelper together with AndroidJNI can be used to ease a bit of pain with raw JNI.
AndroidJavaObject and AndroidJavaClass automate a lot of things and also use caches to make calls to Java faster. The combination of AndroidJavaObject and AndroidJavaClass builds on top of AndroidJNI and AndroidJNIHelper, but also has a lot of logic in itself (to handle the automation). These classes also come in a 'static' version to access static members of Java class.
You can opt for any approach you like, be it a raw JNI through AndroidJNI class methods, or AndroidJNIHelper together with AndroidJNI and eventually AndroidJavaObject/AndroidJavaClass for the maximum automation and convenience.
- UnityEngine.AndroidJNI is a wrapper for the JNI calls available in C (as described above). All methods in this class are static and have a 1:1 mapping to the Java Native Interface.
- UnityEngine.AndroidJNIHelper provides helper functionality used by the next level, but is exposed as public methods because they may be useful for some special cases.
- Instances of UnityEngine.AndroidJavaObject and UnityEngine.AndroidJavaClass have a 1:1 mapping to an instances of java.lang.Object and java.lang.Class (or subclasses thereof) on the Java side, correspondingly. They essentially provide 3 types of interaction with the Java side:
- Call a method
- Get the value of a field
- Set the value of a field
The Call is separated into two categories: Call to a 'void' method, and Call to a method with non-void return type. A generic type is used to represent the return type of those methods which return a non-void type.The Get/Set always take a generic type representing the field type.
Example 1
//The comments is what you would need to do if you use raw JNI AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); // jni.FindClass("java.lang.String"); // jni.GetMethodID(classID, "<init>", "(Ljava/lang/String;)V"); // jni.NewStringUTF("some_string"); // jni.NewObject(classID, methodID, javaString); int hash = jo.Call<int>("hashCode"); // jni.GetMethodID(classID, "hashCode", "()I"); // jni.CallIntMethod(objectID, methodID);
Here we're creating an instance of java.lang.String, initialized with a of our choice and retrieving the hash value for that string.
The AndroidJavaObject constructor takes at least one parameter: the name of class for which we want to construct an instance. Everything after the class name are parameters for the constructor call of the object, in this case a string "some_string". Then the Call to hashCode() that returns an 'int' which is why we use that as the generic type parameter to the Call method.
Note: You cannot instantiate nested Java class using dotted notation. Inner classes must use $ separator, and it should work in both dotted and slash'd format. So android.view.ViewGroup$LayoutParams or android/view/ViewGroup$LayoutParams can be used, where LayoutParams class is nested in ViewGroup class.
Example 2
One of the plugin samples above shows how to get the cache dir for the current application. This is how you would to it from C# without any plugins:
AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); // jni.FindClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); // jni.GetStaticFieldID(classID, "Ljava/lang/Object;"); // jni.GetStaticObjectField(classID, fieldID); // jni.FindClass("java.lang.Object"); Debug.Log(jo.Call<AndroidJavaObject>("getCacheDir").Call<string>("getCanonicalPath")); // jni.GetMethodID(classID, "getCacheDir", "()Ljava/io/File;"); // or any baseclass thereof! // jni.CallObjectMethod(objectID, methodID); // jni.FindClass("java.io.File"); // jni.GetMethodID(classID, "getCanonicalPath", "()Ljava/lang/String;"); // jni.CallObjectMethod(objectID, methodID); // jni.GetStringUTFChars(javaString);
Here we start with AndroidJavaClass instead of AndroidJavaObject, because we want access a static member of com.unity3d.player.UnityPlayer, not create a new object (there is already one created as part of the Android UnityPlayer). Then we access the static field "currentActivity" but this time we use AndroidJavaObject as the generic parameter. This is because the actual field type (android.app.Activity) is a subclass of java.lang.Object, and any non-primitive type must be accessed as AndroidJavaObject (strings are the exception to that rule; they can be accessed directly even though they don't represent a primitive type in Java).
After that it's just a matter of simply traversing the Activity through getCacheDir(), to get the File object representing the cache directory, and call getCanonicalPath() to get a string representation.
Of course nowadays you don't need to do that to get the cache directory; we have provided access to the application's cache and file directory through Application.temporaryCachePath, Application.persistentDataPath.
Example 3
Finally a small trick to show how we can pass data from Java to script code using UnitySendMessage.
using UnityEngine; public class NewBehaviourScript : MonoBehaviour { void Start () { JNIHelper.debug = true; using (JavaClass jc = new JavaClass("com.unity3d.player.UnityPlayer")) { jc.CallStatic("UnitySendMessage", "Main Camera", "JavaMessage", "whoowhoo"); } } void JavaMessage(string message) { Debug.Log("message from java: " + message); } }
The Java class com.unity3d.player.UnityPlayer now has a static method UnitySendMessage, equivalent to the iOS' UnitySendMessage on the native side; it can be used in Java to pass data to script code.
But here we call it directly from script code. That essentially relays the message on the Java side, which then calls back in the native/Unity to deliver the message to the object named "Main Camera", with a script attached to it which as a method called "Java Message".
The best practices of using the Java plugins with Unity
As this section is manly targeted at people who don't have comprehensive JNI, Java and Android experience, we assume that AndroidJavaObject/AndroidJavaClass approach has been taken to interact with Java code from Unity.
The very first thing that should be noted is that any operation you do on a AndroidJavaObject / AndroidJavaClass is expensive (as is a raw JNI approach though). It's very much recommended to keep the number of transitions between managed and native/java to a minimum. Both for the sake of performance and also complexity.
Basically you could have a Java method to do all the actual work and then use AndroidJavaObject / AndroidJavaClass to communicate with that method and get the result. That's said, it may be beneficial to know, that with JNI helper classes we try to cache as much data as possible.
//The first time you call a Java function like AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); // somewhat expensive int hash = jo.Call<int>("hashCode"); // first time - expensive int hash = jo.Call<int>("hashCode"); // second time - not as expensive as we already know the java method and can call it straight
It's the same way as a JIT: first time you call it, it will be slow because the code doesn't exist. Next time is much cheaper. In other words, you pay for every .Call /.Get / .Set, but you pay less after the first time you call it, per object. You pay for creating a AndroidJavaClass / AndroidJavaObject.
Mono garbage collector should release all the created instances of AndroiJavaObject / AndroidJavaClass, yet it is strongly advised to keep them in a using(){} statement to ensure they are deleted as soon as possible. Otherwise there is no real guarantee when they will be destroyed. If you set AndroidJNIHelper.debug = true; you will see the debug output exposing the Garbage Collector behavior.
//Getting the system language with the safe approach void Start () { using (AndroidJavaClass cls = new AndroidJavaClass("java.util.Locale")) { using(AndroidJavaObject locale = cls.CallStatic<AndroidJavaObject>("getDefault")) { Debug.Log("current lang = " + locale.Call<string>("getDisplayLanguage")); } } }
You can also call the .Dispose() method directly to ensure there are no Java objects lingering. The actual C# object might live a bit longer though, but will be garbage collected by mono eventually.
Extending the UnityPlayerActivity Java Code
With Unity Android it is also possible to extend the regular UnityPlayerActivity (primary Java class for the Unity Player on Android, similarly to AppController.mm on Unity iOS).
An application can, by creating a new Activity which derives from UnityPlayerActivity (UnityPlayerActivity.java can be found at on Mac and usually at C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\src\com\unity3d\player), override any and all of the basic interaction between Android OS and Unity Android.
To do that first locate the AndroidManifest.xml. The AndroidManifest.xml should also be placed under .
shipped with Unity Android. It is found under the installation folder (usually (on Windows) or (on Mac)) in a folder called . Then add that file to the classpath used to compile the new activity. The resulting .class file(s) should be compressed into a .jar file and placed under . As the manifest dictates which activity to launch it's also necessary to create a newThe new activity could look like OverrideExample.java:
package com.company.product; import com.unity3d.player.UnityPlayerActivity; import android.os.Bundle; import android.util.Log; public class OverrideExample extends UnityPlayerActivity { protected void onCreate(Bundle savedInstanceState) { // call UnityPlayerActivity.onCreate() super.onCreate(savedInstanceState); // print debug message to logcat Log.d("OverrideActivity", "onCreate called!"); } public void onBackPressed() { // instead of calling UnityPlayerActivity.onBackPressed() we just ignore the back button event // super.onBackPressed(); } }
And this is what the matching AndroidManifest.xml would look like:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product"> <application android:icon="@drawable/app_icon" android:label="@string/app_name"> <activity android:name=".OverrideExample" 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>
Examples
Desktop
Simplest Plugin
This is a basic example of a project and a plugin that only do basic operations (prints a number, prints a string, adds two floats and adds two integers). Check this example if this is your first time learning plugins in Unity.
The project can be found here.
This project includes both Windows and Mac project files.
Midi Plugin
A complete example of the Plugin interface can be found here.
This is a complete Midi plugin for OS X which uses Apple's CoreMidi API. It provides a simple C API and a C# class using the C API. The C# class contains a high level API, with easy access to NoteOn and NoteOff events and their velocity.
Texture Plugin
An example of how to assign image data to a texture from C++ directly to OpenGL (note that this will only work when Unity is using an OpenGL renderer). This example includes both XCode (for Mac) and Visual Studio (for Windows) project files. The plugin with accompanying Unity project can be found here.
Android
Native Plugin Sample
A simple example of the use of native code plugin can be found here
This sample demonstrates how C code can be invoked from a Unity Android application. The package includes a scene which displays the sum to two values, calculated by the native plugin. Please note that you will need the Android NDK to compile the plugin.
Java Plugin Sample
An example of the use of Java code can be found here
This sample demonstrates how Java code can be used to interact with the Android OS and how C++ creates a bridge between C# and Java. The package includes a scene which displays a button that when pressed fetches the application cache directory, as defined by the Android OS. Please note that you will need both the JDK and the Android NDK to compile the plugins.
The same example but based on a prebuilt JNI library to wrap the native code into C# can be found here