Подтвердить что ты не робот

Ошибки, управляющие жизненным циклом UnityPlayer в приложении для собственных приложений Android

Я работаю над андроидным приложением, которое должно загрузить экземпляр UnityPlayer в activiy, используя код из следующего сообщения форума в качестве руководства:

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

Первоначально приложение корректно отображает UnityPlayer внутри действия под названием "UnityActivity.java".

Проблема начинается, когда пользователь переходит к 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);
}

Это выдержка из активности 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();
    }

Вот как описываются действия в манифесте ../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>

Так определяется формат 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>

Буду благодарен за любые советы и решения, указывающие на меня в правильном направлении.

4b9b3361

Ответ 1

Хорошо, сначала легкие вещи

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

Вы ничего не можете с этим поделать. Вы даже получаете это, когда компилируете непосредственно из Unity для Android, так что это проблема внутри движка.

Основная настройка

Указанное вами руководство довольно устарело. Вам больше не нужно копировать файлы из разных мест, чтобы создать простой проект для Android.

  • Создайте проект Android, установив Build Settings -> Android -> Google Android project
  • Теперь у вас есть готовый пакет для импорта в Eclipse или Android Studio
  • Скомпилировать и развернуть

Использование 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);
    }
}

Хотя UnityPlayerNativeActivity extends 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>

Я не уверен в параметре unityplayer.ForwardNativeEventsToDalvik... Проект, созданный в начале, устанавливает его в false, а официальный официальный (устаревший) документация упоминает

Так как события касания/движения обрабатываются в собственном коде, просмотры Java обычно не видят эти события. Однако в Unity существует механизм пересылки, который позволяет распространять события на DalvikVM.

В моем маленьком примере проекта я не видел разницы

Дорога впереди

Вам нужно найти рабочий процесс для интеграции вашей разработки с Unity с проектом Android или наоборот. Экспортирование снова с Unity противоречило бы изменениям, внесенным вами в проект Android, поэтому вам нужно будет экспортировать в отдельную папку и ссылку на часть Unity из вашего проекта Android.

В соответствии с вышеупомянутым documentation вы можете интегрировать свои скомпилированные классы Android и AndroidManifest.xml в качестве плагинов в Unity.

Полученные файлы .class должны быть сжаты в файл .jar и помещены в папку Assets- > Plugins- > Android. Поскольку манифест диктует, какую активность нужно запустить, также необходимо создать новый AndroidManifest.xml. Файл AndroidManifest.xml также должен быть помещен в папку Assets- > Plugins- > Android.

Удачи!

Ответ 2

Вопрос два года назад, но я все еще пытался найти подробное руководство по этому вопросу.

Итак, я написал один

Основная идея - взять проект Unity и использовать его как библиотеку в собственном приложении для Android.

Я надеюсь, что это руководство поможет кому-то.

Ответ 3

ну, у меня нет четкого ответа на этот вопрос но я нашел пару интересных псотов

1- предупреждение pthread_create на Android. говорит о W/libc (21095): вызов pthread_create sched_setscheduler не выполнен: операция не разрешена сообщение начинается с После вызова функции pthread_create я получаю следующее сообщение: я цитирую сообщение:

Эта ошибка означает, что процесс, пытающийся создать поток, не соответствующие привилегии для установки приоритета планирования, как указано.

и сообщение, помеченное как ответ, имеет некоторую хорошую информацию. взгляните туда но на вашем коде вы не вызываете pthread_create() - [1]

2- Значение сообщений хореографа в Logcat W/Хореограф (20963): Уже есть ожидающее событие vsync. Должно быть только одно за раз я цитирую

Хореограф позволяет приложениям подключаться к vsync, и правильно время для улучшения производительности.

и

Да, я. Я понимаю, что Хореограф, вероятно, является компонентом который обрабатывает анимацию, и когда он не получает достаточного количества циклов процессора, он пропускает некоторые кадры и выводит это сообщение отладки поэтому это связано с анимацией в пользовательском интерфейсе --- [2]

так? основанные на [1] и [2]: проблема с анимацией, созданной Unity, и его не под вашим контролем,

мой собственный вывод и мнение таковы: это ошибка в коде Unity, которая должна быть исправлена ​​Unity? может быть?

Извините, если не помогло, но я пробовал. я никогда не работал с Unity, но я попытался получить заключение из некоторых других сообщений.

удача

Ответ 4

Вы можете увидеть эту ссылку

Интегрируйте представление Unity3d в действие Android

открытый класс UnityPlayerNativeActivity расширяет NativeActivity {   защищенный UnityPlayer mUnityPlayer;//не изменяем имя этой переменной; ссылка на собственный код

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

}