将Unity3D场景导入Android片段

时间:2014-11-11 23:34:25

标签: java android android-fragments unity3d

我在网上搜索了类似的问题,虽然我发现了几个类似的问题,但没有一个有可靠的答案,这就是我在这里发帖的原因。

当为Android编译Unity3D项目时,它基本上只是将场景作为活动开始/结束/等。我想将此Activity更改为Fragment,以便将其显示为导航抽屉内的选项卡,并将其显示为另一个片段/活动内的子视图。

所以基本上我有一个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>

我在这里只包含了这两个文件,因为我认为问题来自其中一个。完整代码可下载here。提取/导入到Android Studio中 - 您可能需要按Sync Project with Gradle Files一次才能生效。

项目的其余部分基于Android Studio在创建新项目时自动为您创建的Navigation Drawer Activity项目。我基本上只是在newInstance中按下相应的图标时调用上述UnityPlayerNativeFragment NavigationDrawer

当我按下场景标签时会出现什么结果:黑屏,操作栏显示静止,但没有别的。右上角的“设置”按钮仍然是可按下的,导航抽屉按钮也是如此,但是当按下导航抽屉上的另一个选项卡时,整个应用程序会冻结,您必须强制退出。我认为后一个问题是因为你无法关闭一个没有正确打开的UnityPlayer,所以主要的问题是实际上在UnityPlayer内部正确地打开了AndroidManifest.xml。应用程序的其余部分(其他选项卡)工作正常。我在上面的代码和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. 文件中尝试了几种变体(我能想到的一切),但没有任何效果。

Logcat没有显示任何错误,只是说:

{{1}}

这到底出了什么问题?目标版本适用于Android 5.0,我使用的是Android Studio 0.8.14。 值得注意的是:当它是一个活动时,我能够成功地将场景加载到我的Nexus 5上。

2 个答案:

答案 0 :(得分:1)

尝试这种方式,它可以在我这边工作。

1.在活动中创建UnityPlayer

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

2.On 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 :(得分:0)

借助上面提供的帮助以及以下链接,我设法将其集成到一个片段中:https://forum.unity.com/threads/unity3d-in-fragment.327303/

必须在活动中使用标准方法。

下面详细说明:

  1. 在保存片段的活动中(“ MyActivity”):

    • 声明变量UnityPlayer mUnityPlayer;
    • 这将触发导入“ import com.unity3d.player.UnityPlayer;”
    • 在代码中添加以下所有替代内容

    //区域生命周期方法 @Override 受保护的void onCreate(@Nullable Bundle savedInstanceState){     super.onCreate(savedInstanceState);     mUnityPlayer =新的UnityPlayer(this); }

    / **团结** / @Override 受保护的void onPause(){     super.onPause();     mUnityPlayer.pause(); }

    //恢复团结 @Override受保护的void onResume() {     super.onResume();     mUnityPlayer.resume(); }

    //低内存统一 @Override public void onLowMemory() {     super.onLowMemory();     mUnityPlayer.lowMemory(); }

    //修剪内存统一 @Override public void onTrimMemory(int level) {     super.onTrimMemory(级别);     如果(级别== 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不支持多键事件类型。 //通过重写dispatchKeyEvent()强制事件注入。 @Override public boolean dispatchKeyEvent(KeyEvent event) {     如果(event.getAction()== KeyEvent.ACTION_MULTIPLE)         返回mUnityPlayer.injectEvent(event);     返回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 受保护的void onDestroy(){     super.onDestroy();     mUnityPlayer.quit(); }

  2. 在片段中:

    • 变量声明private MyActivity myActivity;
    • 变量声明:私有UnityPlayer unityPlayer;
    • 这将触发“导入com.unity3d.player.UnityPlayer;”
    • 下面的代码

    @可为空 @Override 公共视图onCreateView(@NonNull LayoutInflater充气机,@ Nullable ViewGroup容器,@ Nullable捆绑包saveInstanceState){ myActivity =(MyActivity)getActivity(); 查看unityPlayViewer = calibActivity.mUnityPlayer.getView(); 新的Handler()。postDelayed(()-> initialize(),5000); 返回unityPlayViewer; }

    private void initialize(){ calibActivity.mUnityPlayer.UnitySendMessage(“您对Unity的命令开始播放”); }