Unity工程导入Android工程


文章目录

  • 1.前言
  • 2.集成到原生Android步骤
    • 2.1 导出Android Module
    • 2.2 导入原生Android工程
  • 3.Unity画面集成与显示
    • 3.1 以Activity形式集成
    • 3.2 桌面局部显示
    • 3.3 添加原生Android组件
  • 4.问题处理
    • 4.1 编译时错误
    • 4.2 桌面显示多个ICON问题
  • 5.Limitations
  • 6.总结

完整的demo。本文基于使用经验对其做进一步的解释,并提供一些必坑方式。

2.集成到原生Android步骤

2.1 导出Android Module

在unity中如下图所示,在PlayerSettings界面勾选Export Project然后运行即可到处Android工程。如果AndroidStudio比较新,可能需要在OtherSettings->Configuration->ScriptingBackend选项选择IL2Cpp,然后在targetArchitecrue中勾选Arm64。以unity的demo中所示,到处的模块最外层目录为androidBuild,里面包含所需的文件,其中可用的主要为unityLibrary这个目录下的两个目录。

2.2 导入原生Android工程

这个步骤稍微多一点,即如何在android工程中引入外部独立module的方法。步骤如下:

1)引入模块
在原生工程中找到setting.gradle文件,然后添加:

include ':unityLibrary'
project(':unityLibrary').projectDir=new File('..\\UnityProject\\androidBuild\\unityLibrary')

由于一个AndroidStudio工程包含很多的子项目,上述命令就是告诉此工程要引入一个叫unityLibrary的模块(子工程),此工程的目录为…\UnityProject\androidBuild\unityLibrary,即unity导出的模块中的unityLibrary目录。下图为unity官方demo的截图,表示此工程由两个项目,一个是app,即原生的Android工程,另一个则是引入的unity模块。
请添加图片描述此时点击SyncNow就会同步工程,结束后此moudule就会存在工程中(此处不用同步,可以改完后同一同步)。

2)工程中引入jar包
在工程中找到build.gradle(project:NativeAndroidApp),在allprojects/repositories节点下添加如下指令。

flatDir {
  dirs "${project(':unityLibrary').projectDir}/libs"
}

请添加图片描述
3)添加依赖到编译路径
在工程中找到build.gradle(module:NativeAndroidApp.app),在dependencies 节点中添加如下指令:

implementation project(':unityLibrary')
implementation fileTree(dir: project(':unityLibrary').getProjectDir().toString() + ('\\libs'), include: ['*.jar'])

请添加图片描述4)同步工程
由于修改了gradle,所以会在右上角提示同步工程“SyncNow",然后点击即可同步,如果顺利就集成到工程中了。
请添加图片描述

3.Unity画面集成与显示

上述只是将unity模块引入到Android工程中,此段则是如何使用。此部分需要有一定的android知识,就像unity此demo的github工程中所述。

3.1 以Activity形式集成

集成步骤:
1)创建基于Unity的Activity,unity提供的工程中提供了MainUnityActivity,它继承自OverrideUnityActivity。OverrideUnityActivity跟unity默认的activity相同,即将UnityPlayer的画面通过setContentView送显。
建议直接使用MainUnityActivity,因为它里面提供了一个showMainActivity方法用来处理一些特殊情况下的异常。
部分代码:

public class MainUnityActivity extends OverrideUnityActivity {
    // Setup activity layout
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addControlsToUnityFrame();
        Intent intent = getIntent();
        handleIntent(intent);
    }

2)添加1)中创建的activity到manifest文件。安卓开发需要在manifest中声明,并设置一些属性。以unity的demo为例,其unity相关的activity叫MainUnityActivity:

<?xml version="1.0" encoding="utf-8"?>
xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity.mynativeapp" >

    android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar" >
            >
                android:name="android.intent.action.MAIN" />
                android:name="android.intent.category.LAUNCHER" />
            >
        >

        android:label="@string/app_name"
            android:name="com.unity.mynativeapp.MainUnityActivity"
            android:screenOrientation="fullSensor"
            android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
            android:hardwareAccelerated="false"
            android:process=":Unity">
        >
    >

>

注:在MainUnityActivity的标签中有一个process=”:Unity"属性,表示在其他进程中开启unity模块,去掉则是依附于主进程。建议采用默认的方式,在独立进程中显示。

3.2 桌面局部显示

将unity渲染画面显示在很小的范围内或者一个view或者Layout中。此功能unity是不推荐的,具体可以看Limitations部分。 但是我们根据实现来将unity画面塞到一个Layout中。在3.1部分讲到,unity是将UnityPlayer的一个实例作为画面的,UnityPlayer是一个Framelayout,所以就可以将其addView到其他layout中。至此可以实现将unity画面显示在一个组件中,也就是可以非全屏显示。
但是要处理好UnityPlayer的声明周期。在3.1中UnityPlayer的生命周期是跟随activity的生命周期的,所以比较好处理,但是如果作为一个普通的画面显示,那么需要开发者做好管控。比如当显示此画面时,调用Start或者Resume,不需要显示时调用Destroy或者Pause等。还有在部分机器上可能要依附于主进程,有的则只能在独立进程中才可以实现此功能。

3.3 添加原生Android组件

由于Unity的画面是一个FrameLayout,也就意味着可以通过addView将原生android组件叠加到unity画面上。如在unity画面上添加一个原生的android Button,怎可以如下处理:

        FrameLayout layout = mUnityPlayer;
  
            Button myButton = new Button(this);
            myButton.setText("Show Main");
            myButton.setX(10);
            myButton.setY(500);

            myButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                   showMainActivity("");
                }
            });
            layout.addView(myButton, 300, 200);

4.问题处理

在android原生工程中接入Unity画面时遇到两个问题,第一个是编译时提示资源问题,第二个是编译完成安装后桌面上显示两个icon。

4.1 编译时错误

编译时包错android.content.res.Resources$NotFoundException。此问题是由于unity在某些地方调用想要获取game_view_content_description这个资源导致,所以需要在main-.>res->values下找到strings.xml文件(没有则新建),然后添加如下

name="game_view_content_description">Game view>

如下所示,至于文字则不是重点,只要name对即可

    >
        name="app_name">NativeAndroidApp>
        name="action_settings">Settings>
        // Add Code
        name="game_view_content_description">Game view>
        // End
    >

4.2 桌面显示多个ICON问题

在导入的工程中(unityLibrary)中src-main下面找到AndroidManifest文件,打开将

            >
                android:name="android.intent.action.MAIN" />
                android:name="android.intent.category.LAUNCHER" />
            >

这几行注释掉即可,同时保证android主manifest文件,unity的activity中也没有上述代码,参考3.1中的manifest文件。

5.Limitations

While we tested many scenarios for Unity as a library hosted by a native app, Unity does not control anymore the lifecycle of the runtime, so we cannot guarantee it’ll work in all possible use cases. For example:

Unity as a Library supports rendering only full screen, rendering on a part of the screen isn’t supported.
Loading more than one instance of the Unity runtime isn’t supported.
You may need to adapt 3rd party Plug-ins (native or managed) to work properly
Overhead of having Unity in unloaded state is: 90Mb for Android and 110Mb for iOS

总之就是Unity提供了这个功能,但是会有各种问题,毕竟安卓手机太多了,还有各种厂商定制。所以根据多场景验证(tested many scenarios)以下情况不支持:
1)只支持全屏渲染,不支持渲染到部分屏幕(尽管3.2部分提供了实现方式)
2)不支持多个unity实例
3)第三方插件需要自己适配
4)不卸载情况,unity在android中大约占用90Mb(如果依附于主进程,即使卸载也没用),所以在unity的activity中有process=":Unity"属性。

6.总结

虽然2019提供了更直接的集成方法,但是有一些限制,尽量不要做Limitations里的事情。尽量使用demo中提供的方式,尽量使用MainUnityActivity,否则多机型复杂状况下会出各种问题。