Keeping Unity Player "alive" to reuse in other View

Hello!

I am currently building an app that needs a “native” UI. The iOS Version is done, but I am struggling with the Android version as I do not really have much Java experience. I read a few tutorials about the activity system and understood most of it, but now I am kinda stuck.

What I want to do, is have the unity view/player always active in my App, so I can show it at different times. The structure of my app is/should be like this ListView → ListView → UnityView and should have an action bar for navigation. I managed to create the UI in a standalone project exactly as I wish it, but I am stuck integrating the Unity Player into this correctly.

Obviously I could put it as a simple activity that gets started with an intent, but that way the unity player had to be restarted every time another element is selected. (I got that working, but the restarting was really ugly/slow and didn’t feel responsive like the iOS App).
So now my idea was to load the UnityPlayer at the start of the app, and when it was done loading, start a new activity with my UI. When it was done loading, I removed the player view from the parent and every time the unity view should appear I put it in another activity as a subview. That however is really hack-y, because the “main” native player activity mustn’t be destroyed. Additionally, pressing the android back-button on the UI main activity lead to getting back to the unity active player view, which isn’t wanted.

So what I want to do is, somehow initialize the player view and then re-use it later again and again. Any tips regarding this?
iOS Version video for reference: YouTube

Best regards,
Marian

Bump?

I’m trying to solve the exact problem you have described. I had a look at your app on the Play Store and it looks like you may have got this working? If so can you provide some information on what you did to solve this? I’d also be interested in how you got the Android action bar to appear in a Unity activity. Any help or information would be useful. Thanks!

After not getting it to work I scrapped the idea of keeping it “alive” and eventually just put it within another view (which never gets deleted/paused), hence the slide-in menu from the side. Doing that is really simple since you get the UnityPlayer view in the UnityPlayerActivity - just create a normal interface layout, load that and add the Unity view into it.

Thanks for the info! I am now loading the unity player as a view inside my activity and then showing and hiding as required. This has resolved the loading speed issue.

One issue I’m still having is that in onDestroy() the mUnityPlayer.quit() must be called. This is killing the app process and destroys any previous activities on the stack. I’ve tried commenting out the quit call but then next time I load the activity Unity crashes. The other option I’ve tried is setting android: process=“:Unity” on the activity. This runs the activity in a separate process and quits successfully but I then have no access to objects created in the original process that my app depends on.

Did you run into this problem? If so how did you resolve it?

Sorry, didn’t have a problem like this as I have kept the normal quit/resume/pause calls from the unity player activity!

Ahh ok. Was your UnityPlayerActivity the launch activity for your application, or did you just have one activity for the whole app?

The application was simple and small enough to keep within that one activity.

Ok that makes sense. How were you able to get the action bar/toolbar to display nicely in the UnityPlayerActivity?

I made a normal layout (like you would in any normal Android app) and embedded the unity view into that.

I’ve tried what you have suggested but I’m not getting an action bar appearing. I’m using unity 5.2.2, Android Studio 2.0 Preview, and running this on a Google Nexus 7 (Android 6.0).

Can you see where I’m going wrong?

UnityPlayerActivity.java

package com.company.unity.test;

import android.app.Activity;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Window;
import android.widget.LinearLayout;

import com.unity3d.player.UnityPlayer;

public class UnityPlayerActivity extends Activity
{
    protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code

    // Setup activity layout
    @Override protected void onCreate (Bundle savedInstanceState)
    {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);

        getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy

        mUnityPlayer = new UnityPlayer(this);
        setContentView(R.layout.activity_unity_player);
        final LinearLayout layout = (LinearLayout) findViewById(R.id.layout);
        layout.addView(mUnityPlayer);
        mUnityPlayer.requestFocus();
    }

    // Quit Unity
    @Override protected void onDestroy ()
    {
        mUnityPlayer.quit();
        super.onDestroy();
    }

    // Pause Unity
    @Override protected void onPause()
    {
        super.onPause();
        mUnityPlayer.pause();
    }

    // Resume Unity
    @Override protected void onResume()
    {
        super.onResume();
        mUnityPlayer.resume();
    }

    // This ensures the layout will be correct.
    @Override public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
        mUnityPlayer.configurationChanged(newConfig);
    }

    // Notify Unity of the focus change.
    @Override public void onWindowFocusChanged(boolean hasFocus)
    {
        super.onWindowFocusChanged(hasFocus);
        mUnityPlayer.windowFocusChanged(hasFocus);
    }

    // For some reason the multiple keyevent type is not supported by the ndk.
    // Force event injection by overriding dispatchKeyEvent().
    @Override public boolean dispatchKeyEvent(KeyEvent event)
    {
        if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
            return mUnityPlayer.injectEvent(event);
        return super.dispatchKeyEvent(event);
    }

    // Pass any events not handled by (unfocused) views straight to UnityPlayer
    @Override public boolean onKeyUp(int keyCode, KeyEvent event)     { return mUnityPlayer.injectEvent(event); }
    @Override public boolean onKeyDown(int keyCode, KeyEvent event)   { return mUnityPlayer.injectEvent(event); }
    @Override public boolean onTouchEvent(MotionEvent event)          { return mUnityPlayer.injectEvent(event); }
    /*API12*/ public boolean onGenericMotionEvent(MotionEvent event)  { return mUnityPlayer.injectEvent(event); }
}

activity_unity_player.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/layout"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical">

</LinearLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.company.unity.test"android:installLocation="preferExternal"android:versionCode="1"android:versionName="1.0">

<supports-screensandroid:anyDensity="true"android:largeScreens="true"android:normalScreens="true"android:smallScreens="true"android:xlargeScreens="true" />

<applicationandroid:banner="@drawable/app_banner"android:debuggable="false"android:icon="@drawable/app_icon"android:isGame="true"android:label="@string/app_name">
<activityandroid:name="com.company.unity.test.UnityPlayerActivity"android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale"android:label="@string/app_name"android:launchMode="singleTask"android:screenOrientation="fullSensor"android:theme="@android:style/Theme.Material.Light.DarkActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<meta-dataandroid:name="unityplayer.UnityActivity"android:value="true" />
</activity>
</application>

<uses-sdkandroid:minSdkVersion="21"android:targetSdkVersion="23" />

<uses-feature android:glEsVersion="0x00020000" />
<uses-featureandroid:name="android.hardware.sensor.accelerometer"android:required="false" />
<uses-featureandroid:name="android.hardware.touchscreen"android:required="false" />
<uses-featureandroid:name="android.hardware.touchscreen.multitouch"android:required="false" />
<uses-featureandroid:name="android.hardware.touchscreen.multitouch.distinct"android:required="false" />
</manifest>

Re-using unity player
I solved my issue by creating a wrapper around UnityPlayer where I override the kill method to do nothing. Doing this means when the quit method is called the thread is not killed. Replace references of UnityPlayer with UnityPlayerWrapper in UnityPlayerActivity.

public class UnityPlayerWrapper extends UnityPlayer {

   public UnityPlayerWrapper(final ContextWrapper contextWrapper) {
      super(contextWrapper);
   }

   @Override
   protected void kill() {
      //Do nothing
   }

}

Action bar

I know it has been a while but I am trying to open a fragment with the unity player still alive, the Unity Player is playing in the Activity, then I open a fragment on top and continue playing (in a new view). This works until I close the fragment, then the one in the initial Activity no longer works. Any ideas?