web-dev-qa-db-ja.com

ネイティブAndroidアプリケーションでのUnityPlayerライフサイクルの管理エラー

次のフォーラム投稿のコードをガイドとして使用して、AndroidアプリでUnityPlayerインスタンスをアクティブにロードする必要があるアプリに取り組んでいます:

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

最初、アプリケーションは「UnityActivity.Java」と呼ばれるアクティビティ内にUnityPlayerを正しく表示しています。

この問題は、ユーザーが(ハードウェアの戻るボタンを押すか、ActionBarの戻るボタンをクリックして)MainActivityに戻ってから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に2回目に移動したときに記録されます。

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>

正しい方向に向けるヒントや解決策に感謝します。

17
IAE

はい、簡単なことから

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

それについてあなたができることは何もありません。 Unity for Androidから直接コンパイルした場合でもこれが発生するため、エンジン内の問題です。

基本セットアップ

リンクしたガイドはかなり古くなっています。単純なAndroidプロジェクトを作成するために、さまざまな場所からファイルをコピーする必要がなくなりました。

  1. _Build Settings -> Android -> Google Android project_を設定してAndroidプロジェクトを作成します
  2. これで、EclipseまたはAndroid St​​udioにインポートできる完全なパッケージが完成しました。
  3. コンパイルしてデプロイ

サブアクティビティでUnityPlayerを使用する

新しいAndroidプロジェクトのクラスUnityPlayerNativeActivityは、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);
    }
}
_

UnityPlayerNativeActivityNativeActivityを拡張しますが、私の知る限り、問題なくActionBarActivityから拡張することもできます。少なくともそれは私の実験中にはうまくいった。

欠落している最も重要な部分は、mUnityPlayer.quit()中のonDestroy()の呼び出しです。古いインスタンスがまだ実行されているときにUnityPlayerの新しいインスタンスを作成しようとすると、クラッシュ、ハングしたアクティビティ、無限の苦しみが発生します。

MUnityPlayer.quit()の予期しない動作

UnityActivityから戻ったときにアプリ全体が単純に閉じることに驚かれる可能性があることを修正します。 mUnityPlayer.quit()は、内部で実行されているプロセスを強制終了します。 mUnityPlayer.quit()を呼び出した後に単一のメソッドが実行されることはなく、onDestroy()メソッドも終了しません。

勝利への道は、_Android:process=":UnityKillsMe_内のアクティビティにパラメーター _AndroidManifest.xml_ を追加して、UnityActivityを新しいプロセスとして開始することです。

あなたの場合は次のようになります

_<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プロジェクトで行った変更と競合するため、別のフォルダーにエクスポートし、AndroidプロジェクトからUnityパーツにリンクする必要があります。

前述の ドキュメント によると、コンパイル済みのAndroidクラスと_AndroidManifest.xml_をプラグインとしてUnityに統合できる場合があります。

結果の.classファイルは.jarファイルに圧縮され、Assets-> Plugins-> Androidフォルダーに配置されます。マニフェストは起動するアクティビティを指示するため、新しいAndroidManifest.xmlを作成することも必要です。 AndroidManifest.xmlファイルも、Assets-> Plugins-> Androidフォルダーに配置する必要があります。

幸運を!

28
Stefan Hoffmann

質問は2年前ですが、それについての詳細なガイドを見つけるのに苦労しました。

だから私が書いたもの

主なアイデアは、Unityプロジェクトを取得し、それをネイティブのライブラリとして使用することですAndroid app。

このガイドがお役に立てば幸いです。

7
David

よく、私はこれに対する明確な答えはありませんが、いくつかの興味深いpsotsを見つけました

1- Androidでのpthread_create警告 について話しますW/libc(21095):pthread_create sched_setscheduler呼び出しが失敗しました:操作は許可されていません投稿で始まりますpthread_create関数を呼び出した後、次のメッセージを受け取ります:私は投稿から引用します:

このエラーは、スレッドを作成しようとしているプロセスに、指定されたスケジューリング優先順位を設定するための適切な特権がないことを意味します。

回答としてマークされた投稿には、良い情報がいくつかあります。そこを見てくださいが、コードではpthread_create()を呼び出していません-[1]

2- LogcatでのChoreographerメッセージの意味W/Choreographer(20963):保留中のvsyncイベントがすでにあります。一度に1つだけあるべきです私は引用します

Choreographerを使用すると、アプリは自身をvsyncに接続し、適切なタイミングでパフォーマンスを改善できます。

そして

はい、そうです。 Choreographerはおそらくアニメーションを処理するコンポーネントであり、十分なCPUサイクルを取得できない場合、いくつかのフレームをスキップしてこのデバッグメッセージを出力するので、これはUIのアニメーションに関連しています--- [2]

そう? [1]と[2]に基づく:Unityによって生成されたアニメーションの問題であり、ユーザーの制御下にはありません。

私自身の結論と意見は:これはUnityコードのバグであり、Unityで修正する必要がありますか?多分?

申し訳ありませんが、役に立たない場合は試してみました。 Unityで作業したことはありませんが、他の投稿から結論を得ようとしました。

幸運を

1
Yazan

あなたはこのリンクを見ることができます

nity3dビューをAndroid activity に統合します

パブリッククラスUnityPlayerNativeActivityはNativeActivityを拡張します{protected 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);
}

}

0
Sam