Unity 與 Android交互通信 之OPPO篇

前言

??本人是Android SDK方向的開發(fā)者,在游戲發(fā)行公司工作藐石,因公司業(yè)務(wù)需求經(jīng)常與Unity進(jìn)行交互,借此機(jī)會讓大家伙了解下Unity與Android交互的一些基礎(chǔ)知識姐直。

1.開發(fā)環(huán)境說明

??Unity和Android Studio所涉及到的SDK岳锁、JDK、NDK安裝步驟新建工程等操作的不做說明

??Android Studio(AS)版本: 3.2.1

??Unity版本: 2018.2.0f2 破解版

2.實現(xiàn)效果

?Unity調(diào)用Andoroid網(wǎng)游OPPO SDK API 實現(xiàn) 登錄 支付 退出 等交互 效果如下圖

oppo_login.png
oppo_pay.png

oppo_exit.png

3.Android篇

要在Unity游戲項目中調(diào)用安卓API吊输,有兩種方式:

  1. Unity項目導(dǎo)出為Android工程(Build System選擇Gradle)饶号,然后在AS中進(jìn)行二次開發(fā),添加交互功能季蚂。這樣的方式開發(fā)起來很靈活茫船,改動起來也很方便,但是就是很麻煩扭屁,因為每次改動都要打一回安卓工程算谈。

  2. 將擴(kuò)展功能打成jar包,然后將jar包導(dǎo)入到Unity中料滥,直接使用然眼。這樣的方式,一次性寫好通用交互層 葵腹,不用頻繁打安卓工程高每。

下面給大家說下第二種方式

3.1Android 工程

1.既然要和Android oppo SDK交互 ,需要去開發(fā)者申請對應(yīng)參數(shù)和 SDK資源文檔

?OPPO開發(fā)者網(wǎng)游SDK下載地址:https://open.oppomobile.com/wiki/doc#id=10470

  1. AS新建一個工程礁蔗,因為需要和Unity交互(用到其中的類)觉义,因此需要把它提供的class.jar包放到AS工程libs下
    我Unity安裝默認(rèn)路徑在D盤 D:\Unity 所以下面路徑取決你Unity安裝路徑
    D:\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Development\Classes\class.jar

  2. 需要按oppo 開發(fā)者文檔要求 放入oppo資源(assets、libs和AndroidManifest.xml)浴井,然后可以按文檔接口接入


    androidPro.png


3.2AS清單文件配置注意點

清單文件配置一定要有<meta-data android:name="unityplayer.UnityActivity" android:value="true" />晒骇,
不然會有莫名其妙的奇怪問題

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity.demo">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round">
        <activity
            android:name="com.unity.demo.MainActivity"
            android:screenOrientation="portrait"
            android:theme="@android:style/Theme.Translucent.NoTitleBar"
            android:configChanges="orientation|keyboardHidden|screenSize|screenSize|navigation">

            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
       
        <!--oppo配置開始-->
        <meta-data
            android:name="debug_mode"
            android:value="false"/>
        <!--  日志開關(guān),發(fā)布時候設(shè)置為false  -->
        <meta-data
            android:name="is_offline_game"
            android:value="false"/>
        <!--  true:單機(jī)游戲   false:網(wǎng)游  -->
        <meta-data
            android:name="app_key"
            android:value=""/>
        <!-- appKey磺浙,游戲上線時請務(wù)必替換成游戲自身的appkey -->
        <uses-library android:name="org.apache.http.legacy" android:required="false" />
        <!--oppo配置結(jié)束-->
    </application>
</manifest>

3.3Android 交互層代碼編寫

??首先需要讓MainActivity繼承UnityPlayerActivity洪囤,因為Unity導(dǎo)出的app的視圖展示需要在UnityPlayerActivity下。

import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;
import com.nearme.game.sdk.GameCenterSDK;
import com.nearme.game.sdk.callback.ApiCallback;
import com.nearme.game.sdk.callback.GameExitCallback;
import com.nearme.game.sdk.common.model.biz.PayInfo;
import com.nearme.game.sdk.common.model.biz.ReportUserGameInfoParam;
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
  *android 與Unity交互層  數(shù)據(jù)均為模擬游戲數(shù)據(jù)
  */
public class MainActivity extends UnityPlayerActivity {
    private static String appSecret = ""; //此處應(yīng)該填寫 oppo后臺申請下來的appSecret參數(shù)
    public final  String TAG="UnityForOppo";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        doInit();  
    }
    /**
     * 初始化
     */
    public void doInit(){
        Log.i(TAG,"doInit");
        GameCenterSDK.init(appSecret, this);
        Toast.makeText(MainActivity.this,"初始化成功",Toast.LENGTH_LONG).show();
    }

    /**
     * 登錄
     */
    public void doLogin(){
        Log.i(TAG,"doLogin");
        GameCenterSDK.getInstance().doLogin(this, new ApiCallback() {
            //登錄成功的回調(diào)
            @Override
            public void onSuccess(String msg) {
                Toast.makeText(MainActivity.this,"獲取登錄狀態(tài):====="+msg.toString(),Toast.LENGTH_LONG).show();
                GameCenterSDK.getInstance().doGetTokenAndSsoid(new ApiCallback() {
                    @Override
                    public void onSuccess(String resultMsg) {
                        Log.i(TAG,"登錄成功獲取的msg:====="+resultMsg.toString());
                        String jsonString = "";
                        try {
                            JSONObject   json = new JSONObject(resultMsg);
                            String token = json.getString("token");
                            String ssoid = json.getString("ssoid");
                            json.put("token", URLEncoder.encode(token, "UTF-8"));
                            json.put("ssoid",json.getString("ssoid"));
                       
                            jsonString=json.toString();
                            Log.i(TAG,"登錄成功向Unity發(fā)送消息:====="+jsonString);
                            /**
                             * 向Unity傳遞消息
                             * 第1個參數(shù)為Unity場景中用于接收android消息的對象名稱
                             * 第2個參數(shù)為對象上的腳本的一個成員方法名稱(腳本名稱不限制)
                             * 第3個參數(shù)為Unity方法的參數(shù)
                             */
                            UnityPlayer.UnitySendMessage("AndroidSDKListener", "LoginCallback", jsonString);
                        } catch (JSONException e) {
                            e.printStackTrace();
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onFailure(String resultMsg, int code) {
                        UnityPlayer.UnitySendMessage("AndroidSDKListener", "LoginCallback", resultMsg);
                    }
                });

            }
            //登錄失敗的回調(diào)
            @Override
            public void onFailure(String msg, int code) {
                //回調(diào)oppo登錄失敗撕氧,把失敗的消息發(fā)給Unity
                UnityPlayer.UnitySendMessage("AndroidSDKListener", "LoginCallback", msg);
            }
        });
    }

    /**
     * 支付(喚起支付參數(shù)為模擬數(shù)據(jù) 真實應(yīng)該由游戲服務(wù)器端傳入)
     */
    public void doPay(){
        Log.i(TAG,"doPay");
        String orderId=System.currentTimeMillis()+"";//訂單號 建議由游戲服務(wù)器提供
        String productDesc="優(yōu)惠月禮包"; //商品描述
        String  productName="鉆石"; //商品名稱
        String  GAME_OPPO_URL="http://192.168.1.1:8080/order/oppo";//支付回調(diào)地址瘤缩,由服務(wù)器提供
        //參數(shù)1  游戲訂單號  參數(shù)2 附加參數(shù) 傳什么都可以 這里傳入訂單號  參數(shù)3 為支付金額 單位分
        PayInfo payInfo = new PayInfo(orderId, orderId, 1);
        payInfo.setProductDesc(productDesc);
        payInfo.setProductName(productName);
        payInfo.setCallbackUrl(GAME_OPPO_URL);
        GameCenterSDK.getInstance().doPay(this, payInfo, new ApiCallback() {

            @Override
            public void onSuccess(String msg) {
                Log.i(TAG,"PAY SUC");
            }

            @Override
            public void onFailure(String msg, int code) {
                Log.i(TAG,"PAY Fail========"+msg);
            }
        });
    }

    /**
     * 上報游戲數(shù)據(jù) 給OPPO
     */
    public  void doSubUserInfo (){
        Map<String, String> mRoleInfo =new HashMap<String, String>();
        mRoleInfo.put("type", "createRole");// 以下場景必傳[enterServer(登錄),levelUp(升級)伦泥,createRole(創(chuàng)建角色)剥啤,exitServer(退出)]
        mRoleInfo.put("roleId", "123456");// 當(dāng)前登錄的玩家角色I(xiàn)D,若無锦溪,可傳入userid
        mRoleInfo.put("roleName", "天下第一");// 當(dāng)前登錄的玩家角色名,不能空
        mRoleInfo.put("roleLevel", "10");// 當(dāng)前登錄的玩家角色等級,不能為空府怯,必須為數(shù)字刻诊,且不能為null,若無牺丙,傳入0
        mRoleInfo.put("serverId", "1001");// 當(dāng)前登錄的游戲區(qū)服ID则涯,不能為空,必須為數(shù)字冲簿,若無粟判,傳入0
        mRoleInfo.put("serverName", "Oppo32服");// 當(dāng)前登錄的游戲區(qū)服名稱,不能為空,長度不超過50,不能為null峦剔,若無档礁,傳入“無”

        if (mRoleInfo != null && "createRole".equals(mRoleInfo.get("type")) || ("enterServer".equals(mRoleInfo.get("type"))) || ("levelUp".equals(mRoleInfo.get("type")))) {
            String serverId = mRoleInfo.get("serverId");
            String serverName = mRoleInfo.get("serverName");
            String roleId = mRoleInfo.get("roleId");
            String roleName = mRoleInfo.get("roleName");
            String roleLevel = mRoleInfo.get("roleLevel");
            GameCenterSDK.getInstance().doReportUserGameInfoData(new ReportUserGameInfoParam(roleId, roleName, Integer.valueOf(roleLevel), roleId, roleName, "", new TreeMap<String, Number>()), new ApiCallback() {
                @Override
                public void onSuccess(String msg) {
                }
                @Override
                public void onFailure(String msg, int code) {

                }
            });
        }

    }
    /**
     * 退出游戲
     */
    public void  doExitGame(){
        Log.i(TAG,"DoExitGame");
        GameCenterSDK.getInstance().onExit(this,new GameExitCallback() {
            @Override
            public void exitGame() {
                finish();
                System.exit(0);
                android.os.Process.killProcess(android.os.Process.myPid());
            }
        });
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.i(TAG, "onKeyDown");
        if ((keyCode == KeyEvent.KEYCODE_BACK)) {
            Log.i(TAG, "DoExitGameDoExitGameDoExitGame");
            doExitGame();
            return false;
        } else {
            return super.onKeyDown(keyCode, event);
        }
    }

3.4AS生成JAR包

?app/build.gradle 下加入生成jar包Task

task makeJar(type: Copy) {
    def jarPath = 'build/intermediates/packaged-classes/debug/'  
    def jarName = 'classes.jar'
    from(jarPath)
    into('build/outputs/')  //輸出路徑為outputs/
    include(jarName)
    rename(jarName, 'unityPlug-1.4.jar') //重名為xxx.jar
}



建議: 先Rebuild Project一下生成packaged-classes文件,然后在Gradle \other下能看到makeJar 點擊執(zhí)行Task

makeJar.png

4.Unity篇

  1. 新建一個Unity工程羊异,在Assets目錄下新建Plugins/Android/ 目錄
  2. 將Android中的assets事秀、res、AndroidManifest.xml 野舶、libs下的gamesdk-20190910.aar還有生成的unityPlug-1.4.jar 放入Assets/Plugins/Android目錄下(在Unity這些東西都會被當(dāng)做資源處理)
  3. 在Assets/Scenes/ 建立一個場景,在場景上創(chuàng)建一個Canvas宰衙,并創(chuàng)建一個名為"AndroidSDKListener"的對象,在AndroidSDKListener之下再放三個按鈕觸發(fā)安卓 登錄 支付 退出游戲
  4. 創(chuàng)建一個btnClick.cs文件平道,將腳本掛載在AndroidSDKListener對象 如圖所示


android.png
scenes.png
unityPro.png


4.1 Unity中C#代碼實現(xiàn)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class btnClick : MonoBehaviour
{

    private AndroidJavaClass jc;
    private AndroidJavaObject jo;
    private Button btnLogin;
    private Button btnPay;
    private Button btnExitGame;
   
    public void Start()
    {   
        //固定寫法
        jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
        btnLogin = transform.Find("BtnLogin").GetComponent<Button>(); //登錄
        btnPay= transform.Find("BtnPay").GetComponent<Button>();   //支付
        btnExitGame = transform.Find("BtnExit").GetComponent<Button>();   //退出游戲
        btnLogin.onClick.AddListener(OnBtnLoginClickHandler);
        btnPay.onClick.AddListener(OnBtnPayClickHandler);
        btnExitGame.onClick.AddListener(OnBtnExitGameClickHandler);
    }
  
    /**
      * 登錄調(diào)用
     */
    private void OnBtnLoginClickHandler()
    {
        jo.Call("doLogin");
    }
   
  /**
      * 支付調(diào)用
     */
     private void OnBtnPayClickHandler()
     {
         jo.Call("doPay");
     }

    /**
     * 退出游戲點擊調(diào)用
     */
    private void OnBtnExitGameClickHandler()
    {
        jo.Call("doExitGame");
       // Application.Quit();//調(diào)用C#退出應(yīng)用
    }
 
    /**
      *接收從安卓端傳過來消息
      *方法體 UnityPlayer.UnitySendMessage("AndroidSDKListener", "LoginCallback", msg);和參數(shù)2對應(yīng)
     */
    public  void LoginCallback(string msg)
    {
        Debug.Log("Login_with_msg : " + msg);
    }
}

4.2 打包測試

File ----Build Settings----Android----Player Settings -----Build System-----選擇Internal (直接生成APK整包)
配置好簽名(jdk sdk ndk均早配置好了) 選擇bulid 靜候幾秒 完整的APK就搞定了
登錄、支付供炼、 退出效果 上面已經(jīng)展示了一屋,看下安卓登錄回調(diào)成功后向Unity發(fā)送的消息

login_msg.png

結(jié)語

??記錄下自己的學(xué)習(xí)和工作經(jīng)驗,分享給有需要的人袋哼。如果有那里寫的不對或者不理解,歡迎大家的指正冀墨。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市涛贯,隨后出現(xiàn)的幾起案子诽嘉,更是在濱河造成了極大的恐慌,老刑警劉巖弟翘,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虫腋,死亡現(xiàn)場離奇詭異,居然都是意外死亡稀余,警方通過查閱死者的電腦和手機(jī)悦冀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睛琳,“玉大人盒蟆,你說我怎么就攤上這事踏烙。” “怎么了历等?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵讨惩,是天一觀的道長。 經(jīng)常有香客問我募闲,道長步脓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任浩螺,我火速辦了婚禮靴患,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘要出。我一直安慰自己鸳君,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布患蹂。 她就那樣靜靜地躺著或颊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪传于。 梳的紋絲不亂的頭發(fā)上囱挑,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天,我揣著相機(jī)與錄音沼溜,去河邊找鬼平挑。 笑死,一個胖子當(dāng)著我的面吹牛系草,可吹牛的內(nèi)容都是我干的通熄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼找都,長吁一口氣:“原來是場噩夢啊……” “哼唇辨!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起能耻,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤赏枚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后嚎京,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗡贺,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年鞍帝,在試婚紗的時候發(fā)現(xiàn)自己被綠了诫睬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡帕涌,死狀恐怖摄凡,靈堂內(nèi)的尸體忽然破棺而出续徽,到底是詐尸還是另有隱情,我是刑警寧澤亲澡,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布钦扭,位于F島的核電站,受9級特大地震影響床绪,放射性物質(zhì)發(fā)生泄漏客情。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一癞己、第九天 我趴在偏房一處隱蔽的房頂上張望膀斋。 院中可真熱鬧,春花似錦痹雅、人聲如沸仰担。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至渣聚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贮尉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工朴沿, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留绘盟,地道東北人。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓悯仙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吠卷。 傳聞我的和親對象是個殘疾皇子锡垄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360