5

The application I'm currently working on uses a Navigation Drawer, and each tab opens up a new fragment in the activity (replacing the old one).

One of these Fragments is a Unity3D scene. Basically, what I did was:

  1. export the Unity project as an Android app
  2. open up the Activity that it gave (which was UnityPlayerNativeActivity)
  3. convert this UnityPlayerNativeActivity to a Fragment, as shown in the code below. I also changed some minor things in the Manifest.

    import com.unity3d.player.*;
    import android.app.Fragment;
    import android.app.NativeActivity;
    import android.content.res.Configuration;
    import android.graphics.PixelFormat;
    import android.os.Bundle;
    import android.view.KeyEvent;
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.WindowManager;
    
    public class UnityPlayerNativeFragment extends Fragment
    {
        protected UnityPlayer mUnityPlayer;     // don't change the name of this variable; referenced from native code
        private static final String ARG_SECTION_NUMBER = "section_number";
    
    public static UnityPlayerNativeFragment newInstance(int sectionNumber) {
        UnityPlayerNativeFragment fragment = new UnityPlayerNativeFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }
    // Setup activity layout
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState)
    {
    
        getActivity().getWindow().takeSurface(null);
        getActivity().setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);
        getActivity().getWindow().setFormat(PixelFormat.RGB_565);
    
        mUnityPlayer = new UnityPlayer(getActivity());
        if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar", true))
            getActivity().getWindow().setFlags (WindowManager.LayoutParams.FLAG_FULLSCREEN,
                                   WindowManager.LayoutParams.FLAG_FULLSCREEN);
    
        int glesMode = mUnityPlayer.getSettings().getInt("gles_mode", 1);
        boolean trueColor8888 = false;
        mUnityPlayer.init(glesMode, trueColor8888);
        mUnityPlayer.windowFocusChanged(true);
        mUnityPlayer.setX((float)-5);
        View playerView = mUnityPlayer.getView();
        return playerView;
    }
    
    // Quit Unity
    @Override
    public void onDestroy ()
    {
        mUnityPlayer.quit();
        super.onDestroy();
    }
    
    // Pause Unity
    @Override
    public void onPause()
    {
        super.onPause();
        mUnityPlayer.pause();
    }
    
    // Resume Unity
    @Override
    public 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);
    }
    
    public void onWindowFocusChanged(boolean hasFocus)
    {
        mUnityPlayer.windowFocusChanged(hasFocus);
    }
    }
    

The problem here is with the mUnityPlayer.quit() function. As stated here, mUnityPlayer.quit() completely kills the process it is running in. So basically what happens is that the entire app exits when I switch fragments (i.e., switch tabs in the NavigationDrawer), because that calls the onDestroy of the UnityPlayerNativeFragment.

I do NOT want this. Ideally, the user would switch to another tab in the NavigationDrawer, and when he/she comes back to the tab of the Unity scene, it is exactly where he/she left it. As though they used the Android multi-tasking to change apps and return - nothing changes.

If this were an Activity, it would be possible to open it in a different process than the main app by adding the android:process=":UnityKillsMe" line in the Manifest file, but this isn't possible for a fragment. So, my questions:

  1. How can I open the UnityPlayerNativeFragment in a new process, so that the .quit() function doesn't kill my entire app, and so that I can relaunch the fragment when the user leaves the tab and then returns to it?
  2. Even if I can achieve Q1, there remains the issue of state. Quitting and then reloading the Unity scene will recreate it anew, starting from the default view, and losing any properties it had (e.g. imagine it was a game, then all the data would be lost). So question 2 is: how can I switch to another tab in the same app, then return to the Unity tab, without having the scene change at all? (Essentially, keeping it running in the background without quitting it at all).

Here's how I'm switching fragments (from the MainActivity):

@Override
    public void onNavigationDrawerItemSelected(int position) {
        // update the main content by replacing fragments
        FragmentManager fragmentManager = getFragmentManager();
        if (position==3) {
            fragmentManager.beginTransaction()
                    .replace(R.id.container, DirectoryFragment.newInstance(position + 1))
                    .commit();
        }
        else if (position == 0) {
            upnf = UnityPlayerNativeFragment.newInstance(position + 1);
            fragmentManager.beginTransaction()
                    .replace(R.id.container, upnf, "UnityPlayerNativeFragment")
                    .commit();
        }
        else if (position == 2){
            fragmentManager.beginTransaction()
                    .replace(R.id.container, WeatherFragment.newInstance(position + 1))
                    .commit();
        }
        else if (position == 4) {
            fragmentManager.beginTransaction()
                    .replace(R.id.container, SettingsFragment.newInstance(position + 1))
                    .commit();
            //Log.d("worked pos=4","worked pos=4");
        }
        else{
            fragmentManager.beginTransaction()
                    .replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
                    .commit();
        }
    }
Community
  • 1
  • 1
Jimmy
  • 435
  • 1
  • 5
  • 18

1 Answers1

0

thanks for your code . I'm finding the code how to change unity activity to a fragment.

For your question , I'm thinking why you need to call mUnityPlayer.quit(); on Destory() method . the onDestory method mean the fragment finish . not the activity .

I think remove the quit method . add a listener between the fragment with MainActivity , when the MainActivity onDestroy , call the fragment method to call the unity quit.

GioLaq
  • 2,489
  • 21
  • 26