Android中嵌入Unity游戏

说一下需求,因为最近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了,如下简单布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical">

    <com.amap.api.maps.MapView
        android:id="@+id/mapView"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </com.amap.api.maps.MapView>

    <FrameLayout
        android:id="@+id/fl_unity_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"></FrameLayout>

    <ImageView
        android:id="@+id/iv_unity_loading"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/unity_loading"
        android:scaleType="fitXY"/>
</RelativeLayout>

这里的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<AndroidJavaObject> ("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了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容