前言
??本人是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) 登錄 支付 退出 等交互 效果如下圖
3.Android篇
要在Unity游戲項目中調(diào)用安卓API吊输,有兩種方式:
Unity項目導(dǎo)出為Android工程(Build System選擇Gradle)饶号,然后在AS中進(jìn)行二次開發(fā),添加交互功能季蚂。這樣的方式開發(fā)起來很靈活茫船,改動起來也很方便,但是就是很麻煩扭屁,因為每次改動都要打一回安卓工程算谈。
將擴(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
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-
需要按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
4.Unity篇
- 新建一個Unity工程羊异,在Assets目錄下新建Plugins/Android/ 目錄
- 將Android中的assets事秀、res、AndroidManifest.xml 野舶、libs下的gamesdk-20190910.aar還有生成的unityPlug-1.4.jar 放入Assets/Plugins/Android目錄下(在Unity這些東西都會被當(dāng)做資源處理)
- 在Assets/Scenes/ 建立一個場景,在場景上創(chuàng)建一個Canvas宰衙,并創(chuàng)建一個名為"AndroidSDKListener"的對象,在AndroidSDKListener之下再放三個按鈕觸發(fā)安卓 登錄 支付 退出游戲
- 創(chuàng)建一個btnClick.cs文件平道,將腳本掛載在AndroidSDKListener對象 如圖所示
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ā)送的消息
結(jié)語
??記錄下自己的學(xué)習(xí)和工作經(jīng)驗,分享給有需要的人袋哼。如果有那里寫的不對或者不理解,歡迎大家的指正冀墨。