Frobei
Frobei

Reputation: 49

Importing two Unity projects into Android Studio doesn't work as expected

I am currently training to make applications which use Unity to add some features (AR, VR, etc...). For now I've been working on Android, with Android Studio, and once I'll be done with I'll train on iOS.

My aim is simple: my MainActivity displays two buttons, each one calls a separate Unity project (exported from Unity as Google Android project) to launch its scene.

To do so, I imported those two Unity projects Scene1 and Scene2 as libraries and I call them by starting their activities (see the code below).

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void goToUnity1(View v){
        Intent intent = new Intent(this, com.example.unityscene1.UnityPlayerActivity.class);
        startActivity(intent);
    }

    public void goToUnity2(View v){
            Intent intent = new Intent(this, com.example.unityscene2.UnityPlayerActivity.class);         
            startActivity(intent);

     }
}

And its AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.multipleunity">

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <activity android:name="com.example.unityscene1.UnityPlayerActivity"></activity>
    <activity android:name="com.example.unityscene2.UnityPlayerActivity"></activity>


</application>

The UnityPlayerActivity files in Scene1 and Scene2 are generated by Unity so they are similar (here are their onCreate method):

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(mUnityPlayer);
        mUnityPlayer.requestFocus();
    }

    ...
}

The code compiles without any problem, the application launches and displays the MainActivity with its two buttons. When I click on the first button, it launches the first Unity scene as I expected. However, when I click on the second button it also launches the first Unity scene instead of the second one.

So I tried to understand why this happens and here is what I can tell so far:

UPDATE

After spending hours on this I'm sure the problem comes from the name of the ressources Unity delivers when I exported in Google Android project which are always the same (unity-classes.jar).

Here's what I read here:

The Android development tools merges the resources of a library project with the resources of the application project. In the case that a resource’s ID is defined several times, the tools select the resource from the application, or the library with highest priority, and discard the other resource.

Adding to that, if I have multiple libraries the priority matches the dependency order (the order they appear in the Gradle dependencies block).

So I tried to reverse the order of the dependencies, I put the second Unity project before the first one and as that was expected both buttons launch the second Scene of Unity.

Now I know that, is there a way to avoid this name conflict ?

Upvotes: 2

Views: 2028

Answers (2)

ANAND PURUSHOTTAM
ANAND PURUSHOTTAM

Reputation: 93

One possible solution which i came across is dynamically loading multiple smaller unity apks inside a single apk . I have done some research and found out that it is possible to launch one apk from other without even actually installing the apk. This can be done by various methods and there are many library available for doing this to. Few of them are.

DexClassLoader class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.

Grab-n-Run Securely load code dynamically into your Android application from APK containers or JAR libraries translated to be executable by both the Dalvik Virtual Machine (DVM) and the Android Runtime (ART).

Upvotes: 0

Frobei
Frobei

Reputation: 49

I did not solve my problem, but I managed to find a way to avoid it: instead of importing two small Unity projects I decided to make a bigger one which includes two different scenes (one with Vuforia AR and the other with some UI text). Then the challenge was to find a way to call the right Unity scene when I click on the associated button I designed on Android.

Unity

On each scene I created an empty object (I called it SceneManagerObject) which will be attached the script GoToScene. In this script there will be the following methods:

private void ChangeScene(string sceneName)
{
    SceneManager.LoadScene (sceneName);
}

public void ReceiveJavaMessage(string message)
{
    if (message.Equals ("Scene1") || message.Equals ("Scene2")) 
    {
        ChangeScene (message);
    } 
    else 
    {
        Debug.Log ("The scene name is incorrect");
    }
}

The second method will be the one being called from the Java code.

Then I created a UI button on each scene which will call onClick method when clicked, its purpose is to be able to go back to Android without destroying the Unity activity so we can resume it after if I wanted to. For this, I created another Empty Object with the script GoBackToAndroid attached:

public class GoBackToAndroid : MonoBehaviour {
public string activityName; // contains the name of the activity I want to go when I quit this scene

public void onClick()
{
    callJavaMethod();
}


private void callJavaMethod()
{
    AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer);
    AnroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
    jo.Call("ReceiveUnityMessage", activityName); // Will call a method in Java with activityName as parameter
}

That's it for the Unity, after I exported the project as a Google Android project and I kept it warm for the next step.

Android Studio

After importing the Unity project as a library. I created three activities: MainActivity, SecondActivity and MultipleScenesUnityActivity:

public class MultipleScenesUnityActivity extends UnityPlayerActivity {
    public static boolean unityIsRunning = false; // true if a Unity Activity is running on background


    @Override protected void onDestroy()
    {
        unityIsRunning = false;
        super.mUnityPlayer.quit();
        super.onDestroy();
    }

    @Override protected void onResume()
    {
        Intent currentIntent = getIntent();
         // If there's no Unity Activity inside the back stack
        // (meaning it just had been created)
        if (!unityIsRunning)
        {
             super.mUnityPlayer.UnitySendMessage("SceneManagerObject", "ReceiveJavaMessage", currentIntent.getStringExtra("sceneName");
        }
        unityIsRunning = true;
        super.onResume();
        super.mUnityPlayer.resume();
    }

    public void ReceiveUnityMessage (String messageFromUnity)
    {
        Intent intent;
        if (messageFromUnity.equals("MainActivity"))
        {
            intent = new Intent(this, MainActivity.class);
            startActivity(intent);
        }
        else if (messageFromUnity.equals("SecondActivity"))
        {
            intent = new Intent(this, SecondActivity.class);
            startActivity(intent);
        }
        else 
            Log.d("Test", "The activity " + messageFromUnity + " doesn't exist");
    }
}

The ReceiveUnityMessage is the method called when we leave the Unity activity and its purpose is to lead us to the activity we desire to go.

Then the MainActivity (the SecondActivity follows the same pattern):

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void goToActivity2(View view) // called when I click on a button
    {
        Intent intent = new Intent(this, SecondActivity.class);
        startActivity(intent);
    }

    public void goToUnity(View view) // called when I click on another button
    { 
        Intent intentUnity = new Intent(this, MultipleScenesUnityActivity.class);

        if (unityIsRunning)
        {
            intentUnity.removeExtra("sceneName"); // We have to clean the extra before putting another one otherwise we'll get always the same Unity scene

            /*  If set in an Intent passed to Context.startActivity(), this flag will cause the launched activity to be brought
             *  to the front of its task's history stack if it is already running. */
            intentUnity.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

            /* We send a string to Unity which, when received, will change the scene.
            * This ONLY works when the Unity activity is running on background. */
            UnitySendMessage("SceneManagerObject", "ReceiveJavaMessage", "Scene1");
        }

        else
        {
            /* The intent gets a key with its String value. The value has to be the name of the Unity
            * scene and is received in UnityActivity1 when the activity starts.*/
            intentUnity.putExtra("sceneName", "Scene1");
        }
        startActivity(intentUnity);
    }
}

So what I did is connecting everything by sending messages so I can manage the scene I'd like to launch and the same goes for the activities.

I'm sure there are a better ways to do so, I'm still a novice and I can be clumsy.. Anyway, I managed to avoid my original problem so if anyone faces the same issue this answer might be an interesting alternative.

Upvotes: 2

Related Questions