Android中嵌入Unity游戏 您所在的位置:网站首页 unityplayer手机 Android中嵌入Unity游戏

Android中嵌入Unity游戏

2023-07-15 04:28| 来源: 网络整理| 查看: 265

说一下需求,因为最近AR有点火,大BOSS从手机淘宝发现了一个AR游戏,在“我的淘宝”右上角

我的某宝--抓怪游戏

有一个“抓喵喵”的游戏,好像是AR实现的(本人小白,不确定是不是AR。。。囧!),点进去就是一个游戏,如下图:

抓喵喵

所以大BOSS发话了,也要弄一个出来,就集成到我们自己的App里,于是开干。。。。。。先介绍一下大体功能,点击进入游戏,先是一个加载页,这个我没上图,就是一张介绍游戏规则的图片加一个进度条,说一下游戏场景里的功能,首先是一个地图,看左下角。。。。。高德地图 这里用到了地图和定位功能,主角定位的位置就是我当前位置,主角少女旁边有很多怪物(喵喵),点击喵喵,就进入捕怪场景了(这些就属于游戏部分了,具体功能大家可以下载或更新手机淘宝体验一下),抓捕到喵后就能获取优惠券折扣之类的。

本来的安排是游戏这块是交给Unity开发做的,我们移动端只需要在app上留个入口,然后加载Unity游戏就行,所以任务下来了后就了解了一个Android和Unity交互这块的知识点,这个网上太多了,我这里就不多说了,主要说一下,在嵌入Unity过程中遇到的各种问题

Android 中嵌入Unity

关于Android和Unity集成,网上有两种方案,一种是把Android打包成jar,集成到Unity中,Unity中还要引入Android的SDK,另一种就是把Unity打包,然后解压,把相关的jar包、资源文件引入Android项目中,我采用的是第二种,具体步骤我就不写了(主要是懒。。。。),找了两个,大家可以看看 (交互步骤两位作者都写的十分清楚,赞!):

初识untiy3d与安卓交互 作者:暗尘随码去

Android与Unity交互研究

说一下我的问题,Unity开发大哥,写了Demo,打包发给我,我照着网上的步骤先试着往项目里集成,一切都很OK,很轻松。然后问题来了,本来这个游戏场景是Unity做的,但是Unity那边集成地图出现了无法解决的问题,所以地图这块儿需要移动端来做了,我有点忐忑,因为以前从没集成过地图,于是咬着牙硬上了,花了一天时间(囧!!!),把地图弄出来了,刚开始用的是百度地图,但是要求中心点不在地图正中心(中心点不在地图中心还叫中心点吗?),弄半天没弄出来,然后iOS哥们用的高德地图,可以把中心点偏移出中心位置,于是建议我也用高德,好吧,换吧,正好统一下,换成高德地图,OK,都弄好了,开始往里面集成Unity了,如下简单布局:

这里的mapView就是高德地图,FrameLayout 就是Unity场景的父布局了,最后的那个ImageView就是最开始游戏加载的图片。当前的Activity须得继承UnityPlayerActivity,代码:

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_catch_layout); //高德地图 root = View.inflate(CatchActivity.this, R.layout.activity_catch_layout, null); mMapView = (MapView) findViewById(R.id.mapView); mMapView.onCreate(savedInstanceState);// 此方法必须重写 //Unity 父布局 fl_unity_layout = (FrameLayout) findViewById(R.id.fl_unity_layout); //把Unity游戏加载进来 fl_unity_layout.addView(mUnityPlayer.getView()); //游戏加载图片 iv_unity_loading = (ImageView) findViewById(R.id.iv_unity_loading); //下面省略地图初始化和定位设置。。。。 }

我按上面代码跑了一下,发现Unity场景怎么也出不来,之前单独加载Unity可是OK的,咋回事?而且我的Unity可是放在地图上面的,后来查了一下,地图的MapView和Unity都涉及到了OpenGL中的glsurfaceview,所以冲突了,想具体了解的可以看看OpenGL和glSurfaceView,于是我把这里的MapView换成了TextureMapView,解决了Unity不显示的问题

Unity背景不透明问题

解决了Unity不显示问题后,出现了另一个问题,那就是Unity场景的背景色是一片漆黑色的,即便Unity那边设置了背景透明,打包后集成到Android中后也是漆黑一片,这就坑了,于是Android和Unity两边找解决办法,各种找答案 https://forum.unity.com/threads/transparency-on-android.456736/ https://stackoverflow.com/questions/17705364/unityplayer-as-a-subview-with-transparent-background-unity-game-engine http://www.geekyhamster.com/2013/07/unityplayer-as-subview-with-transparent.html 硬着头皮看了几篇英文文章,都遇到了这个问题,发现只有Unity开发版本降到4.2才行,这叫一个坑!!!没办法,现在Unity开发大哥的Unity开发版本是2017的,最后把版本降到了4.9,没解决,再降到4.2,搞到了半夜,还是没弄出来,回家。。。。。第二天早上,稍改了一下,试跑了一下,居然可以了,当时激动的,这个问题搞了两天,终于好了。。。。解决办法是参照上面第三个链接改好的,但是只能Unity 4.2版本打的包有效果,其他版本都不行,操蛋!!!

Unity-class.jar 混淆问题

解决了背景透明问题,心情大好,想看看集成了Unity游戏后apk有多大,于是准备打个release包看看,但是,一打release包就报错,但是debug包没问题啊,也能直接在手机上运行,错误如下:

Warning:Exception while processing task java.io.IOException: Can't read [D:\****\app\libs\untiy-classes.jar(;;;;;;**.class)] (Can't process class [org/fmod/FMODAudioDevice.class] (256))

在网上查了一下,好像是混淆的时候出的问题,在build.gradle 文件里一看,debug版没有进行混淆,所以没有问题,但是release混淆就出问题了,所以,很明显,就是untiy-class.jar 这个包混淆时出问题了,于是照着网上的方案,将proguard-rules.pro 文件里的混淆规则改了一下

-dontwarn com.unity3d.player.** -dontwarn org.fmod.** -dontwarn bitter.jnibridge.** -keep class com.unity3d.player.** {*;} -keep class org.fmod.** {*;} -keep class bitter.jnibridge.** {*;}

再打包。。。。还是报错,还是这个问题,摔桌。。。。。 再搜。。。。 找到了遇到这个问题的解决方案: android引入unity-classes.jar之后进行混淆的问题解决 作者遇到的问题和我的简直一毛一样啊,兴奋。。。。。照着作者的方案,把ProGuard 的源文件改了,使用ant 重新编译了,然后再打包。。。。。还是报一样的错,再摔桌。。。。。 再发几个相同解决方案的链接: http://www.cnblogs.com/huangbei1990/p/6097782.html http://bbs.csdn.net/topics/392084419?list=lz

http://blog.csdn.net/vinomvp/article/details/58614043 http://blog.csdn.net/tianyutaizi/article/details/41698933 https://sourceforge.net/p/proguard/bugs/420/?page=0 https://stackoverflow.com/questions/22165902/proguard-returned-with-error-code-1-proguard-errors-with-untiy-classes-jar http://glblong.blog.51cto.com/3058613/1435941 https://sourceforge.net/p/proguard/bugs/420/

然而,都没解决我的问题,有点小绝望了。。。。 最后仔细想想,应该是版本的问题,几位作者的帖子大都是三四年前的了,可能版本更新后,有的问题就不能照原办法解决了,问题卡这儿了。。。。 这个打包的问题还没解决,另一个问题又来了,4.2版本的Unity打包的程度在app上陀螺仪失效了,在这个游戏里,用户是可以拿着手机移动方位的,效果就是地图也会随着屏幕旋转,而且游戏中的人物也会随着屏幕旋转的,比如,上图中,有一只喵在少女的后面,没显示出来,需要用户拿着手机转个身,将身后的怪物显示在屏幕当中,这里的Unity中有陀螺仪效果,喵喵会随着屏幕旋转而出现/隐藏于屏幕中,在iOS中是可以的,但是到了Android里就不行了,地图可以旋转,但是Unity中的模型动不了。。。。。 android端开发顿时卡住了,而Unity开发大哥因为android这方面的问题就先顾iOS去了,先把iOS搞出来,再来解决android问题

替换方案

android这边两个问题最终还是没解决,一个是4.2版本的打包问题(2017版Unity的可以打包),另一个就是陀螺仪失效问题。。。。 最后技术老大拍板拿了方案,地图和怪物显示都android来做,Unity只负责打怪场景(Unity开发版本用2017版),当然,只是android端。。。苦逼 只能在地图上动手脚了,要在地图上显示怪物,只能看自定义 Marker,我是拿到当前位置的经纬度,然后通过随机数,在当前位置上随机增加或减少经度和纬度,将怪物分布到当前位置的附近,再设置MarkerOption来显示怪物的图片

//随机生成经纬度 double demonLongitude = currentLongitude + ((random.nextDouble() + 0.01) * (random.nextInt() > 0.5 ? 0.002 : -0.002)); double demonLatitude = currentLatitude + ((random.nextDouble() + 0.01) * (random.nextInt() > 0.5 ? 0.002 : -0.002)); LatLng latlng = new LatLng(demonLatitude, demonLongitude); MarkerOptions markerOption = new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(demonList[db.getMonster_id()-1])) .position(latlng) .draggable(true); aMap.addMarker(markerOption);

当然这个效果和iOS端用Unity实现的效果比起来就差多了,但是我暂时没其他解决办法了。。。。一个是怪物不能动,不像Unity实现的那样能够动而且是3D效果的,地图上只有一张图片,第二个就是图片还是固定大小的,不能设置,想把怪物显示大一点都不行,若是哪位同学对高德地图API里设置marker这块比较熟悉的话望告知,谢谢

Unity退出问题

这里还有个坑爹问题,就是Unity退出的时候,会将整个进程杀掉,导致app重启。。。。 Unity中退出使用的是 mUnityPlayer.quit() 方法,但是我们看看这个方法的代码:

public void quit() { if(this.r != null) { this.r.b(); } this.o = true; if(!this.e.e()) { this.pause(); } this.a.a(); try { this.a.join(4000L); } catch (InterruptedException var1) { this.a.interrupt(); } if(this.g != null) { this.l.unregisterReceiver(this.g); } this.g = null; if(j.c()) { this.removeAllViews(); } //这里是关键 this.kill(); g(); }

关键代码是在倒数第三行 this.kill(); 下面是kill()方法:

protected void kill() { Process.killProcess(Process.myPid()); }

杀死进程啊,有木有。。。。。所以APP就重启了啊。。。。 网上千篇一律的都说加个按钮调用mUnityPlayer.quity() 方法,或是在onBackPress方法中调用mUnityPlayer.quity() 方法,这样和直接调用 mUnityPlayer.quity()有什么不一样么,都重启app了,或许我用的方法不对? 稍靠谱点的就是android端调用Unity方法,Unity端执行退出,另一个方案就是将Unity单独放另一个进程里,这样退出的话就不会杀死app所在的进程了。 但是这两种方案我都试过了,第一种,Unity端退出执行的还是quity() 方法,最后还是把进程给杀了,第二种试了也没效果。。。 最后想了个笨方法,就是用户点返回按钮或是退出这个继承 UnityPlayerActivity的Activity时,不将这个Activity 关闭,而是将这个Activity的启动模式设置为singleInstance,即每次打开的时候将其单独放在一个任务栈里(因为app主页是singleTask模式,进入主页时,会将其上面的activity都清除出栈,为了避免UnityPlayerActivity子类被清除,所以将其设置为singleInstance,作为一个单例),这样这个包含Unity游戏的Activity就不会finish掉,同时也解决了每次加载Unity游戏时时间过长的问题,就第一次加载时花费一些时间,下次就不用再花时间来加载了。。。。。我们Unity加载时间得几十秒钟,得等十一过后,Unity开发大哥来解决这个问题了。

其他要注意的问题

1、Unity调用Android是在子线程中运行的,所以如果涉及到UI操作,记得切换到主线程

/** * 供Unity端调用 * 接收Unity传递过来的数据 * * @param MessageData */ public void getUnityMessage(int messageType, String MessageData) { switch (messageType) { case 2://获取怪物列表 runOnUiThread(new Runnable() { @Override public void run() { //隐藏加载中图片和Unity场景,显示地图 iv_unity_loading.setVisibility(View.GONE); fl_unity_layout.setVisibility(View.GONE); //若未授权定位,则提示用户去设置 if (!isGrant) { showFinishActivityDialog(); } } }); break; case 3://打怪结果 String[] result = MessageData.split("/"); handleCatchResult(result[0],result[1],result[2]); break; } }

2、Unity端调用android 方法

AndroidJavaClass jc = new AndroidJavaClass ("com.unity3d.player.UnityPlayer"); AndroidJavaObject jo = jc.GetStatic ("currentActivity"); jo.Call ("makePauseUnity");

上面的三个参数,前两个是固定不变的,即"com.unity3d.player.UnityPlayer"和"currentActivity" 这两个参数是固定 不变的,之前,Unity开发那边,换成了我们app的包名和 Unity所在的Activity名称,发现怎么也调用不了。

上面就是在Android中嵌入Unity时,我遇到的一些坑,暂时只想到了这么多,后面想起来了再加上,同时如果有遇到同样问题的同学可以提问,力所能及的话就帮着解决。同时,如果有对我上面遇到的问题有更好的解决办法的,也请在下面留言,多谢多谢! 另外想学习Unity的同学可以去这位大神博客看看,挺详细的

雨松MOMO---Unity

废话:第一次在简书写博客,主要是太懒了。。。。明天就十一了,同事都下班了,国庆好好休息,这半个月累坏了,天天到家十二点、一点的,脑仁儿疼,下班走人。。。

更新:最后Unity退出还是用mUnityPlayer.quit()方法退出,不过Unity所在的Activity设置process属性,单独一个进程,这样退出时就会退出Unity所在的进程,而不会退出我们自己的app了



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有