在本机Android应用程序中管理UnityPlayer生命周期的错误

IAE*_*IAE 16 android unity-game-engine android-activity

我正在开发一个需要在活动中加载UnityPlayer实例的Android应用程序,使用以下论坛帖子中的代码作为指南:

http://forum.unity3d.com/threads/98315-Using-Uni​​ty-Android-In-a-Sub-View.

最初,应用程序在名为"UnityActivity.java"的活动中正确显示UnityPlayer.

当用户导航回MainActivity(通过按下硬件后退按钮或单击ActionBar后退按钮)然后尝试重新打开UnityActivity时会出现问题 - 在这种情况下会显示黑屏而不是UnityPlayer.论坛中的用户建议将onPause和onResume生命周期事件转发给UnityPlayer,如下面的代码所示.但是,在执行此操作时,会显示以下错误并且应用程序崩溃.

首次导航到UnityActivity时会记录此信息:

W/libc(21095): pthread_create sched_setscheduler call failed: Operation not permitted

单击后退按钮时会记录此错误:

W/Choreographer(20963): Already have a pending vsync event. There should only be one at a time.

第二次导航到UnityActivity时会记录此错误:

A/libc(21095): Fatal signal 11 (SIGSEGV) at 0x00000000 (code=1), thread 21176 (Thread-5073)

......此时我被踢出了申请表.

这是主要活动的摘录MainActivity.java:

public void startUnityActivity(View view) {
        Intent intent = new Intent(this, UnityActivity.class);
    startActivity(intent);
}
Run Code Online (Sandbox Code Playgroud)

这是Unity活动的摘录UnityActivity.java:

public class UnityActivity extends ActionBarActivity {
    UnityPlayer m_UnityPlayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

         setContentView(R.layout.activity_unity);

        m_UnityPlayer = new UnityPlayer(this);
        int glesMode = m_UnityPlayer.getSettings().getInt("gles_mode", 1);
        m_UnityPlayer.init(glesMode, false);

        FrameLayout layout = (FrameLayout) findViewById(R.id.unityView);
        FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        layout.addView(m_UnityPlayer, 0, lp);
        m_UnityPlayer.windowFocusChanged(true);
        m_UnityPlayer.resume();
    }
    @Override
    public void onWindowFocusChanged(boolean hasFocus)
    {
        super.onWindowFocusChanged(hasFocus);
        m_UnityPlayer.windowFocusChanged(hasFocus);
    }
    @Override
    public void onPause() {
         super.onPause();  
         m_UnityPlayer.pause();
    }
    @Override
    public void onResume() {
        super.onResume(); 
        m_UnityPlayer.resume();
    }
Run Code Online (Sandbox Code Playgroud)

这是清单中描述活动的方式../AndroidManifest.xml:

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name="com.package.example.MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity 
        android:name="com.package.example.UnityActivity" 
        android:label="@string/title_activity_unity" 
        android:screenOrientation="portrait" 
        android:launchMode="singleTask" 
        android:parentActivityName="com.package.example.MainActivity"
        android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale">
      <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
      <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />
    </activity>
</application>
Run Code Online (Sandbox Code Playgroud)

这就是如何定义UnityActivity的布局../res/layout/activity_unity.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.package.example.UnityActivity"
tools:ignore="MergeRootFrame" >
    <FrameLayout
    android:id="@+id/unityView"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    </FrameLayout>
</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

我会感谢任何提示和解决方案指向我正确的方向.

Ste*_*ann 28

好的,首先是简单的事情

W/libc(21095): pthread_create sched_setscheduler call failed: Operation not permitted
Run Code Online (Sandbox Code Playgroud)

你无能为力.当你直接从Unity for Android编译时,你甚至会得到这个,所以这是引擎内部的一个问题.

基本设置

您链接的指南已经过时了.您不再需要从各个位置复制文件来创建简单的Android项目.

  1. 通过设置创建Android项目 Build Settings -> Android -> Google Android project
  2. 您现在有一个完整的包可以导入到Eclipse或Android Studio中
  3. 编译和部署

在子活动中使用UnityPlayer

UnityPlayerNativeActivity新Android项目中的课程向您展示如何设置UnityPlayer以及需要转发的事件.这是Unity 4.3.4使用的版本

package de.leosori.NativeAndroid;

import com.unity3d.player.*;
import android.app.NativeActivity;
import android.content.res.Configuration;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

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

    // UnityPlayer.init() should be called before attaching the view to a layout - it will load the native code.
    // UnityPlayer.quit() should be the last thing called - it will unload the native code.
    protected void onCreate (Bundle savedInstanceState)
    {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        super.onCreate(savedInstanceState);

        getWindow().takeSurface(null);
        setTheme(android.R.style.Theme_NoTitleBar_Fullscreen);
        getWindow().setFormat(PixelFormat.RGB_565);

        mUnityPlayer = new UnityPlayer(this);
        if (mUnityPlayer.getSettings ().getBoolean ("hide_status_bar", true))
            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);

        View playerView = mUnityPlayer.getView();
        setContentView(playerView);
        playerView.requestFocus();
    }
    protected void onDestroy ()
    {
        mUnityPlayer.quit();
        super.onDestroy();
    }

    // onPause()/onResume() must be sent to UnityPlayer to enable pause and resource recreation on resume.
    protected void onPause()
    {
        super.onPause();
        mUnityPlayer.pause();
    }
    protected void onResume()
    {
        super.onResume();
        mUnityPlayer.resume();
    }
    public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
        mUnityPlayer.configurationChanged(newConfig);
    }
    public void onWindowFocusChanged(boolean hasFocus)
    {
        super.onWindowFocusChanged(hasFocus);
        mUnityPlayer.windowFocusChanged(hasFocus);
    }
    public boolean dispatchKeyEvent(KeyEvent event)
    {
        if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
            return mUnityPlayer.onKeyMultiple(event.getKeyCode(), event.getRepeatCount(), event);
        return super.dispatchKeyEvent(event);
    }
}
Run Code Online (Sandbox Code Playgroud)

虽然我可以说UnityPlayerNativeActivity延伸NativeActivity你仍然可以延伸ActionBarActivity而不是任何问题.至少它在我的实验中起作用了.

你缺少的最重要的部分是对mUnityPlayer.quit()期间的呼唤onDestroy().试图创建一个新的实例,UnityPlayer而旧的实例仍在运行将导致崩溃,悬挂活动和无尽的痛苦.

mUnityPlayer.quit()的意外行为

修复你可能会感到惊讶,现在你的整个应用程序只是在你从你的回来时关闭UnityActivity.mUnityPlayer.quit()将杀死它在里面运行的进程.调用后没有一个方法会执行mUnityPlayer.quit(),甚至onDestroy()方法都不会完成.

获胜的途径是UnityActivity通过将参数添加android:process=":UnityKillsMe到您的活动中来启动您的新流程AndroidManifest.xml.

在你的情况下,它看起来像这样

<activity 
    android:name="com.package.example.UnityActivity" 
    android:label="@string/title_activity_unity" 
    android:screenOrientation="portrait" 
    android:launchMode="singleTask" 
    android:process=":UnityKillsMe"
    android:parentActivityName="com.package.example.MainActivity"
    android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale">
    <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
    <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="false" />
</activity>
Run Code Online (Sandbox Code Playgroud)

我不确定参数unityplayer.ForwardNativeEventsToDalvik...在开头创建的项目设置它false官方(过时的)文档提到

由于触摸/运动事件是在本机代码中处理的,因此Java视图通常不会看到这些事件.但是,Unity中存在一种转发机制,允许将事件传播到DalvikVM.

在我的小示例项目中,我看不出有什么区别

前方的路

您需要找到一个工作流程,将您的开发与Unity集成到Android项目中,反之亦然.使用Unity再次导出会与您在Android项目中所做的更改发生冲突,因此您需要导出到单独的文件夹并从Android项目链接到Unity部分.

根据上述文档,您可以将已编译的Android类和AndroidManifest.xml插件集成到Unity中.

生成的.class文件应压缩为.jar文件并放在Assets-> Plugins-> Android文件夹中.由于清单指示要启动哪个活动,因此还需要创建新的AndroidManifest.xml.AndroidManifest.xml文件也应该放在Assets-> Plugins-> Android文件夹中.

祝好运!


Dav*_*vid 7

问题是两年前,但我仍然很难找到有关它的详细指南.

所以我写了一个

主要的想法是采用Unity项目并将其用作本机Android应用程序中的库.

我希望本指南能够帮到某些人.