【问题标题】:Importing two Unity projects into Android Studio doesn't work as expected将两个 Unity 项目导入 Android Studio 无法按预期工作
【发布时间】:2017-03-22 13:50:33
【问题描述】:

我目前正在培训使用 Unity 添加一些功能(AR、VR 等)的应用程序。目前,我一直在使用 Android Studio 开发 Android,一旦完成,我将在 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>

Scene1Scene2 中的 UnityPlayerActivity 文件是由 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 的第二个场景。

现在我知道了,有没有办法避免这种名称冲突?

【问题讨论】:

  • 你解决了这个问题吗?

标签: android android-studio unity3d


【解决方案1】:

我没有解决我的问题,但我设法找到了避免它的方法:我决定制作一个更大的 Unity 项目,而不是导入两个小的 Unity 项目,其中包括两个不同的场景(一个使用 Vuforia AR,另一个使用一些用户界面文本)。然后挑战是当我点击我在 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
}

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

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

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

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

【讨论】:

  • 您好 Frobei,这对您有用吗?还是你找到了更好的方法?我一直在努力让它发挥作用。对我来说,无论我按下哪个按钮,每次都会打开相同的场景。转到 Android 应用程序的后退按钮根本不起作用。我是 Unity 新手,所以可能我错过了设置中的某些内容。
【解决方案2】:

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

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

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

【讨论】:

  • 这看起来确实很有趣。我不知道这是可能的,虽然我知道我们可以从另一个应用程序启动一个应用程序,但我不想为每个 Unity 项目安装一个应用程序。你成功了吗?
  • 可以在不安装的情况下加载 apk。我尝试使用这种方法来实现,但是这种方法只能访问类文件而不是资源文件,因此这种方法失败了。期待更好的解决方案,但这对我来说似乎是不可能完成的任务。
  • 那么也许你可以试试我做的,但是你可能需要先将你的两个 Unity 项目融合为一个。我不知道怎么做,但这似乎不是不可能的,然后你可以从 Android 调用你的场景。
  • 我已经提到在单个应用程序中添加两个统一项目作为模块似乎是不可能的,因为您不能在单个应用程序中拥有两个版本的 JNI 库。
  • 我同意你的观点,我们不能(或者我不知道如何)将一些 Unity 项目添加为模块。但是,您可以尝试将您的项目融合到一个大的 Unity 项目中,因此您只需导入这个项目,您将获得一个模块。因此,您不会以这种方式发生任何冲突。我没有尝试融合 Unity 项目,我只是创建了一个包含 2 个场景的项目,我将这些场景导入 Android Studio 并且借助 Java 方法我能够调用它们。
猜你喜欢
  • 2018-06-20
  • 2018-02-02
  • 2014-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-25
相关资源
最近更新 更多