簡體   English   中英

將 Unity3D 場景導入 Android 片段

[英]Importing a Unity3D Scene into an Android fragment

我在網上搜索過類似的問題,雖然我發現了幾個類似的問題,但沒有一個可靠的答案,這就是我在這里發帖的原因。

當Unity3D項目為Android編譯時,它基本上只是將場景作為一個你可以開始/結束/等的Activity。 我想將此活動更改為片段,以便將其顯示為導航抽屜中的選項卡,以及另一個片段/活動中的子視圖。

所以基本上我有一個帶有打開按鈕的MainActivity和一個UnityPlayerNativeActivity ,它是實際的 Unity3D 項目。

我搜索了如何將一般活動更改為片段,並將UnityPlayerNativeActivity更改為匹配。 例如,在下面新命名的UnityPlayerNativeFragment中(注釋反映了與之前的確切更改):

import com.unity3d.player.UnityPlayer;
// Other imports available in full code linked to below

public class UnityPlayerNativeFragment extends Fragment
{
    // Changes in this class:
    // 1- 'this' references changed to "getActivity()"
    // 2- onCreate -> onCreateView
    // 3- protected -> public in function names
    // 4- @Override added before function calls
    // 5- newInstance and onAttach added

    protected UnityPlayer mUnityPlayer;     // don't change the name of this variable; referenced from native code
    private static final String ARG_SECTION_NUMBER = "section_number";

    public static UnityPlayerNativeFragment newInstance(int sectionNumber) {
        UnityPlayerNativeFragment fragment = new UnityPlayerNativeFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }


    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        ((HomeActivity) activity).onSectionAttached(
                getArguments().getInt(ARG_SECTION_NUMBER));
    }

    // 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.

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState)
    {
        //below line removed because it was causing errors
        //getActivity().requestWindowFeature(Window.FEATURE_NO_TITLE);

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

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

    @Override
    public void onDestroy ()
    {
        mUnityPlayer.quit();
        super.onDestroy();
    }

    // onPause()/onResume() must be sent to UnityPlayer to enable pause and resource recreation on resume.
    @Override
    public void onPause()
    {
        super.onPause();
        mUnityPlayer.pause();
    }

    @Override
    public void onResume()
    {
        super.onResume();
        mUnityPlayer.resume();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
        mUnityPlayer.configurationChanged(newConfig);
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.SamerBekhazi.Test" android:versionName="1.0" android:versionCode="1" android:installLocation="preferExternal">
  <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
  <application android:theme="@style/AppTheme" android:icon="@drawable/app_icon" android:label="@string/app_name">
    <activity android:launchMode="singleTask" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="portrait" android:name=".HomeActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="false" />
    </activity>
  </application>
  <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" />
  <uses-feature android:glEsVersion="0x00020000" />
</manifest>

我只在這里包含了這 2 個文件,因為我認為問題出在其中一個。 完整代碼可在此處下載。 提取/導入到 Android Studio - 您可能需要按一次Sync Project with Gradle Files才能使其工作。

該項目的其余部分基於 Android Studio 在創建新項目時自動為您創建的Navigation Drawer Activity項目。 我基本上只是在NavigationDrawer按下其相應的圖標時調用上述UnityPlayerNativeFragmentnewInstance

當我按下場景的選項卡時會產生什么結果:黑屏,操作欄仍然顯示,但沒有其他內容。 右上角的“設置”按鈕仍然可以按下,導航抽屜按鈕也是如此,但是當按下導航抽屜上的另一個選項卡時,整個應用程序會凍結,您必須強制退出。 我認為后一個問題是因為您無法關閉未正確打開的UnityPlayer ,因此主要問題實際上是使UnityPlayer在 Fragment 中正確打開。 應用程序的其余部分(其他選項卡)工作正常。 我在上面的代碼和AndroidManifest.xml文件中嘗試了幾種變體(我能想到的一切),但沒有任何效果。

Logcat 沒有顯示任何錯誤,它只是說:

11-11 21:22:19.681  29280-29280/com.Bekhazi.Bouncy_Ball W/linker﹕ libmain.so has text relocations. This is wasting memory and is a security risk. Please fix.
11-11 21:22:19.681  29280-29280/com.Bekhazi.Bouncy_Ball D/dalvikvm﹕ Added shared lib /data/app-lib/com.Bekhazi.Bouncy_Ball-2/libmain.so 0x42d64cd8
11-11 21:22:19.691  29280-29280/com.Bekhazi.Bouncy_Ball W/linker﹕ libmono.so has text relocations. This is wasting memory and is a security risk. Please fix.
11-11 21:22:19.691  29280-29280/com.Bekhazi.Bouncy_Ball W/linker﹕ libunity.so has text relocations. This is wasting memory and is a security risk. Please fix.

這里究竟有什么問題? 目標版本適用於 Android 5.0,我使用的是 Android Studio 0.8.14。 值得注意的是:當它是一個 Activity 時,我能夠成功地將場景加載到我的 Nexus 5 上。

試試這種方式,它在我身邊工作。

1.在Activity創建UnityPlayer

    private UnityPlayer mUnityPlayer;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mUnityPlayer = new UnityPlayer(this);

2.在片段上

 private MyActivity mUnityMainActivity; private UnityPlayer mUnityPlayer; View playerView;    @Override    public View onCreateView(LayoutInflater inflater,            @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {        // TODO Auto-generated method stub mUnityMainActivity = (MyActivity) getActivity(); mUnityPlayer = mUnityMainActivity.GetUnityPlayer();        playerView = mUnityPlayer.getView();        LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);        playerView.setLayoutParams(lp);        if(playerView.getParent() != null){            ((ViewGroup)playerView.getParent()).removeAllViews();        }        return playerView;    }

我設法在上面提供的幫助和此鏈接的幫助下將其集成到片段中: https : //forum.unity.com/threads/unity3d-in-fragment.327303/

在活動中使用標准方法是必不可少的。

更詳細的如下:

  1. 在保存片段的活動 ( MyActivity ) 中:
  • 聲明變量 UnityPlayer mUnityPlayer;

  • 這將觸發 import import com.unity3d.player.UnityPlayer;

  • 在下面添加所有覆蓋代碼

    //region Lifecycle methods @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mUnityPlayer = new UnityPlayer(this); } /** FOR UNITY **/ @Override protected void onPause() { super.onPause(); mUnityPlayer.pause(); } // Resume Unity @Override protected void onResume() { super.onResume(); mUnityPlayer.resume(); } // Low Memory Unity @Override public void onLowMemory() { super.onLowMemory(); mUnityPlayer.lowMemory(); } // Trim Memory Unity @Override public void onTrimMemory(int level) { super.onTrimMemory(level); if (level == TRIM_MEMORY_RUNNING_CRITICAL) { mUnityPlayer.lowMemory(); } } // This ensures the layout will be correct. @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mUnityPlayer.configurationChanged(newConfig); } // Notify Unity of the focus change. @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); mUnityPlayer.windowFocusChanged(hasFocus); } // For some reason the multiple keyevent type is not supported by the ndk. // Force event injection by overriding dispatchKeyEvent(). @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_MULTIPLE) return mUnityPlayer.injectEvent(event); return super.dispatchKeyEvent(event); } // Pass any events not handled by (unfocused) views straight to UnityPlayer @Override public boolean onKeyUp(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); } /*API12*/ public boolean onGenericMotionEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); } @Override protected void onDestroy() { super.onDestroy(); mUnityPlayer.quit(); }
  1. 在片段中:
  • 變量聲明 private MyActivity myActivity;

  • 變量聲明:private UnityPlayer unityPlayer;

  • 這將觸發“import com.unity3d.player.UnityPlayer;”

  • 下面的代碼

    @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { myActivity = (MyActivity) getActivity(); View unityPlayViewer = calibActivity.mUnityPlayer.getView(); new Handler().postDelayed(() -> initialize(), 5000); return unityPlayViewer; } private void initialize() { calibActivity.mUnityPlayer.UnitySendMessage("your commands to Unity to start playing"); }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM