将两个Unity项目导入Android Studio无法按预期工作

时间:2017-03-22 13:50:34

标签: android android-studio unity3d

我目前正在培训制作使用Unity添加一些功能(AR,VR等等)的应用程序。目前我一直在Android上使用Android Studio工作,一旦完成,我将在iOS上进行训练。

我的目标很简单:我的MainActivity显示两个按钮,每个按钮调用一个单独的Unity项目(从Unity作为Google Android项目导出)以启动其场景。

为此,我将这两个Unity项目Scene1Scene2导入为库,我通过开始他们的活动来调用它们(请参阅下面的代码)。

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);

     }
}

及其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>

UnityPlayerActivityScene1中的Scene2文件由Unity生成,因此它们类似(这是他们的onCreate方法):

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

    ...
}

代码编译没有任何问题,应用程序启动并显示MainActivity及其两个按钮。当我点击第一个按钮时,它会按照我的预期启动第一个Unity场景。但是,当我点击第二个按钮时,它也会启动第一个Unity场景而不是第二个场景。

所以我试着理解为什么会发生这种情况,这是我到目前为止所说的:

  • 我没有错误地将相同的Unity项目放两次
  • 在将Scene2转换为库之前,它可以自行运行
  • 点击第二个按钮
  • 时调用活动com.example.unityscene2.UnityPlayerActivity

更新

花了好几个小时之后,我确信问题来自于我在Google Android项目中导出时,Unity提供的资源名称总是相同的(unity-classes.jar)。

以下是我读到的here

  

Android开发工具将库项目的资源与应用程序项目的资源合并。在多次定义资源ID的情况下,工具从应用程序或具有最高优先级的库中选择资源,并丢弃其他资源。

除此之外,如果我有多个库,则优先级与依赖顺序(它们在Gradle依赖项块中出现的顺序)相匹配。

所以我试图颠倒依赖关系的顺序,我把第二个Unity项目放在第一个之前,因为预期两个按钮都会启动第二个Unity场景。

现在我知道,有没有办法避免这个名字冲突?

2 个答案:

答案 0 :(得分:0)

我没有解决我的问题,但我设法找到了避免它的方法:我决定制作一个更大的一个包括两个不同的场景(一个用Vuforia AR,另一个用一些),而不是导入两个小的Unity项目。 UI文字)。然后挑战是当我点击我在Android上设计的相关按钮时,找到一种方法来调用正确的Unity场景。

<强>统一

在每个场景中,我创建了一个空对象(我称之为SceneManagerObject),它将附加脚本GoToScene。在此脚本中将有以下方法:

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");
    }
}

第二种方法是从Java代码中调用的方法。

然后我在每个场景上创建了一个UI按钮,在点击时会调用onClick方法,其目的是能够在不破坏Unity活动的情况下返回到Android,这样我们就可以在我想要之后恢复它。为此,我创建了另一个附加脚本GoBackToAndroid的空对象:

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
}

在我将项目导出为Google Android项目之后,这就是Unity的用途,我为下一步保持温暖。

Android Studio

将Unity项目导入为库后。我创建了三项活动:MainActivitySecondActivityMultipleScenesUnityActivity

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");
    }
}

ReceiveUnityMessage是我们离开Unity活动时调用的方法,它的目的是引导我们进行我们想去的活动。

然后MainActivitySecondActivity遵循相同的模式):

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);
    }
}

所以我所做的就是通过发送消息来连接所有内容,这样我就可以管理我想要启动的场景,活动也是如此。

我确信有更好的方法可以做到这一点,我仍然是一个新手,我可以笨拙..无论如何,我设法避免我原来的问题,所以如果有人面临同样的问题,这个答案可能是一个有趣的选择。

答案 1 :(得分:0)

我遇到的一个可能的解决方案是在单个apk中动态加载多个较小的统一apks。我做了一些研究,发现有可能从其他人发起一个apk甚至没有实际安装apk。这可以通过各种方法完成,并且有许多 library可用于执行此操作。其中很少是。

<强> DexClassLoader 类加载器,用于从包含classes.dex条目的.jar和.apk文件加载类。这可用于执行未作为应用程序的一部分安装的代码。

<强> Grab-n-Run 从APK容器或JAR库动态地将代码动态加载到您的Android应用程序中,这些库被转换为可由Dalvik虚拟机(DVM)和Android运行时(ART)执行。