文章目录
1.前言
在2018以及以前的版本中,unity到处android工程时是一个完整的AndroidStudio工程。从2019开始unity中到处工程是一个module,所以用起来也更加方便,且unity提供了完整的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"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity.mynativeapp" >
<application
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" >
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
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">
</activity>
</application>
</manifest>
注:在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文件(没有则新建),然后添加如下
<string name="game_view_content_description">Game view</string>
如下所示,至于文字则不是重点,只要name对即可
<resources>
<string name="app_name">NativeAndroidApp</string>
<string name="action_settings">Settings</string>
// Add Code
<string name="game_view_content_description">Game view</string>
// End
</resources>
4.2 桌面显示多个ICON问题
在导入的工程中(unityLibrary)中src-main下面找到AndroidManifest文件,打开将
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
这几行注释掉即可,同时保证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,否则多机型复杂状况下会出各种问题。
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/tech/272293.html