前言
在网上搜索 Unity 和 Android 的混合开发,一般可以查到,大致分为两种方案:
以 Unity 主导的开发,Android 提供 JAR 包或 AAR 包,导入到 Unity 作为插件,最后由 Unity 开发打包 APK;(对 Unity 技能要求高点)
以 Android 主导的开发,Unity 导出 Android 项目,再以 Module 方式或 AAR 方式引入到 Android 工程项目中,最后由 Android 开发打包 APK;(对Android 技能要求高点)
但是这两种方式都有不足的地方:
方式1,对 Android 开发人员不够友好,Android 工程,只能运行 Android 端的代码,不能同步 Unity 端代码,每次 Android 端修改代码都要导出包给 Unity 开发,通过 Unity 来调试;另外,如果有依赖第三方库,每次改完Android 工程的,还得修改 Unity 的 Android 插件配置的;(使用常规的方式,第三方库不支持打包进 aar)
同样,方式2,对 Unity 开发人员不够友好,Unity 端只能运行 Unity 代码,不能同步运行 Android 端代码,每次改完都要导包给 Android 端验证;
这两种方式,都增加了 Android 端和 Unity 端的开发测试成本(只能单方开发测试)和沟通成本(虽然导包给对方的流程可以直接通过 Git 上传包文件到对方的项目工程,但开发阶段,需要太频繁上传包来测试,频繁沟通也挺费时间的)。需要三个Git 工程:Unity 工程、Unity 导出的 Android 工程、Android 工程。
新方案的大致思路
那么,有没有一种更友好的混合开发方式,使两端开发都能够测试对方的功能,又可以减少沟通成本呢?
有!
- 以 Unity 导出的 Android 工程结构作为 Android 项目的基本结构,Unity 每次导出工程都是直接覆盖在 Android 工程上;(这样能做到 Unity 或 Android 端都能在各自开发平台上运行对方的代码)
- 将 Unity 上需要频繁修改的配置文件索引到 Android 工程中 (后续修改配置只需要改 Android 工程的配置,无需再修改两份配置)
- 只需要管理两个工程:Unity 工程和 Android 工程;
- 另外,为了减少 Unity 导出的 Android 工程中 unityLibrary module 频繁变化,导致提交 Git 的时候变化的文件太多( asset 中 unity 相关文件),还可以通过脚本将 unityLibrary 改成 AAR 来上传;
这种混合方式特别适合,Unity 端和 Android 端都需要频繁修改代码的情况,能够极大降低调试和沟通成本,而且使双端整个开发流程都比较顺畅。
操作流程
下面介绍大致的操作流程:
PS: 为了减少两个平台使用的 Gradle 和 JDK 版本兼容性问题,Unity 建议使用2019.4 长期稳定版本,Android Gradle 建议使用 4.0.1 (不要太高),并且 JDK 使用 1.8 的,下面的示例都是基于此版本(都是在 Mac 平台,Windows可能有点差异性),其他高版本不一定可行,或需要花更多时间来弄适配。
1.创建一个新的 Unity 工程:在File/Build Settings
切换到Android平台后,进入Player Settings
,在 Other Settings
修改包名、版本号和版本名、Minimum API和 Target API、Unity编译方式、ABI架构等信息。
再在Publishing Settings
上勾选使用自定义模板:
2.从 Unity 平台导出 Android 工程:选择 File/Build Settings,然后务必勾选 Export Project,在点击 Export (首次选择会引导选择导出的目录,后续会直接导出到上次选的目录),选择创建好的 Android 项目的目录
3.使用 Android Studio 打开导出的 Android 工程,并修改相关配置:
会提示是否使用 Android Studio (后面简称为 AS)的SDK,可以改为使用 AS 的 SDK;
打开工程后,会自动添加了
gradlew
和gradlew.bat
脚本;(后续编写的脚本会调用到,如果没有,可以拷贝其他 Android 项目的)在
gradle/wrapper/gradle-wrapper.perperties
修改 gradle 版本为 6.7.1,根目录下的 build.gradle 的插件版本改为 4.0.1 (笔者测试这两个版本的匹配性较好)编译运行通过后,分别将根目录和 launcher 下的
build.gradle
分别拷贝到 gradle 文件下,并重命名为 base.gradle和 launcher.gradle,原来的文件内容改为使用插件引入;再次编译运行保证配置无误;
4.修改 Unity 工程 Android 插件配置:
- 拷贝Android 工程下
settings.gradle
,并重命名为settingsTemplate.gradle
黏贴到 Unity 工程的 Assets/Plugins/Android 目录下(这是一个 Unity 隐藏的模板文件) - 进入 Assets/Plugins/Android 下分别将
baseProjectTemplate.gradle
和launcherTemplate.gradle
的内容改为 Android 工程根目录和 launcher 下的build.gradle
的内容,可以查看 UnitySample/HaloUnity/Assets/Plugins/Android/launcherTemplate - 后续,如果 Android 工程修改了
settings.gradle
、gradle.properties
,以及 launcher 下的 AndroidManifest.xml,要同时修改 Unity 里面的模板配置 - 删掉 AndroidManifest.xml 文件启动页 UnityPlayerActivity 的配置(也就是 unityLibrary 里面的,不然会有两个包名的启动页),建议不要使用 Unity SDK 里面提供的 UnityPlayerActivity,使用自己定义的;改完后,如下:
1
2
3
4
5
6
7
8
9
10
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.traffee.lovetigresse.verse"
xmlns:tools="http://schemas.android.com/tools">
<application>
</application>
</manifest> - 将启动页配置在 LauncherManifest.xml 中,或自己的业务 module;
- 改完后,再次导出工程到 Android 项目,并保证导出的工程能正常运行;
此时,基本配置就弄完了。但还有些地方需要优化,比如,如果 Unity 每次导出项目,需要同步代码给 Android的时候,基本上 unityLibrary module 下很多文件都会改变(特别是assets目录下,有很多unity的逻辑),为了避免 GIT 提交太多文件的变更,可以考虑将 unityLibrary module 改为 AAR 来提交(只需要提交一个文件)。
动态修改 unityLibrary 编译方式
首先,需要考虑的时,Unity 导出工程时,unityLibrary 是以 module 的形式存在的,但在 Android 开发时,unityLibrary 是以 AAR 形式存在的,所以需要动态修改
settings.gradle
的配置,并判断当前是处于什么状态去编译。
需要实现一个脚本来实现以下功能:
- 通过调用 gradlew 或 gradlew.bat 脚本将 unityLibrary module 编译生成 AAR;
- 将 AAR 拷贝到指定目录;
- 移除 unityLibrary module(最好将 unityLibrary 文件夹添加到忽略文件中):包含修改 settings.gradle 配置,以及删除 unityLibrary 文件夹(防止编译缓存影响到下次 Unity 导出工程时的编译结果);
- AAR 最好支持根据版本来命名,方便区分;
- 脚本最好可以传版本名参数作为 AAR 的版本标识;
下面是完成上述功能的 Shell 脚本 build_aar.sh (内部有判断),Windows 脚本看具体工程 build_aar.bat
1 | !/bin/bash |
另外,还需要动态判断 unityLibrary 是以 module 还是 AAR 形式存在。
在 base.gradle
增加校验当前是否有 unityLibrary module
1 | ext { |
在 launcher.gradle 里面配置编译时使用的是 module 还是 AAR(注意:必须保证 AAR的版本命名和应用版本命名一致)
1 | dependencies { |
另外,settings.gradle 文件必须保证 include ‘:unityLibrary’
是单行格式,脚本会在生成完 AAR 后,删掉该行。
使用自定义 UnityPlayerActivity
需要用到一个小技巧,由于要用到 Unity SDK 的 API ,就必须在当前 module 添加,如果是编译 unityLibrary module 时,Unity 的 jar 包是在 unityLibrary module 里面的,是无法引用到,这时需要拷贝一份 unity-classes.jar 放到当前 module 的 libs 下,并使用 compileOnly 的编译方式,上述配置则需改为:
1 | // 务必注释掉,否则会将 unity-classes.jar 打到 aar,仅采用 compileOnly 引入 |
然后拷贝 UnityPlayerActivity 重命名为 GameActivity,再修改 Android 和unity 工程的 Manifest 启动页配置即可。
Unity 平台直接编译运行到手机
如果 Android 端没配置 productFlavors, Unity 平台是可以直接使用 Build And Run
导出并运行、安装 APK 到手机上的。
但如果配置了的话,由于编译后的 APK 文件命名被改了,需要在编译之后,拷贝个备份改回原来的默认命名,才能找到 APK 文件,才能正常安装。
1 | productFlavors { |
修改编译后 APK 文件的脚本,添加在 launcher.gradle
里面的 android{} 内
1 | // 打包完成复制一份重命名的文件,并改为默认命名(unity开发平台需要用到默认命名的文件) |
开发流程
- Unity 开发:在 Unity 开发平台上开发完功能后,通过
File/Build And Run
直接导出 Android 工程并编译运行 APK 安装到手机上;测试无误后,进入 Android 工程目录,Mac 执行./build_aar.sh versionName
,Windows 执行build_aar.bat versionName
(versionName 名字一定要和 Android 工程的一致,或者只输入./build_aar.sh
或build_aar.bat
会以当前的 AAR 文件名版本名作为版本),编译生成 AAR 后(最好再打开 AS 运行一遍,验证编译安装正常),直接 GIT 提交 unityLibrary-v1.x.xx.aar 文件到 Android 工程即可; - Android 开发:正常基于 Android 工程开发,如果改动
settings.gradle
、gradle.properties
,以及 launcher 下的 AndroidManifest.xml,需要同步改动 Unity Asset/Plugin 的模板文件,并提交,同时通知 Unity 更新 Unity 工程和 Android 工程代码即可;
问题&&解决
如果提示 Project with path ‘:unityLibrary’ could not be found in root project ‘xxx’.
可以全局搜索 unityLibrary 看看哪里有引用到 unityLibrary module 的配置(脚本里面的不用管),可以检查下 base.gradle 是否有以下配置,有则将它改为放置 AAR 的 module 名字,比如 launcher1
2
3flatDir {
dirs "${project(':launcher').projectDir}/libs"
}如果提示
Could not resolve all files for configuration':launcher:debugRuntimeClasspath'. > Could not find :unityLibrary-v1.0.0
,是找不到引用的AAR文件,有可能是你的版。本号命名规则和脚本里面默认的不一致,没指定版本的话,AAR 默认名字是unityLibrary-v1.0.00.aar
,你的应用版本命名必须和AAR的版本命名一致;在电脑终端上先执行 gradle -version,如果提示找不到该命令,则先配置 gradle 环境;
如果有提示 Unity 使用的 Gradle 版本缺失某个文件或和Android 工程的不一致的话,可以考虑将 Unity 的 Gradle 版本替换成 Android 工程使用的版本;
如果提示 JDK 11 版本太高,可考虑将 Android Studio 的 JDK 版本降到 1.8 (新版本的 AS用的 JDK 都是11的)
–End–