Android中嵌入Unity游戲

說一下需求,因為最近AR有點火,大BOSS從手機(jī)淘寶發(fā)現(xiàn)了一個AR游戲,在“我的淘寶”右上角

我的某寶--抓怪游戲

有一個“抓喵喵”的游戲,好像是AR實現(xiàn)的(本人小白,不確定是不是AR。。。囧!),點進(jìn)去就是一個游戲,如下圖:


抓喵喵

所以大BOSS發(fā)話了,也要弄一個出來,就集成到我們自己的App里,于是開干。。。。。。先介紹一下大體功能,點擊進(jìn)入游戲,先是一個加載頁,這個我沒上圖,就是一張介紹游戲規(guī)則的圖片加一個進(jìn)度條,說一下游戲場景里的功能,首先是一個地圖,看左下角。。。。。高德地圖 這里用到了地圖和定位功能,主角定位的位置就是我當(dāng)前位置,主角少女旁邊有很多怪物(喵喵),點擊喵喵,就進(jìn)入捕怪場景了(這些就屬于游戲部分了,具體功能大家可以下載或更新手機(jī)淘寶體驗一下),抓捕到喵后就能獲取優(yōu)惠券折扣之類的。

本來的安排是游戲這塊是交給Unity開發(fā)做的,我們移動端只需要在app上留個入口,然后加載Unity游戲就行,所以任務(wù)下來了后就了解了一個Android和Unity交互這塊的知識點,這個網(wǎng)上太多了,我這里就不多說了,主要說一下,在嵌入Unity過程中遇到的各種問題

Android 中嵌入Unity

關(guān)于Android和Unity集成,網(wǎng)上有兩種方案,一種是把Android打包成jar,集成到Unity中,Unity中還要引入Android的SDK,另一種就是把Unity打包,然后解壓,把相關(guān)的jar包、資源文件引入Android項目中,我采用的是第二種,具體步驟我就不寫了(主要是懶。。。。),找了兩個,大家可以看看 (交互步驟兩位作者都寫的十分清楚,贊!):

初識untiy3d與安卓交互 作者:暗塵隨碼去

Android與Unity交互研究

說一下我的問題,Unity開發(fā)大哥,寫了Demo,打包發(fā)給我,我照著網(wǎng)上的步驟先試著往項目里集成,一切都很OK,很輕松。然后問題來了,本來這個游戲場景是Unity做的,但是Unity那邊集成地圖出現(xiàn)了無法解決的問題,所以地圖這塊兒需要移動端來做了,我有點忐忑,因為以前從沒集成過地圖,于是咬著牙硬上了,花了一天時間(囧?。。。训貓D弄出來了,剛開始用的是百度地圖,但是要求中心點不在地圖正中心(中心點不在地圖中心還叫中心點嗎?),弄半天沒弄出來,然后iOS哥們用的高德地圖,可以把中心點偏移出中心位置,于是建議我也用高德,好吧,換吧,正好統(tǒng)一下,換成高德地圖,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就是高德地圖,F(xiàn)rameLayout 就是Unity場景的父布局了,最后的那個ImageView就是最開始游戲加載的圖片。當(dāng)前的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游戲加載進(jìn)來
        fl_unity_layout.addView(mUnityPlayer.getView());

        //游戲加載圖片
        iv_unity_loading = (ImageView) findViewById(R.id.iv_unity_loading);

      //下面省略地圖初始化和定位設(shè)置。。。。

    }

我按上面代碼跑了一下,發(fā)現(xiàn)Unity場景怎么也出不來,之前單獨(dú)加載Unity可是OK的,咋回事?而且我的Unity可是放在地圖上面的,后來查了一下,地圖的MapView和Unity都涉及到了OpenGL中的glsurfaceview,所以沖突了,想具體了解的可以看看OpenGL和glSurfaceView,于是我把這里的MapView換成了TextureMapView,解決了Unity不顯示的問題

Unity背景不透明問題

解決了Unity不顯示問題后,出現(xiàn)了另一個問題,那就是Unity場景的背景色是一片漆黑色的,即便Unity那邊設(shè)置了背景透明,打包后集成到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
硬著頭皮看了幾篇英文文章,都遇到了這個問題,發(fā)現(xiàn)只有Unity開發(fā)版本降到4.2才行,這叫一個坑?。?!沒辦法,現(xiàn)在Unity開發(fā)大哥的Unity開發(fā)版本是2017的,最后把版本降到了4.9,沒解決,再降到4.2,搞到了半夜,還是沒弄出來,回家。。。。。第二天早上,稍改了一下,試跑了一下,居然可以了,當(dāng)時激動的,這個問題搞了兩天,終于好了。。。。解決辦法是參照上面第三個鏈接改好的,但是只能Unity 4.2版本打的包有效果,其他版本都不行,操蛋?。?!

Unity-class.jar 混淆問題

解決了背景透明問題,心情大好,想看看集成了Unity游戲后apk有多大,于是準(zhǔn)備打個release包看看,但是,一打release包就報錯,但是debug包沒問題啊,也能直接在手機(jī)上運(yùn)行,錯誤如下:

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))

在網(wǎng)上查了一下,好像是混淆的時候出的問題,在build.gradle 文件里一看,debug版沒有進(jìn)行混淆,所以沒有問題,但是release混淆就出問題了,所以,很明顯,就是untiy-class.jar 這個包混淆時出問題了,于是照著網(wǎng)上的方案,將proguard-rules.pro 文件里的混淆規(guī)則改了一下

-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之后進(jìn)行混淆的問題解決
作者遇到的問題和我的簡直一毛一樣啊,興奮。。。。。照著作者的方案,把ProGuard 的源文件改了,使用ant 重新編譯了,然后再打包。。。。。還是報一樣的錯,再摔桌。。。。。
再發(fā)幾個相同解決方案的鏈接:
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/

然而,都沒解決我的問題,有點小絕望了。。。。
最后仔細(xì)想想,應(yīng)該是版本的問題,幾位作者的帖子大都是三四年前的了,可能版本更新后,有的問題就不能照原辦法解決了,問題卡這兒了。。。。
這個打包的問題還沒解決,另一個問題又來了,4.2版本的Unity打包的程度在app上陀螺儀失效了,在這個游戲里,用戶是可以拿著手機(jī)移動方位的,效果就是地圖也會隨著屏幕旋轉(zhuǎn),而且游戲中的人物也會隨著屏幕旋轉(zhuǎn)的,比如,上圖中,有一只喵在少女的后面,沒顯示出來,需要用戶拿著手機(jī)轉(zhuǎn)個身,將身后的怪物顯示在屏幕當(dāng)中,這里的Unity中有陀螺儀效果,喵喵會隨著屏幕旋轉(zhuǎn)而出現(xiàn)/隱藏于屏幕中,在iOS中是可以的,但是到了Android里就不行了,地圖可以旋轉(zhuǎn),但是Unity中的模型動不了。。。。。
android端開發(fā)頓時卡住了,而Unity開發(fā)大哥因為android這方面的問題就先顧iOS去了,先把iOS搞出來,再來解決android問題

替換方案

android這邊兩個問題最終還是沒解決,一個是4.2版本的打包問題(2017版Unity的可以打包),另一個就是陀螺儀失效問題。。。。
最后技術(shù)老大拍板拿了方案,地圖和怪物顯示都android來做,Unity只負(fù)責(zé)打怪場景(Unity開發(fā)版本用2017版),當(dāng)然,只是android端。。??啾?br> 只能在地圖上動手腳了,要在地圖上顯示怪物,只能看自定義 Marker,我是拿到當(dāng)前位置的經(jīng)緯度,然后通過隨機(jī)數(shù),在當(dāng)前位置上隨機(jī)增加或減少經(jīng)度和緯度,將怪物分布到當(dāng)前位置的附近,再設(shè)置MarkerOption來顯示怪物的圖片

        //隨機(jī)生成經(jīng)緯度
         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);

當(dāng)然這個效果和iOS端用Unity實現(xiàn)的效果比起來就差多了,但是我暫時沒其他解決辦法了。。。。一個是怪物不能動,不像Unity實現(xiàn)的那樣能夠動而且是3D效果的,地圖上只有一張圖片,第二個就是圖片還是固定大小的,不能設(shè)置,想把怪物顯示大一點都不行,若是哪位同學(xué)對高德地圖API里設(shè)置marker這塊比較熟悉的話望告知,謝謝

Unity退出問題

這里還有個坑爹問題,就是Unity退出的時候,會將整個進(jìn)程殺掉,導(dǎo)致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();
        }
        //這里是關(guān)鍵
        this.kill();
        g();
    }

關(guān)鍵代碼是在倒數(shù)第三行 this.kill(); 下面是kill()方法:

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

殺死進(jìn)程啊,有木有。。。。。所以APP就重啟了啊。。。。
網(wǎng)上千篇一律的都說加個按鈕調(diào)用mUnityPlayer.quity() 方法,或是在onBackPress方法中調(diào)用mUnityPlayer.quity() 方法,這樣和直接調(diào)用 mUnityPlayer.quity()有什么不一樣么,都重啟app了,或許我用的方法不對?
稍靠譜點的就是android端調(diào)用Unity方法,Unity端執(zhí)行退出,另一個方案就是將Unity單獨(dú)放另一個進(jìn)程里,這樣退出的話就不會殺死app所在的進(jìn)程了。
但是這兩種方案我都試過了,第一種,Unity端退出執(zhí)行的還是quity() 方法,最后還是把進(jìn)程給殺了,第二種試了也沒效果。。。
最后想了個笨方法,就是用戶點返回按鈕或是退出這個繼承 UnityPlayerActivity的Activity時,不將這個Activity 關(guān)閉,而是將這個Activity的啟動模式設(shè)置為singleInstance,即每次打開的時候?qū)⑵鋯为?dú)放在一個任務(wù)棧里(因為app主頁是singleTask模式,進(jìn)入主頁時,會將其上面的activity都清除出棧,為了避免UnityPlayerActivity子類被清除,所以將其設(shè)置為singleInstance,作為一個單例),這樣這個包含Unity游戲的Activity就不會finish掉,同時也解決了每次加載Unity游戲時時間過長的問題,就第一次加載時花費(fèi)一些時間,下次就不用再花時間來加載了。。。。。我們Unity加載時間得幾十秒鐘,得等十一過后,Unity開發(fā)大哥來解決這個問題了。

其他要注意的問題

1、Unity調(diào)用Android是在子線程中運(yùn)行的,所以如果涉及到UI操作,記得切換到主線程

 /**
     * 供Unity端調(diào)用
     * 接收Unity傳遞過來的數(shù)據(jù)
     *
     * @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);
                        //若未授權(quán)定位,則提示用戶去設(shè)置
                        if (!isGrant) {
                            showFinishActivityDialog();
                        }
                    }
                });
                break;
            case 3://打怪結(jié)果
                String[] result = MessageData.split("/");
                handleCatchResult(result[0],result[1],result[2]);
                break;
        }
    }

2、Unity端調(diào)用android 方法

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

上面的三個參數(shù),前兩個是固定不變的,即"com.unity3d.player.UnityPlayer"和"currentActivity" 這兩個參數(shù)是固定 不變的,之前,Unity開發(fā)那邊,換成了我們app的包名和 Unity所在的Activity名稱,發(fā)現(xiàn)怎么也調(diào)用不了。

上面就是在Android中嵌入Unity時,我遇到的一些坑,暫時只想到了這么多,后面想起來了再加上,同時如果有遇到同樣問題的同學(xué)可以提問,力所能及的話就幫著解決。同時,如果有對我上面遇到的問題有更好的解決辦法的,也請在下面留言,多謝多謝!
另外想學(xué)習(xí)Unity的同學(xué)可以去這位大神博客看看,挺詳細(xì)的

雨松MOMO---Unity

廢話:第一次在簡書寫博客,主要是太懶了。。。。明天就十一了,同事都下班了,國慶好好休息,這半個月累壞了,天天到家十二點、一點的,腦仁兒疼,下班走人。。。

更新:最后Unity退出還是用mUnityPlayer.quit()方法退出,不過Unity所在的Activity設(shè)置process屬性,單獨(dú)一個進(jìn)程,這樣退出時就會退出Unity所在的進(jìn)程,而不會退出我們自己的app了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容