历史背景
近期在游戏SDK接入的技术支持中,不断有游戏反馈希望有现成的unity插件可以直接接入,为了减轻和方便游游戏方接入,对SDK进行unity的插件开发
用到的兵器
1、Android studio 官方下载地址
2、Unity 2017.4.2f2 官方下载地址
官网有时候无法访问,这边提供百度
unity2017中文安装包:链接:https://pan.baidu.com/s/1gW2tDhAiodKf0qRNUNCKWg 提取码:k8to
unity2017的Android支持的插件:链接:https://pan.baidu.com/s/1tTCAYlZnDUtXIizz_70Gpg 提取码:0mtp
Android方面的操作(这边提供的是aar的形式)
1、新建一个Android Library的module
2、编译下对应的module,名称和包名(包名就是unity的项目的包名)
3、原有的要接入SDK,已经达成aar包,将SDK的aar包,导入到我们这个plugin的module的libs下,配置一下gradle
4、个人这边将SDK的内容进行了封装到一个类
public class SinglePaySDK {
private static final String TAG = "SDK";
private SingleOperateCenter mOpeCenter;
public SinglePaySDK(Activity context, String appKey, String gameName, int orientation,
SingleOperateCenter.SingleRechargeListener singleRechargeListener) {
initSDK(context, appKey, gameName, orientation, singleRechargeListener);
}
private void initSDK(Activity activity, String appKey, String gameName, int orientation,
SingleOperateCenter.SingleRechargeListener singleRechargeListener) {
Log.i(TAG, "appKey = " + appKey + ", orientation = " + orientation);
if (isInitReady()) {
return;
}
mOpeCenter = SingleOperateCenter.getInstance();
new OperateCenterConfig.Builder(activity)
.setDebugEnabled(true) //发布游戏时,要设为false
.setOrientation(orientation) //设置SDK界面方向,应与游戏设置一直
.setSupportExcess(true) //设置是否支持超出金额充值
.setGameKey(appKey) //换成实际游戏的game key
.setGameName(gameName) //换成实际游戏的名字,原则上与游戏名字匹配
.build();
mOpeCenter.init(activity, singleRechargeListener);
}
public void doPay(Activity activity, final String money, final String productName) {
doPay(activity, money, productName, null);
}
public void doPay(final Activity activity, final String money, final String productName,
final String extra) {
Log.d(TAG, "execute method doPay... money = " + money + ", productName = " + productName);
if (mOpeCenter == null) {
Log.d(TAG, "SDK need init first");
return;
}
//extra是透传的字段
if (TextUtils.isEmpty(extra)) {
mOpeCenter.recharge(activity, money, productName);
} else {
mOpeCenter.recharge(activity, money, productName, extra);
}
}
public boolean isInitReady() {
return mOpeCenter != null;
}
public void doCheck(Activity activity, Callbacks.OnCheckFinishedListener listener) {
Log.d(TAG, "execute doCheck...");
if (!isInitReady()) {
Log.d(TAG, "SDK need init first");
return;
}
mOpeCenter.doCheck(activity, listener);
}
private void doDownload(Activity activity, Callbacks.OnDownloadListener listener) {
Log.d(TAG, "execute doDownload...");
if (!isInitReady()) {
Log.d(TAG, "SDK need init first");
return;
}
mOpeCenter.doDownload(activity, listener);
}
private void doInstall(Activity activity) {
Log.d(TAG, "execute doInstall...");
if (!isInitReady()) {
Log.d(TAG, "SDK need init first");
return;
}
mOpeCenter.doInstall(activity);
}
}
5、在unity的安装目录下:D:\Program Files\Unity\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar,将该文件拷贝到plugin 的module下的libs目录, 刷新gradle
6、由于当前默认使用原有的UnityPlayerActivity,并没有继承该该类 继承UnityPlayerActivity的写法
AndroidManifest.xml文件需要配置
............其他的代码.......
7、打包成aar
8、由于我们SDK插件打包成aar的时候,会将libs的unity的classes的jar包一并打包进去,需要手动用压缩软件打开aar将里面的classes.jar的文件删掉
Unity方面的操作
1、新建Unity项目,创建Secne场景,添加了Canvas的画布,在画布下添加了,Button和Text
2、在unity的project下的assert目录下新建plugins目,然后再plugins目录下,在新建Android目录,将项目的AndroidManifest.xml文件拷贝进去,然后再新家libs目录将aar包copy进去,这边有两个,一个是SDK的aar包,一个我们编写创建的aar包,
要注意一个地方是plugin-release的AndroidManifest文件包名和主项目的AndroidManifest不能一样,在17版本编译会出现重复包名的错误
3、在Assert目录下创建Scripts目录(这个主要是这边用于存放C#的脚本文件,直接放Assert级的目录下也是可以的)
4、上述的脚本文件,就需要选择一个主要脚本,将该脚本文件添加到component中,用于绑定到该场景,设置调用通过add component-->scripts, 然后选择所开发的脚本文件
5、编译这边有两个区分,一个是gradle编译,一个Internal编译,主要区别是在2017版本gradle编译,可以不进行包名设置,internal是需要设置。
6、注意PlatForm中Android 是需自己安装的
unity2017的Android 平台支持的插件:链接:https://pan.baidu.com/s/1tTCAYlZnDUtXIizz_70Gpg 提取码:0mtp
7、生成apk
这边讲讲上面C#调用java的相关代码
先贴代码
--SinglePaySDKContext.cs文件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace SinglePay{
public sealed class SinglePaySDKContext : MonoBehaviour
{
private AndroidJavaObject currentActivity;
private static readonly SinglePaySDKContext _SinglePaySDKContext = new SinglePaySDKContext();
/*
* 获取当前实例
*/
public static SinglePaySDKContext GetInstance()
{
return _SinglePaySDKContext;
}
/*
* 获取当前Activity
*/
public AndroidJavaObject GetActivity()
{
if (null == currentActivity)
{
currentActivity = new AndroidJavaClass("com.unity3d.player.UnityPlayer")
.GetStatic("currentActivity");
}
return currentActivity;
}
/*
* 运行在主UI线程
*/
public void RunOnUIThread(AndroidJavaRunnable runnable)
{
GetActivity().Call("runOnUiThread", runnable);
}
/*
* 获取根节点的布局
*/
public AndroidJavaObject GetRootLayout()
{
AndroidJavaClass R = new AndroidJavaClass("android.R$id");
return currentActivity.Call("findViewById", R.GetStatic("content"));
}
}
}
--SinglePaySDK.cs文件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using SinglePay;
public class SinglePaySDK : MonoBehaviour {
private Transform canvasForm;
private Button initButton;
private Button payButton;
private Button checkButton;
private Text ResultText;
//表示横屏
private const int SCREEN_ORIENTATION_LANDSCAPE = 0;
//表示竖屏
private const int SCREEN_ORIENTATION_PORTRAIT = 1;
//表示反向横屏
private const int SCREEN_ORIENTATION_REVERSE_LANDSCAPE = 8;
//表示反向竖屏
private const int SCREEN_ORIENTATION_REVERSE_PORTRAIT = 9;
//获取全局的上下文件对象
private SinglePaySDKContext singlePaySDKContext;
//获取单机支付SDK的对象
private AndroidJavaObject singlePaySDK;
//是否开始计时
private bool IsTiming;
//倒计时
private float CountDown;
// Use this for initialization
void Start () {
singlePaySDKContext = SinglePaySDKContext.GetInstance();
canvasForm = GameObject.Find("Canvas").transform;
//获取控件对象设置时间监听
initButton = canvasForm.Find("InitButton").GetComponent
--ToastUtils.cs文件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace SinglePay
{
public class ToastUtils
{
public static void showToast(string text)
{
SinglePaySDKContext.GetInstance().RunOnUIThread(new AndroidJavaRunnable(() =>
{
AndroidJavaClass Toast = new AndroidJavaClass("android.widget.Toast");
Toast.CallStatic("makeText", SinglePaySDKContext.GetInstance().GetActivity(), text, Toast.GetStatic("LENGTH_SHORT")).Call("show");
}));
}
}
}
上面主要的语法知识点:
1、AndroidJavaClass 这个就是相当于C#到AndroidJava的类的映射
---- 调用静态方法Android中类的静态方法 和获取对应的静态字段
AndroidJavaClass jc = new AndroidJavaClass("完整的路径类名");
静态方法调用:jc.CallStatic<返回类型>("java对应的方法名",object[] args); //args:参数,类型没有传默认就是void
静态字段获取:jc.GetStatic<返回类型>("字段名称")). //public类型的字段
例子
AndroidJavaClass Toast = new AndroidJavaClass("android.widget.Toast"); Toast.CallStatic("makeText", SinglePaySDKContext.GetInstance().GetActivity(), text, Toast.GetStatic("LENGTH_SHORT")).Call("show");
2、AndroidJavaObject 这个就是相当于C#到AndroidJava的对象的映射
---- 调用Android中对的方法 和获取对应的字段
AndroidJavaObject jo= new AndroidJavaObject ("完整的路径类名",object[] args); //args:构成方法的参数
方法调用:jo.Call<返回类型>("方法名", object[] args); //args:参数,//类型没有传默认就是void
成员变量调用:jo.Get("字段名称"));
例子:
AndroidJavaObject singlePaySDK = new AndroidJavaObject("cn.m4399.plugin.SinglePaySDK", singlePaySDKContext.GetActivity(), "70001", "测试游戏",SCREEN_ORIENTATION_PORTRAIT, listenerProxy);
singlePaySDK.Call("doPay", singlePaySDKContext.GetActivity(), "10", "砖石", "测试数据");
3、AndroidJavaProxy接口的语法调用
----unity对应java接口
java的接口代码:
public interface SDKCallbackListener
{
void OnSDKInited(String msg);
void OnAuthSuccess(String token);
void OnCreatedLive(String url);
void OnDeletedLive(String id);
}
Unity中C#的代码:
Unity C#代码实现Android Java 代码必须要完全一致,但是允许在UnityC#代码中实现多次
class SDKCallbackListener : AndroidJavaProxy
{
// 这句话很重要!!!C#找到Jar中接口的引用
public SDKCallbackListener() : base("包名.SDKCallbackListener") { }
public void OnSDKInited(string msg)
{
ATrace.Log( "OnSDKInited:" + msg);
}
public void OnAuthSuccess(string token)
{
ATrace.Log("OnAuthSuccess:" + token);
}
public void OnCreatedLive(string url)
{
ATrace.Log("OnCreatedLive:" + url);
}
public void OnDeletedLive(string id)
{
ATrace.Log("OnDeletedLive:" + id);
}
}
4、AndroidJavaRunnable其实就是对应java的 java.lang.Runnable.
直接看Unity Api
总结,Android和Unity的交互就是一方导出插件的形式给一方使用,本文写法是提供了Android导出aar给Unity使用,以上就本文的全部内容,如果有什么错误的地方,欢迎大家指出
参考文献:
https://docs.unity3d.com/ScriptReference
https://www.jianshu.com/p/ceaac83808f2
https://www.jianshu.com/p/b5e3cfcdf081