Android+Unity Integration
Using instruments for what they were created for
In theory, everything is simple. If your goal is an AR/VR game, you use Unity. If you want to create a plain Android app, you use Android SDK. But what if you need both?
In our startup, we need to create demo applications which allow to perform a certain customization in plain Android mode and then to see an interactive result in a 3D viewer. To achieve it, we use Unity.
Have you ever seen a Unity app developer trying to imitate native Android interface with Material Design in a limited time? If yes, then you know the problem. Unity is a very powerful tool for AR/VR games, but when it comes to creating traditional interfaces in Unity the amount of time and efforts become unreasonable.
So, why not to use Android SDK for that? I decided to give it a try. Since I haven’t found any tutorial on that, here is a description of what I did.
Here is a sample Android app containing a touchable Unity sub-view with a flying cube and a native Android button to control its motion:
From the Android+Unity integration standpoint this app implementation consists of two parts:
- Displaying Unity. Depending on the app, the Unity view can be placed in a sub-view like it’s done in the flying cube example or it can own a whole activity.
- Interaction with Unity. Unity functions can be called from Android and vice versa.
1. Displaying Unity View
Unity Android build looks like any other Android project with some auto-generated code. All meaningful Unity code and everything to run it is stored in libs/unity-classes.jar, src/main/assets and src/main/jniLibs. Once you imported it all in a new Android project and added compile project(‘:unity-classes’)
to the app dependencies, it’s ready to work with Unity. To connect Unity and Android we use UnityPlayer mUnityPlayer
object. Since the Unity player is accessed from both Unity and Android code it should be always named this way and have protected
or public
scope.
To display Unity view we need to mark the activity as Unity-driven in AndroidManifest.xml:
<activity...>
<meta-data android:name="unityplayer.UnityActivity" android:value="true" />
</activity>
and to attach the Unity player to a view:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy mUnityPlayer = new UnityPlayer(this);
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
unityLayout.addView(mUnityPlayer.getView(), 0, lp);
}
If you want Unity to own the whole activity instead of placing it in a subview, just call setContentView(mUnityPlayer)
.
Unity view won’t function until it’s signaled that the activity is running and the view is in focus. That’s why you need to override 4 methods:
@Override
protected void onDestroy() {
mUnityPlayer.quit();
super.onDestroy();
}
@Override
protected void onPause() {
super.onPause();
mUnityPlayer.pause();
}
@Override
protected void onResume() {
super.onResume();
mUnityPlayer.resume();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
mUnityPlayer.windowFocusChanged(hasFocus);
}
The final step is making the activity aware of what’s going on with the device.
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mUnityPlayer.configurationChanged(newConfig);
}
At this point, we have a fully functional Android app with a Unity view inside. Unity player responds to all touch events on the attached view without any additional code, however, if that’s not enough, you can inject any other events manually:
mUnityPlayer.injectEvent(event);
2. Interaction with Unity
Any C# public function on an object can be called with
UnityPlayer.UnitySendMessage("Object Name", "Function Name", "arg");
Unity player allows to pass only one String parameter but in most cases it’s enough. Calling Android methods from the Unity code is performed by the same UnityPlayer
class:
using (AndroidJavaClass cls_UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
{
using (AndroidJavaObject obj_Activity = cls_UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
{
obj_Activity.Call("function name", "arg");
obj_Activity.CallStatic("static function name");
}
}
That’s it. Now you can create any Material Design interfaces with sub-views driven by Unity inside.
Remember that by default Unity makes the apps full screen by setting View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
,View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
and WindowManager.LayoutParams.FLAG_FULLSCREEN
flags. I haven’t found a way to turn it off in Android but you can change it in Unity in 2 steps:
- Go to Build Setting →Resolution & Presentation → Status Bar and uncheck Status Bar Hidden,
- If it didn’t work, add
Screen.fullScreen = false;
to the C# script.
Thanks for reading!