亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android集成Unity的兩種方案

 更新時(shí)間:2024年05月06日 08:40:15   作者:Stephen10086  
現(xiàn)在市面上的形形色色Android客戶端,為了更優(yōu)的用戶體驗(yàn),我們開(kāi)發(fā)的上游產(chǎn)品和交互往往會(huì)在界面里設(shè)計(jì)很多動(dòng)效,傳統(tǒng)的一頁(yè)頁(yè)的靜態(tài)展示頁(yè)面已經(jīng)不足以滿足用戶的審美需求了,本文將給大家分享Android集成Unity的兩種方案,感興趣的朋友可以參考下

?Android平臺(tái)常見(jiàn)動(dòng)效

現(xiàn)在市面上的形形色色Android客戶端,為了更優(yōu)的用戶體驗(yàn),我們開(kāi)發(fā)的上游產(chǎn)品和交互往往會(huì)在界面里設(shè)計(jì)很多動(dòng)效。傳統(tǒng)的一頁(yè)頁(yè)的靜態(tài)展示頁(yè)面已經(jīng)不足以滿足用戶的審美需求了。

而動(dòng)效的分類也是花樣百出的,以播放時(shí)機(jī)來(lái)說(shuō)有點(diǎn)擊觸發(fā),打開(kāi)頁(yè)面觸發(fā),還有可跟隨手指的交互持續(xù)觸發(fā)的等等。有時(shí)候一些和數(shù)據(jù)耦合性較大的動(dòng)效甚至需要我們自己來(lái)手寫(xiě)復(fù)雜的自定義View,比如曲線圖、圖表類型。

而我 日常碰到的大部分的動(dòng)效需求,還是依賴UI設(shè)計(jì)的同時(shí)來(lái)制作提供的,像那些短時(shí)間單次的展示類動(dòng)效,往往實(shí)現(xiàn)方式比較隨意,對(duì)資源的格式要求也不太嚴(yán)苛。一般有以下幾種方案:

幀動(dòng)畫(huà)

在Android中,幀動(dòng)畫(huà)是通過(guò)Drawable動(dòng)畫(huà)實(shí)現(xiàn)的。你可以創(chuàng)建一個(gè)AnimationDrawable對(duì)象,然后在XML中定義一系列的幀(frames),每幀可以是一個(gè)Drawable資源。然后在代碼中啟動(dòng)這個(gè)動(dòng)畫(huà)。

以下是兩個(gè)簡(jiǎn)單的例子:

1. 在res/drawable目錄下創(chuàng)建一個(gè)名為frame_animation.xml的文件,并定義動(dòng)畫(huà)的幀:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"

android:oneshot="false">

<item android:drawable="@drawable/frame1" android:duration="100" />

<item android:drawable="@drawable/frame2" android:duration="100" />

<item android:drawable="@drawable/frame3" android:duration="100" />

<!-- 更多幀 -->

</animation-list>

這里android:oneshot="false"表示動(dòng)畫(huà)會(huì)循環(huán)播放,如果設(shè)置為true則播放一次。android:duration表示每幀顯示的時(shí)間。

2. 在你的布局文件中(例如activity_main.xml),添加一個(gè)ImageView來(lái)展示動(dòng)畫(huà):

<ImageView

android:id="@+id/imageView"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:src="@drawable/frame_animation" />

然后在調(diào)用處啟動(dòng)動(dòng)畫(huà):

ImageView imageView = (ImageView) findViewById(R.id.imageView);

// 獲取AnimationDrawable

final AnimationDrawable frameAnimation = (AnimationDrawable) imageView.getDrawable();

// 在UI線程之外啟動(dòng)動(dòng)畫(huà)

imageView.post(new Runnable() {

    @Override

    public void run() {

        frameAnimation.start();

    }

});

注意確保你的每個(gè)Drawable資源的尺寸是一致的,以便在動(dòng)畫(huà)過(guò)程中保持幀的正確顯示。這樣就創(chuàng)建了一個(gè)簡(jiǎn)單的幀動(dòng)畫(huà),當(dāng)Activity加載時(shí),動(dòng)畫(huà)會(huì)自動(dòng)開(kāi)始循環(huán)播放。

PAG動(dòng)畫(huà)

pag相較于上面的幀動(dòng)畫(huà)對(duì)性能更加友好。PAG是騰訊公司自主研發(fā)的一套完整動(dòng)畫(huà)工作流解決方案。 PAG誕生于2016年,最初的原因是為了解決更為復(fù)雜的視頻編輯場(chǎng)景下動(dòng)畫(huà)渲染問(wèn)題,同時(shí)又覆蓋了UI動(dòng)畫(huà)和直播場(chǎng)景,于2022年1月在Github開(kāi)源。

其使用方法可以說(shuō)相當(dāng)簡(jiǎn)單,只需要先從github主頁(yè)確定版本,到gradle里引入依賴,

implementation 'com.tencent.tav:libpag:3.2.7.40'

然后在我們應(yīng)用的xml布局中放置pagView,沒(méi)有額外的屬性需要配置:

 <org.libpag.PAGView
        android:id="@+id/pagview"
        android:layout_width="@dimen/dp_1190"
        android:layout_height="@dimen/dp_1110"
        android:layout_marginTop="@dimen/dp_290"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

最后在代碼里設(shè)置其文件源,循環(huán)方式,調(diào)用播放即可

  @Override
    protected void onCreate(Bundle savedInstanceState) {
        LogUtils.d("AirConditioner Start");
        super.onCreate(savedInstanceState);
        setContentView(R.layout.instance);

        PAGView pagView = findViewById(R.id.pagview); 
        PAGFile pf = PAGFile.Load(getContext().getAssets(), fileName);
        pagView.setComposition(pf);
        pagView.setRepeatCount(-1);
        pagView.play();
    }

MP4動(dòng)畫(huà)

這個(gè)單次動(dòng)效的實(shí)現(xiàn)方案是最簡(jiǎn)單的,不寫(xiě)demo演示了,直接獲取mediaplayer實(shí)例,綁定surfaceView或者TextureView,再填文件,播放視頻即可。需要關(guān)注的是surfaceView播放視頻一開(kāi)始可能會(huì)有黑屏問(wèn)題,可以用靜態(tài)圖占位。

可交互的動(dòng)效

顧名思義,這一類動(dòng)效需要能跟隨用戶的操作而實(shí)時(shí)改變表現(xiàn)形式。最常見(jiàn)的就是吸頂動(dòng)效,例如在一個(gè)列表滑動(dòng)過(guò)程中,會(huì)監(jiān)聽(tīng)列表的滑動(dòng)距離,對(duì)界面頂部或者側(cè)邊的其他View位置和透明度,顏色等做動(dòng)態(tài)設(shè)置。還有systemui的allapp界面的翻頁(yè)動(dòng)畫(huà),負(fù)一屏下拉的時(shí)候,根據(jù)距離對(duì)桌面背景做高斯模糊處理等等。

Kanzi動(dòng)效

跟手可互動(dòng)的動(dòng)效,也不得不談kanzi動(dòng)效。以下介紹來(lái)自百科與官網(wǎng):

Kanzi產(chǎn)品是行業(yè)領(lǐng)先的3D引擎和UI開(kāi)發(fā)工具,支持高效率沉浸式3D效果,跨系統(tǒng)多屏互聯(lián)并能與安卓生態(tài)完美融合,已經(jīng)成為全球主流車廠智能座艙首選的UI開(kāi)發(fā)工具和引擎。更新后的Kanzi架構(gòu)可與安卓操作系統(tǒng)、生態(tài)系統(tǒng)深度兼容。Kanzi可基于安卓的任何功能提供強(qiáng)大的圖形設(shè)計(jì)支持,確保高質(zhì)量的圖像效果。

對(duì)于Kanzi動(dòng)效的集成使用方式,因?yàn)闆](méi)有自己從頭開(kāi)始對(duì)接,我只按照順序一筆帶過(guò),有不對(duì)的地方歡迎指正。首先我們集成kanzi運(yùn)行所需的Runtime.aar,kanziJava支持庫(kù)aar,資源文件,資源列表的txt等等,還需要在gradle里寫(xiě)明不可壓縮的文件類型,以防止無(wú)法加載資源。

在使用上,我們先在XML布局中聲明,同時(shí)通過(guò)屬性填入asstes里的資源名,和資源文件綁定:

 <com.rightware.kanzi.KanziTextureView
        android:id="@+id/tx_KanziSurfaceView"
        android:layout_width="@dimen/dp_2560"
        android:layout_height="@dimen/dp_1190"
        app:clearColor="@android:color/transparent"
        app:kzbPathList="climate.kzb"
        app:layout_constraintTop_toTopOf="parent"
        app:name="climate"
        app:startupPrefabUrl="kzb://climate/StartupPrefab"
        tools:ignore="MissingConstraints" />

在代碼里我們需要設(shè)置通信的工具類,在里面添加監(jiān)聽(tīng)器來(lái)接收和上行下行信號(hào)的交互:

// 數(shù)據(jù)接口定義
public interface AndroidNotifyListener {
    void notifyDataChanged(String name, String value);

    void dataSourceFinish();
}

// 添加數(shù)據(jù)接收監(jiān)聽(tīng)和下行通信
AndroidUtils.setListeners(this);
AndroidUtils.removeListeners(this);
AndroidUtils.setValue(SourceData.RightMidMove_up2down, y);

Unity動(dòng)效

Unity的大名在游戲界可謂如雷貫耳,記得小時(shí)候玩的很多游戲的開(kāi)屏界面即有一個(gè)大大的Unity字樣和圖標(biāo)。

以下介紹來(lái)自百科和官網(wǎng):Unity是實(shí)時(shí)3D互動(dòng)內(nèi)容創(chuàng)作和運(yùn)營(yíng)平臺(tái)。包括游戲開(kāi)發(fā)、美術(shù)、建筑、汽車設(shè)計(jì)、影視在內(nèi)的所有創(chuàng)作者,借助Unity將創(chuàng)意變成現(xiàn)實(shí)。Unity平臺(tái)提供一整套完善的軟件解決方案,可用于創(chuàng)作、運(yùn)營(yíng)和變現(xiàn)任何實(shí)時(shí)互動(dòng)的2D和3D內(nèi)容,支持平臺(tái)包括手機(jī)、平板電腦、PC、游戲主機(jī)、增強(qiáng)現(xiàn)實(shí)和虛擬現(xiàn)實(shí)設(shè)備。Unity作為全球領(lǐng)先的 3D 引擎之一,團(tuán)結(jié)引擎可以為 3D HMI提供全棧支持。即為從概念設(shè)計(jì)到量產(chǎn)部署的整個(gè) HMI 工作流程提供創(chuàng)意咨詢、性能調(diào)優(yōu)、項(xiàng)目開(kāi)發(fā)等解決方案,從而為車載信息娛樂(lè)系統(tǒng)和智能駕駛座艙打造令人驚嘆的交互式體驗(yàn)。

其實(shí)在第一版我們項(xiàng)目集成的是Kanzi方案,其性能表現(xiàn)較Unity要差一些,關(guān)鍵是項(xiàng)目推進(jìn)的過(guò)程中,對(duì)方工程師對(duì)動(dòng)效樣式的優(yōu)化也達(dá)不到評(píng)測(cè)部門的要求,后來(lái)更新迭代我們就更換了Unity方案。而本文的重點(diǎn)也是在于Unity3D動(dòng)效的使用,案例為車載IVI系統(tǒng)空調(diào)app的風(fēng)向調(diào)節(jié),交互邏輯比上面舉的例子更加復(fù)雜,需要實(shí)時(shí)跟手,在交互熱區(qū)范圍內(nèi)需要不斷變化動(dòng)效形態(tài),并完成雙向通信,保證動(dòng)效和車載信號(hào)的一致性。

Unity集成的兩種方案

前面做了這些動(dòng)效的鋪墊,終于進(jìn)入正題了。本文暫時(shí)不深究Unity的渲染原理,只談集成和使用。

通信協(xié)議制定

第一步不是創(chuàng)建工程,而是要提前根據(jù)HMI的產(chǎn)品交互定義來(lái)指定和Unity之間的通信協(xié)議。有哪些功能是有開(kāi)關(guān)的,需要調(diào)整哪些屬性??照{(diào)app里就涉及幾個(gè)出風(fēng)口的打開(kāi)關(guān)閉,可以以0/1來(lái)區(qū)分。還有風(fēng)口的方向調(diào)節(jié),需要互傳x,y坐標(biāo)值。Android和Unity之間的是采用JSON字符串來(lái)通信的,對(duì)于JSON字符串的的打包與解包通過(guò)谷歌的Gson等三方庫(kù)來(lái)操作,相當(dāng)簡(jiǎn)單。

而且,兩方通信鏈路和Unity的集成方式還有關(guān),像下面要談到的第一種進(jìn)程隔離方案,就是通過(guò)集成全量的Unity依賴包,利用其中的JNI接口來(lái)通信的,而Client/Server架構(gòu)就是通過(guò)Android的AIDL接口來(lái)和單獨(dú)的服務(wù)端進(jìn)程通信的。另外交互的車載信號(hào)鏈路方案涉及項(xiàng)目架構(gòu)機(jī)密,此處不作描述。

進(jìn)程隔離方案-UAAL(Render As Library)

這種方式集成的話,Unity會(huì)將渲染引擎,資源文件,和Android上層的通信代碼都打包導(dǎo)出到一個(gè)aar中,其體積隨動(dòng)效的復(fù)雜程度而變化,同時(shí)會(huì)使集成方的apk包體積增加。而且項(xiàng)目里有多少方要使用Unity動(dòng)效,就需要多少份的渲染引擎。這個(gè)方案由客戶端來(lái)負(fù)責(zé)Unity控件的創(chuàng)建銷毀,顯示隱藏,一般適用一對(duì)一,通信鏈路簡(jiǎn)單的,即項(xiàng)目中可能只有一個(gè)模塊需要使用Unity動(dòng)效的情況。在多模塊需要使用Unity的情況下,進(jìn)程隔離的方案對(duì)性能的占用也比較高。

上層使用到的控件——UnityPlayer,它是一個(gè)Unity自定義的FrameLayout,里面有他們自己實(shí)現(xiàn)的一系列添加view,顯示,和渲染邏輯。資源文件均存在于Unity打的依賴包中,對(duì)外不開(kāi)放。

集成步驟

進(jìn)程隔離的集成方式如下:

第一步,將Unity提供的aar放置于libs文件夾中,并在gradle里添加其編譯引用。

implementation files('libs/UnityAnimation_0321V4.aar')

第二步,gradle中配置Unity所需的NDK版本,配置abifilters,設(shè)置要將哪些架構(gòu)的動(dòng)態(tài)庫(kù)打包到apk中,對(duì)于車機(jī)項(xiàng)目來(lái)說(shuō)只需要固定的某一種架構(gòu)即可。還有設(shè)置不壓縮的文件類型,使Unity可以順利找到資源使用。

ndkVersion "23.1.7779620"

aaptOptions {
        noCompress = ['.tj3d', '.ress', '.resource', '.obb', '.bundle', '.tuanjieexp', 'global-metadata.so'] + tuanjieStreamingAssets.tokenize(', ')
        ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~"
    }

ndk{
    abiFilters 'arm64-v8a'
}


注意,我們還需要在項(xiàng)目的string.xml資源文件中添加Unity所需的一條String資源,否則Unity側(cè)會(huì)空指針。

 <string name="game_view_content_description">Game view</string>

第三步,將要顯示Unity動(dòng)效的頁(yè)面Activity改為繼承自UnityPlayerActivity,Unity的核心顯示控件,UnityPlayer,它的創(chuàng)建銷毀,顯示隱藏,由這個(gè)UnityPlayerActivity來(lái)統(tǒng)一管理,項(xiàng)目中集成這個(gè)Activity的子類再將mUnityPlayer通過(guò)addView添加到自己的根布局ViewGroup中當(dāng)背景即可,而且可以在xml上面繼續(xù)增加其他View控件。

第四步,封裝Unity通信工具類,Android給Unity發(fā)消息可以直接通過(guò)UnityPlayer的sendMessage靜態(tài)方法,傳入U(xiǎn)nity通信協(xié)議中指定的類名。

 UnityPlayer.UnitySendMessage(OBJ_NAME, METHOD_NAME, communicateMessage)

Unity使用C#開(kāi)發(fā),其給Android上層發(fā)消息則是通過(guò)反射回調(diào)信號(hào)類里的方法實(shí)現(xiàn)的,所以我們最好將信號(hào)管理類做成單例的,并給其Unity留下一個(gè)方法或者成員,可以拿到我們類的實(shí)例,順利反射回調(diào)。我這里使用的是一個(gè)Kotlin類聲明,并對(duì)外暴露一個(gè)公開(kāi)的unityInstance成員。而這個(gè)方法onReceiveMsgFromUnity,即是Unity的反射調(diào)用,我們?cè)谄渲羞M(jìn)行信號(hào)的解析,并傳到View中去,注意這個(gè)方法不是在主線程中反射的,所以后面需要優(yōu)化一波。

object UnityMessageHelper {

    val unityInstance = this

    // Unity給Android的消息回調(diào)
    fun onReceiveMsgFromUnity(msg: String) {
        LogUtils.d(TAG, "onReceiveMsgFromUnity: $msg")
        if (listenerList.size > 0) {
            listenerList.forEach {
                it.onReceiveUnityMessage(msg)
            }
        }
    }
}

信號(hào)類UnityMessageHelper的優(yōu)化

由于我們的目標(biāo)工程是空調(diào)app,在用戶調(diào)節(jié)風(fēng)向時(shí)的回調(diào)頻率相當(dāng)高,而自動(dòng)掃風(fēng)模式下,底層上傳的數(shù)據(jù)頻率也相當(dāng)高,所以不適合到主線程中操作這么多的數(shù)據(jù),我們用協(xié)程,配合Default調(diào)度器來(lái)處理這種CPU密集型的任務(wù)。兩條鏈路,用戶手指的拖動(dòng)操作時(shí),Unity反射回調(diào)的線程本身都是工作線程了,所以我們?cè)谑褂米远x的接口回調(diào)到View類的時(shí)候,使用MainScope.launch包一層,確保是到主線程更新我們的UI。而自動(dòng)掃風(fēng)模式從域控制器接收到風(fēng)口點(diǎn)擊的坐標(biāo)值時(shí),我們拿到數(shù)據(jù)后給Unity下發(fā)信號(hào),更新動(dòng)效的指向位置??梢允褂脜f(xié)程上下文切換,withContext(Dispatcher.Default)將其切到工作線程里發(fā)送給Unity。

遇到的問(wèn)題

Unity方給的aar里的基類Activity適用與絕大多數(shù)的普通應(yīng)用,但是我這里空調(diào)app的定位是一個(gè)懸浮的臨時(shí)面板,其實(shí)我的工程里壓根就沒(méi)有Activity,而是采用WindowManager來(lái)添加高層級(jí)窗口的View的形式來(lái)展示界面。

這個(gè)時(shí)候我們用不上他們定義的UnityPlayerActivity,只能使用原生Raw的UnityPlayer,自己管理其創(chuàng)建,銷毀,resume和pause。這里需要注意的是,UnityPlayer的創(chuàng)建需要傳一個(gè)Context上下文,而應(yīng)用里又沒(méi)有Activity類型的Context,故只能使用非Activity類型的Context,而且實(shí)踐中發(fā)現(xiàn),這個(gè)UnityPlayer的實(shí)例必須是我們的應(yīng)用拿到可用的窗口句柄之后,才能被成功創(chuàng)建,否則就會(huì)報(bào)錯(cuò)。

所以正確的創(chuàng)建與初始化順序是先使用WindowManager添加一個(gè)xml布局inflate來(lái)的ViewGroup,在其onAttachToWindow的方法回調(diào)之后,再創(chuàng)建UnityPlayer的實(shí)例,并添加到這個(gè)ViewGroup的布局中去,調(diào)用其resume方法。

    public void initUnity() {
        if (mUnityPlayer == null) {
            LogUtils.i(TAG, "initUnity");
            unityInitView = (LinearLayout) LayoutInflater.from(mContext).inflate(R.layout.layout_unity_init, null);
            unityInitView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
                @Override
                public void onViewAttachedToWindow(@NonNull View v) {
                    LogUtils.w(TAG, "unityInitView onViewAttachedToWindow");
                    mUnityPlayer = new UnityPlayer(mContext);
                    unityInitView.addView(mUnityPlayer);
                    mUnityPlayer.requestFocus();
                    mUnityPlayer.resume();
                    mUnityPlayer.windowFocusChanged(true);
                }

                @Override
                public void onViewDetachedFromWindow(@NonNull View v) {
                    LogUtils.w(TAG, "unityInitView onViewDetachedFromWindow");
                    // Unityplayer已經(jīng)成功移除,通知airView將player添加進(jìn)去
                    airConditionerView.addUnityToAirView();
                }
            });

           mWindowManager.addView(unityInitView, initUnityWindowParams());
        }
    }

注意,這樣添加的UnityPlayer有一個(gè)無(wú)法解決的黑屏問(wèn)題,因?yàn)閁nity的渲染加載至少都需要4,5秒,期間我們只能在更上層的View里設(shè)置靜態(tài)背景圖覆蓋上去,等Unity加載完畢,發(fā)送ready的回調(diào)之后,我們移除掉這個(gè)占位的靜態(tài)圖,展示Unity動(dòng)效的界面。這也是進(jìn)程隔離的方案的一個(gè)很棘手的問(wèn)題。我的解決方案是在開(kāi)機(jī)的時(shí)候往屏幕外添加一個(gè)View專門來(lái)初始化加載Unity,加載完畢后,再將UnityPlayer給從里面remove掉,重新添加到實(shí)際的要展示的窗口中去,這樣打開(kāi)界面的時(shí)候可以略去加載的耗時(shí),稍微減少頁(yè)面僵直的時(shí)間。

單進(jìn)程-URAS(Render As Service)

Unity Rendering as Service(簡(jiǎn)稱URAS) 的渲染方案是團(tuán)結(jié)引擎特有的,無(wú)需在多個(gè)安卓應(yīng)用中集成多個(gè)Unity 3D player,而是后臺(tái)運(yùn)行,前端應(yīng)用可直接調(diào)用,節(jié)省系統(tǒng)資源,更適合多應(yīng)用動(dòng)效一鏡到底的設(shè)計(jì)。

相較進(jìn)程隔離方案的優(yōu)勢(shì)

這個(gè)方案是在UAAL方案的基礎(chǔ)上升級(jí)的,所以有一些前期工作是重復(fù)的,不作重復(fù)的闡述。

它是將要顯示的幾個(gè)Unity引擎都打包到同一個(gè)Server服務(wù)端去統(tǒng)一管控。其實(shí)服務(wù)端的apk打包也是拿到Unity提供的服務(wù)端AAR打進(jìn)一個(gè)空工程,內(nèi)部邏輯也隱藏到了AAR中。服務(wù)端和客戶端的通信采用我們熟知的AIDL接口來(lái)實(shí)現(xiàn)。而且這個(gè)服務(wù)端我們需要設(shè)置為persistent應(yīng)用,使其能開(kāi)機(jī)自啟,自動(dòng)執(zhí)行渲染等工作,其他應(yīng)用有顯示需求可以秒開(kāi),并且長(zhǎng)時(shí)間不顯示也不會(huì)自己回收資源了,客戶端的黑屏問(wèn)題也可以解決了。

相比于UAAL方案,客戶端需要集成的是一個(gè)體積很小的Client.aar,對(duì)于客戶端apk的體積控制是有優(yōu)勢(shì)的。

集成與使用方式

我們只需要在gradle里引入這個(gè)客戶端aar。在gradle sync之后,將遠(yuǎn)程的UnityView添加到自己的布局中去,配置好display參數(shù)(用來(lái)給服務(wù)端區(qū)分是哪個(gè)引擎的內(nèi)容),并指定服務(wù)端的包名。承載的View類型有SurfaceView和TextureView兩種,而我的應(yīng)用界面因?yàn)槭且粋€(gè)懸浮窗口,設(shè)計(jì)有進(jìn)出場(chǎng)的漸隱漸出動(dòng)效,而SurfaceView不可以線性地設(shè)置alpha動(dòng)畫(huà),所以選取TextureView來(lái)當(dāng)作容器。

<com.unity3d.renderservice.client.TuanjieView
        android:id="@+id/unityview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:tuanjieDisplay="2"
        app:tuanjieServicePkgName="com.tuanjie.renderservice"
        app:tuanjieViewType="TextureView" />

剩余的代碼邏輯僅僅是服務(wù)端Service的啟動(dòng),添加服務(wù)連接的回調(diào),消息回調(diào)。由于服務(wù)端為若干個(gè)Client的公共引擎,所以連resume和pause都不需要處理,因?yàn)檫@兩個(gè)操作會(huì)對(duì)所有的客戶端都生效。我們只需要確保啟動(dòng)服務(wù),并使用正確的display即可,面板退到后臺(tái)可以使用setVisbility來(lái)控制其顯示隱藏。除此之外,我們的通信工具類,UnityMessageHelper還需要實(shí)現(xiàn)兩個(gè)接口,一個(gè)服務(wù)連接狀態(tài)接口,一個(gè)業(yè)務(wù)數(shù)據(jù)的消息回調(diào)接口,代碼如下:

object UnityMessageHelper : TuanjieRenderService.Callback, SendMessageCallback {

    fun initUnityService() {
        LogUtils.i(TAG, "initUnityService")
        unityRenderService = 
            TuanjieRenderService.init(appContext,TUANJIE_PACKAGENAME).apply {
                enableAutoReconnect = true
                addCallback(this@UnityMessageHelper)
                addSendMessageCallback(this@UnityMessageHelper)
                ensureStarted()
        }
    }


    override fun onServiceConnected() {
        LogUtils.w(TAG, "onUnityRenderServiceConnected")
    }
   
    override fun onServiceDisconnected() {
        LogUtils.w(TAG, "onUnityRenderServiceDisConnected")
        messageScope.launch {
            delay(500L)
            initUnityService()
            unityRenderService.resume()
        }
    }
   
    override fun onServiceStartRenderView(p0: Int) {
        LogUtils.i(TAG, "onServiceStartRenderView")
    }

    override fun onClientRecvMessage(message: String?) = null

    // 服務(wù)端的消息回調(diào)
    override fun onClientRecvMessageWithNoRet(msg: String?) {
        // 回調(diào)消息的解析

    }

}

同時(shí)Android給Unity發(fā)信號(hào)的方法也從UnityPlayer的靜態(tài)方法換成了這個(gè)服務(wù)實(shí)例的方法調(diào)用:

  unityRenderService.c2sSendMessage(OBJ_NAME, METHOD_NAME, communicateMessage)

接收Unity的回調(diào)消息也換成了addSendMessageCallback里設(shè)置的回調(diào)方法。

可以說(shuō)URAS方案由于其統(tǒng)一管控,一對(duì)多的特點(diǎn),在性能和客戶端的易集成性方面,是優(yōu)于UAAL方案的??梢詮募軜?gòu)層面上,聯(lián)動(dòng)更多的動(dòng)效使用模塊,實(shí)現(xiàn)一鏡到底的絲滑轉(zhuǎn)場(chǎng)。

結(jié)語(yǔ)

以上就是本文對(duì)于Android常用幾種動(dòng)效的闡述以及對(duì)兩種常見(jiàn)的Unity集成方案記錄。后續(xù)我們除了在最表面的使用層面上,還可以進(jìn)一步挖掘其原理,甚至自己使用Unity的開(kāi)發(fā)工具,自己體驗(yàn)一把動(dòng)效的制作和接入,做到全鏈路知己知彼,才可以更高效的集成Unity為自己的應(yīng)用錦上添花。

到此這篇關(guān)于Android集成Unity的兩種方案的文章就介紹到這了,更多相關(guān)Android集成Unity內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論