我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Unity嵌入Android项目开发

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Unity嵌入Android项目开发

目录

前言

随着元宇宙的概念越来越火爆,各个平台都想和3d虚拟世界搭上关系,Android作为移动端的巨头之一当然也不例外。而App想要搭上元宇宙这趟列车,3d渲染能力必不可缺。Unity作为3d引擎中的热门,具有庞大的开发生态,并且渲染效果和开发效率都非常出色,今天我们就让Unity和Android结合起来,双剑合璧,天下无敌!

我们首先看看Unity和Android的混合开发方案,一般Unity在Android的开发分为三种形式

  • 完全用Unity打包成Apk,所有开发完全在Unity上
  • Android作为主体,把Unity作为库文件引入到Android项目中使用
  • Unity作为主体,把Android的jar/aar引入到Unity项目中使用

第一种实际上几乎不涉及到Android相关的知识,直接用Unity就能打包成Apk,所以很简单,直接跳过。这篇文件主要说第二种方案

1 搭建开发环境

主要使用的开发工具为

  • Android:Android Studio
  • Unity:Unity Editor

具体的搭建教程这里不细说了,网上到处都是这类文章,不过这里建议Android Studio版本在3.3.2以上,Unity在Unity 2019.3.a2以上,否则可能会出现各种问题,这里指路官方的搭建教程:

2 创建Unity项目

2.1 新建项目

安装好合适版本的Unity后,点击新建项目,随意选择一个项目模板,这里我直接选择3D核心模板,然后随便起个项目名称,最后点击创建项目即可:
在这里插入图片描述

2.2 Unity构建配置

创建后我们不对Unity的场景做任何操作,我们直接准备导出工作,首先打开点击右上角File菜单,在弹出的选项中选择Build Setting打开构建设置窗口:
在这里插入图片描述

  • 首先点击上图中红框1右下角的Add Open Scenes把当前的Unity场景添加进去,否则的话不会有任何场景被构建,点击后会显示即将打包的场景在方框内
  • 然后在上图红框2内选择Android平台,可能会需要进行平台的转换,点击如上图右下角的Switch Platform即可。如果还没装过Unity中Android平台相关的模块,Unity会有相关的提示,按照提示去安装即可。
  • 当上面两步骤都完成后,就能看到如图红框3中的内容了。我们暂时不需要管这些配置具体什么意思,我们直接点击勾选Export Project即可

到这里Unity构建的配置就差不多了,但是我们还不能直接导出库文件,还需要进行更多的配置。

2.3 Android环境相关配置

我们知道,要打包Android相关的东西都是要配置好java环境,配置好Android Sdk,然后使用Gradle等工具进行构建,Unity也不例外,我们点击Unity编辑器上面的菜单栏的Edit选项,再选择Preferences,弹出如下图的窗口:
在这里插入图片描述
我们主要需要配置的就是下面的JDK、Android SDK、Android NDK和Gradle这些路径,可以按照默认的设置,前提是有按照Unity转换Android平台时的提示安装这些需要的东西。但是我个人建议自定义去选择和Android Studio的这些部分配置一样的路径,这样一来两边使用的配置都一致,可以减少很多不必要的版本差异问题。

2.4 导出Unity库文件

我们回到Build Setting,点击右下角的Export,选择一个文件夹位置进行保存,Unity就开始自动打包和导出了,如果遇到任何的打包异常,大部分问题都是由于上面打包配置部分的某个地方路径不对。如果都没问题,我们将在我们指定的文件夹中得到如下图的文件:
在这里插入图片描述
如果得到的文件和上图不一致,一般是由于使用了过低的Unity版本。因为Unity官方近些年在这部分有不少改动,所以没法使用于所有版本,当前这篇文章使用的版本是Unity 2021.3.5f1c1。到这里为止,我们得到了可用于Android项目的库文件。

3 创建Android项目

3.1 新建Android项目

我们这里新建一个Android项目,当然,用已有项目也是可以的,但是可能会存在一些配置问题需要修改,我们这里选用新项目,可以减少一些由于旧项目配置差异导致的额外问题。创建过程这里就不细说了,因为只需要一路默认就行了,直到创建成功。

3.2 Android环境相关配置

Android Studio在安装配置的时候已经帮我们配置到了Sdk和Jdk等信息,但我们需要检查一下,这些配置是否和Unity前面配置所用的路径或者版本是一致的,这样可以减少各种版本差异导致的报错。配置路径在File->Project Structure->Sdk Location中。

3.2 导入Unity相关的库

回到前面Unity打包好的库文件所在文件夹,把里面的unityLibrary文件夹直接拖到Android的项目中,这样Android Studio会自动帮我们做一些配置操作,然后把它作为一个module的形式存在,当然,也可以自己对其做一些操作使其和app module融合一起,但是不建议这样做,最好是把它作为单独的一个module,方便后续替换更新。最终如下图所示:
在这里插入图片描述
打开settings.gradle文件检查看是否有把module给包含到项目中,如果没有则手动输入include一下,如下图:
在这里插入图片描述
sync同步一下,不出意外会报出第一个错误:

A problem occurred evaluating project ':unityLibrary'.> Could not get unknown property 'unityStreamingAssets' for object of type com.android.build.gradle.internal.dsl.AaptOptions$AgpDecorated.

意思是不知道unityStreamingAssets这个属性是啥东西。既然Android Studio不知道这是啥,那我们就告诉它,打开gradle.properties文件,添加以下代码:

unityStreamingAssets=.unity3d, google-services-desktop.json, google-services.json, GoogleService-Info.plist

再次同步一下,不出意外的话unityLibrary就正式作为module成为了Android项目的一员。

3.3 Android中跳转到Unity视图

我们首先在App主模块中打开它的build.gradle文件,把unityLibrary依赖一下,毕竟不同的模块,只需要在dependencies的闭包中写下以下代码:

implementation project(path: ':unityLibrary')

这样在App模块中就能访问了。我们打开MainActivity,如果没有则创建。在MainActivity的布局中添加一个按钮,然后给它设置一个监听,里面写上跳转到Unity的代码,其中的UnityPlayerActivity就是unity帮我们创好的Unity的 Activity,代码如下:

findViewById(R.id.showUnityBtn).setOnClickListener(v -> {    Intent intent = new Intent(MainActivity.this, UnityPlayerActivity.class);    startActivity(intent);});

写好了代码,我们先运行一下看看效果如何。可能又会遇到编译问题,例如下面这种:

Manifest merger failed : uses-sdk:minSdkVersion 21 cannot be smaller than version 22 declared in library [:unityLibrary] D:\Android\AndUnity\unityLibrary\build\intermediates\merged_manifest\debug\AndroidManifest.xml as the library might be using APIs not available in 21Suggestion: use a compatible library with a minSdk of at most 21,or increase this project's minSdk version to at least 22,or use tools:overrideLibrary="com.unity3d.player" to force usage (may lead to runtime failures)

很明显,就是说我们unity模块里的minSdkVersion比工程中的minSdkVersion要大,下面还有Suggestion告诉我们怎么改,我们就改一下unity模块里的吧,把minSdkVerson改为21,毕竟大多数情况下是不应该动项目里的配置。把unityLibrary的build.gradle里的minSdkVersion改为21后,我们同步一下,再运行看看。终于运行起来了,界面如下:
在这里插入图片描述

当我们点击按钮时,不出意外又报错了!报错信息一般如下:

Process: com.demo.andunity, PID: 16924    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.demo.andunity/com.unity3d.player.UnityPlayerActivity}: android.content.res.Resources$NotFoundException: String resource ID #0x0

很明显,意思是有某个资源id找不到,说明我们还少了一些东西。回想之前Unity之前打包出来的东西,还有个launcher文件夹,从名字可以看出它本来就应该作为一个启动入口存在的,但我们使用了Android项目里的App主模块,因此有些launcher里的资源就找不到了。我们在launcher的子文件夹里找到res文件夹里的资源,直接复制到unityLibraryres目录下。我们再次启动app,然后点击按钮进行跳转,拨开云雾见青天,我们终于看到了Unity的启动画面和场景画面,由于我们什么都没放上去,因此显示的是Unity的默认空场景,如下图:
在这里插入图片描述
到这里我们已经成功把Unity作为一个module集成到了我们的Android项目里,并且对没对App主模块做太多额外的操作,仅仅依赖了一下unityLibrary和进行了activity跳转。

4 进阶扩展

4.1 包体积优化

虽然我们成功把Unity嵌入了Android里,但移动端一直存在着一个不成文的规定,那就是APP的包体积要尽量的小,我们看看新增了Unity模块后包体积增大了多少(都以debug版本来算,Android端也未做优化):

  • Android空项目大小:
    在这里插入图片描述
  • Android增加了Unity模块后的大小:
    在这里插入图片描述
    可以看到,增加了unity模块后直接增加了20M多,这还只是个空的unity,这大小增量肯定忍不了。我们稍微检查一下就能看到大部分的增量都在lib下的so库和Assets下的dll:
    在这里插入图片描述

4.1.1 mono和IL2Cpp

如果去查询过Unity相关的编译知识,就会知道Unity长久以来都在用Mono的编译方式,在这里就不详细说mono具体是什么,大家只需要理解为Unity用mono框架实现跨平台就足够了,感兴趣的可以搜索一下Unity的mono框架。但在新版本的Unity中,引入了IL2CPP的编译方式,这里也不细说IL2CPP是什么,只需要理解为它把C#翻译成一个中间语言,到需要的时候才去解析就行了。这样就不需要引入一套mono框架所需要的DLL了。因此这种编译方式打包出来的unity库所携带的库文件更少,但是运行速度却更快(无需经过mono框架的转换)。

4.1.2 IL2CPP编译打包

我们回到Unity里,按序打开Eidt->Project Settings->Player->Other Settings,打开后按下图进行配置:
在这里插入图片描述
主要是把Mono改成了IL2CPP,这里需要勾选上ARM64,不然会有warming,建议勾选一下,也打包ARM64架构的库,提高对应手机CPU架构的Unity性能。顺带一提,用Mono编译的时候是无法选择ARM64的,可能会在手机适配方面没那么好。然后我们准备打包工程,记得先删除原来生成的文件,不然会出现新的错误,然后重新Export,导出成功后把unityLibrary覆盖Android工程目录下的unityLibrary,记得把minSdkVersion改一下,然后再同步一下,项目中的unityLibrary就多了arm64-v8a的文件夹,并且so的数量也大幅度减少。如图所示:
在这里插入图片描述
我们再次运行项目,然而又又又又报错了!查看报错信息我们看到如下描述:

* Exception is:org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':unityLibrary:BuildIl2CppTask'.at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:188)...........省略若干报错.............Caused by: org.gradle.api.InvalidUserDataException: NDK is not installed

NDK is not installed!原来是由于我们转换了编译方式,用到了NDK,因此我们再去配置一下NDK路径。打开local.properties配置文件,输入一下NDK路径,可以直接复制Unity里配置的路径过来。比如我的项目里配置的是这样:

ndk.dir=C\:\\Program Files\\Unity\\Hub\\Editor\\2021.3.15f1c1\\Editor\\Data\\PlaybackEngines\\AndroidPlayer\\NDK

再打包一下,这次不出意外是成功了,我们看下Apk大小:
在这里插入图片描述
勉强比之前的小了一点,但是似乎也不太行啊?其实这里是有区别的,我们前面Mono方式打包的时候是只有一个armeabi-v7a了文件夹,现在是多了一个arm64-v8a文件夹,我们为了能真实点比较,我们暂时把这个arm64-v8a给去掉,我们在主模块的build.gradle中把项目指定为armeabi-v7a:

ndk {    abiFilters 'armeabi-v7a'}

再次打包,看看大小:
在这里插入图片描述
可以减去Apk空项目时的6.5M大小,那么Unity模块也只占了和空项目差不多的大小,也在6-7M左右。当然,Unity包体积的优化还有更多手段,但那些都是后话了,光这一个简单的优化也已经能满足很多app的需求了,毕竟别忘了我们用的可是Debug编译,换成Release编译包体积大小还会再次减小

4.2 局部渲染

前面我们打开Unity的Activity,我们可以看到场景是沾满全屏的,但很多场景中我们都需要把Unity的视图嵌入原生的页面中,那么有没有方法让其只显示在一个自定义View的范围内呢?答案当然是有的。我们主要是启动了一个叫UnityPlayerActivity的Activity,我们进入这个Activity查看,可以看到以下代码:

// Setup activity layout@Override protected void onCreate(Bundle savedInstanceState){    requestWindowFeature(Window.FEATURE_NO_TITLE);    super.onCreate(savedInstanceState);    String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity"));    getIntent().putExtra("unity", cmdLine);    mUnityPlayer = new UnityPlayer(this, this);    setContentView(mUnityPlayer);    mUnityPlayer.requestFocus();}

这里创建了一个UnityPlayer,然后把它设为当前的ContentView。那么我们只需要稍微改造一下,新建一个布局,在布局中新建一个有大小的FrameLayout,然后把这个UnityPlayer作为子View添加到我们创建的帧布局就行了,修改后的代码如下:

// Setup activity layout@Override protected void onCreate(Bundle savedInstanceState){    requestWindowFeature(Window.FEATURE_NO_TITLE);    super.onCreate(savedInstanceState);    String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity"));    getIntent().putExtra("unity", cmdLine);    mUnityPlayer = new UnityPlayer(this, this);    setContentView(R.layout.unity_layout);    FrameLayout frameLayout = findViewById(R.id.unityView);    frameLayout.addView(mUnityPlayer);    mUnityPlayer.requestFocus();}

再次编译运行打开,点击跳转按钮,可以看到如下的界面:
在这里插入图片描述
到这里我们就做到了简单的局部渲染,但我们只是演示了一种最简单的方式,反正UnityPlayer实际上就是继承自Framelayout,所以我们可操作性有很多,就靠大家自由发挥了。

4.3 限制问题

Unity嵌入Android存在一个较大的限制问题,可以说是官方设计的缺陷,那就是退出Unity所在的Activity会销毁UnityPlayer,而UnityPlayer会直接杀死自己所在的进程,就是这么的暴力!那么如果把其放在主进程中,一退出Unity的Activity整个App就退出了,这当然是不能忍受的。官方建议的解决方案是把Unity的Activity放在另一个进程中,从而避免了直接退出主进程。但实际上这样的办法也不是很好,毕竟进程间通信不是那么的方便快捷,会有更多的麻烦需要处理。既然会杀死当前进程,那么机智的小伙伴们肯定想到了办法:重写销毁方法屏蔽掉这个杀死进程的逻辑,不就ok了?即使无法重写也能用反射的方式强行就修改。但实际上,Unity既然这样做就有它的理由,当大家屏蔽掉这个逻辑后,就会出现各种奇怪的现象,比如再次启动时崩溃、显示的视图不正确等等各种问题接踵而来。因此不得已满世界找解决方案,包括国外各种论坛,找来找去最终还是没有比较稳妥的解决方案。只能求助于Unity官方,结果官方明确表明暂时没有方法解决这个问题,只能给它起个进程。然后转头提出了一个商业的方案,大概意思是跟他们合作,把App给他们,他们帮忙实现非子进程方式接入,看看这是正常官方能干出来的事吗?自己家引擎的问题,这钱怎么好意思拿,也不吐槽太多了,希望官方后续能看看这块的诉求并且处理一下,毕竟市场还是很大的。总结就是,现在想要Unity结合Android,就需要另起进程,处理好进程间通信,并且有几M的包体增加。当这些问题都能接受,那么App集成上Unity还是很美妙的,毕竟Unity的生态实在是太好,开发效率极佳,效果表现也不差。

来源地址:https://blog.csdn.net/Nbin_Newby/article/details/128065977

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Unity嵌入Android项目开发

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android开发之关于项目

本系列文章会根据项目的进度进行相关介绍,其会分为多个模块,每个模块互不依赖,各个模块都是单独的学习内容,如sqlite学习模块,contacts联系人模块等。 新建项目 相信大部分童鞋都可以自定义项目了,那么在此,我简单介绍下android
2022-06-06

Go 嵌入式开发

嵌入式 go 开发使用 go mod init 创建项目。指定目标架构进行交叉编译:goos 和 goarch。使用 runtime/cgo 和 runtime/volatile 与硬件交互,以及 github.com/d2r2/go-i2
Go 嵌入式开发
2024-04-08

Android项目开发之UI设计器

开发人员可以用以下两种方式声明UI:一是通过.xml文件(不带预览界面)或者.axml文件(带预览界面)来描述;二是用C#代码实现。 用.axml文件描述用户界面(UI)时,设计器分为【设计】视图和【源】视图。这种方式的优点是:可以尽可能
2022-06-06

Android购物车项目快速开发

购物车项目,业务需要实现了一个购物车的项目,简单的了解下实现逻辑:数据计算等是在Adapter中计算出来的,通过在Adapter中计算出来的数据就可以回调到Activity中进行订单操作等功能业务逻辑,每一个店铺产生的数据是走一条流程的,(
2022-06-06

选择java开发还是嵌入式开发

首先,Java开发和嵌入式开发都是目前IT行业内比较常见的开发岗位,也都有大量的从业人员,所以从就业的角度来看,学习Java开发和嵌入式开发都是不错的选择。Java语言的应用领域包括Web开发、Android开发和大数据开发等领域,这些领域也都有大量的岗位需求
选择java开发还是嵌入式开发
2019-07-23

一文教你在现有Vue项目中嵌入Blazor项目

目前官方只提供了angular和react俩种示例,所以本教程将来讲解如何在Vue的现有项目中嵌入使用Blazor项目。文中的方法讲解详细,感兴趣的小伙伴可以了解一下
2023-01-28

Android开发笔记(三)——创建第一个Android项目

创建 第一个Android 工程 1)启动 Eclipse,菜单栏选择 File -> New -> Project…。 2)在 New Project 窗口的列表中找到 Android,选择 Android Application Pro
2022-06-06

Android 在现有项目中使用NDK开发

新建项目直接选中Native C++即可,本篇文章主要描述如何在现有项目中添加Native方法 添加步骤 1.AS中依次点击Preferences->Android SDK -> SDK Tools安装Cmake和NDK,如果安装过程中下载
2022-06-06

IDEA开发及运行第一个Android项目

IDEA自动下载SDK、Gradle,保证能访问网络。 原来eclipse能使用的sdk,配到idea报错,就换成自动下载最新的了。 之前没成功可能是我防火墙禁用了上网。 新建项目提示安装SDK等待下载完成继续建项目选择手机或平板及目标设备
2022-06-06

Android开发中项目实现一个显示输入密码的功能

Android开发中项目实现一个显示输入密码的功能?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。具体如下:main.xml:
2023-05-31

Android Studio项目中导入开源库的方法

前两天,谷歌发布了Android Studio 1.0的正式版,也有更多的人开始迁移到Android Studio进行开发。然而,网上很多的开源库,控件等还是以前的基于Eclipse进行开发,很多人不知道怎么导入到自己的基于Android
2022-06-06

Android开源项目大全

以下是本人日常工作中收集的比较不错的Android开源项目,欢迎博友提供未收录的网址。roottools:RootTools gives Rooted developers easy access to common rooted tool
2022-06-06

android 开发教程之日历项目实践(三)

二、创建样式 日历显示的表格线,使用 Cell 填充图形的边框来实现,为了统一,我们先定义边框线的颜色及线条精细。 另外还要定义一系统填充样式等。 创建 color: color_calendar_border 表格线color_calen
2022-06-06

android 开发教程之日历项目实践(一)

前言:决定开始学习 Android 平台下的软件开发,以日历作为实践项目,进行一周后,基本完成。 为了总结及笔记,并给有需要的朋友借鉴,开始整理本教程。开始之前: 在编写程序之前,需要进行项目设计,因为是练习项目,主要是确定软件 UI 界面
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录