将Unity3D场景导入到Android片段中

4

我在网上搜索了类似的问题,虽然找到了一些相似的问题,但没有可靠的答案,这就是我在这里发布的原因。

当Unity3D项目编译为Android时,它基本上只是将场景作为一个Activity提供,您可以启动/结束等。我想将此Activity更改为Fragment,以便在导航抽屉中显示它作为选项卡,并在另一个fragment/activity的子视图中显示它。

所以基本上我有一个带有打开按钮的MainActivity,和一个实际的Unity3D项目UnityPlayerNativeActivity

我搜索了如何将通用活动更改为片段,并更改了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>

我只包括了这两个文件,因为我认为问题可能来自其中一个。完整的代码可以在这里下载。将其解压缩/导入到Android Studio中-您可能需要按一次Sync Project with Gradle Files才能使其正常工作。
该项目的其余部分基于Android Studio在创建新项目时自动创建的Navigation Drawer Activity项目。当按下相应图标时,我基本上只调用上述UnityPlayerNativeFragmentnewInstance来实现它在NavigationDrawer中的呈现。
当我按场景选项卡时会出现什么结果:一个黑屏,Action Bar仍然显示,但其他什么也没有。右上角的“设置”按钮仍然可按,Navigation Drawer按钮也是如此,但是当您按下Navigation Drawer上的另一个选项卡时,整个应用程序都会冻结,您必须强制退出。我认为后者的问题是因为您无法关闭未正确打开的UnityPlayer,因此主要问题实际上是在Fragment内正确打开UnityPlayer。应用程序的其余部分(其他选项卡)工作正常。我在上述代码和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上。


你成功了吗? - Cigogne Eveillée
不,出于各种原因,我最终使用了与Unity完全不同的东西。 - Jimmy
2个回答

2

尝试这种方法,它在我的端上有效。

1.在Activity中创建UnityPlayer。

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

2.关于Fragment

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

 


1

我成功地将它与上面提供的帮助和这个链接https://forum.unity.com/threads/unity3d-in-fragment.327303/集成到了一个片段中。

在活动中拥有标准方法是必不可少的。

更详细的内容如下:

  1. 在持有片段的活动(MyActivity)中:
  • 声明变量UnityPlayer mUnityPlayer;

  • 这将触发导入import com.unity3d.player.UnityPlayer;

  • 添加以下所有重写代码

    //区域生命周期方法
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      mUnityPlayer = new UnityPlayer(this);
    }
    
    /** FOR UNITY **/
    @Override
    protected void onPause() {
      super.onPause();
      mUnityPlayer.pause();
    }
    
    //恢复Unity
    @Override protected void onResume()
    {
      super.onResume();
      mUnityPlayer.resume();
    }
    
    //低内存Unity
    @Override public void onLowMemory()
    {
      super.onLowMemory();
      mUnityPlayer.lowMemory();
    }
    
    //修剪内存Unity
    @Override public void onTrimMemory(int level)
    {
      super.onTrimMemory(level);
      if (level == TRIM_MEMORY_RUNNING_CRITICAL)
      {
          mUnityPlayer.lowMemory();
      }
    }
    
    //这确保布局将正确。
    @Override public void onConfigurationChanged(Configuration newConfig)
    {
      super.onConfigurationChanged(newConfig);
      mUnityPlayer.configurationChanged(newConfig);
    }
    
    //通知Unity焦点变化。
    @Override public void onWindowFocusChanged(boolean hasFocus)
    {
      super.onWindowFocusChanged(hasFocus);
      mUnityPlayer.windowFocusChanged(hasFocus);
    }
    
    //由于某种原因,ndk不支持多个keyevent类型。
    //通过覆盖dispatchKeyEvent()来强制事件注入。
    @Override public boolean dispatchKeyEvent(KeyEvent event)
    {
      if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
          return mUnityPlayer.injectEvent(event);
      return super.dispatchKeyEvent(event);
    }
    
    //将任何未处理的事件直接传递给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();
    }
    
  • 在这个片段中:

    • 变量声明: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");
      }
      

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接