Unity接入Andrioid原生SDK

前言

因为事业部在主攻娱乐社交行业,游戏作为娱乐社交行业的大头势必是需要主抓的。手游大多需要使用 Unity 和 Cocos 开发,事先掌握 Unity 或者 Cocos 如何接入原生 SDK 能够做到有备无患

Unity 介绍

Unity 3D 也称 Unity,是由 Unity Technologies 公司开发的一个让玩家轻松创建诸如三维视频游戏、建筑可视化、实时三维动画等类型互动内容的多平台的综合型游戏开发工具

Unity 环境安装

  1. 进入 Unity官网 https://unity.com/
  2. 点击 Get started—Individual
  3. 选择 Personal 版本下载,别问我为什么下载这个版本,问就是因为免费
  4. 点击 Get started 下载对应的安装包,Windows 是 UnityHubSetup.exe
  5. 点击 UnityHubSetup.exe 进行安装。一步步 next 之后,Unity Hub 就安装好了
  6. 在 Unity Hub 管理界面安装 Unity Editor

  1. 创建 Unity ID

创建 Unity 工程

在 Unity Hub 管理界面新建 Project,可以选择不同的模板

如何运行到 Android 设备

点击 file->Build Settings

选择 Android,第一次必须安装 Unity Android Build Support 平台模块,点击 Install with Unity Hub安装。安装好之后若还是不行关掉项目重启一下

重启完成之后显示如下

点击 Build And Run ,运行过程中若是报如下错误

需要修改 JDK、SDK、NDK 路径,点击 Edit-Preferences-External Tools 重置 JDK、SDK、NDK 路径

Unity2020.3.14 版本只支持 19.0.5232133 版本的 ndk(各个版本不同),但是 ndk 的下载列表中我们发现并没有 19.0.5232133 版本

我这边的做法是修改 19.2.5345600 版本为 19.0.5232133,需要同步修改 19.2.5345600 文件夹中 source.properties 文件的 pkg.Revision 为 19.0.5232133
同时 Player Settings 中可以修改包名、版本、最低支持版本等各种 Android 相关配置

Unity 工程 导入 aar

Unity 和 Android 的交互类型和方式很多,具体可以参考 https://docs.unity3d.com/cn/2018.4/Manual/PluginsForAndroid.html。因为目前大部分 SDK 是以 aar 的形式提供的,这边只讲解在 Unity 工程中使用 aar。

  1. 将 aar 包放置在 Assets 目录下的 Plugins/Android 文件夹下(没有需要新建),IOS 需要同理新建 IOS 文件夹

  1. 在 Assets 目录下新建 C# 脚本,Unity 和原生 aar 的交互主要通过 C# 脚本实现

  1. 将脚本挂载到层级上,拖动脚本到对应的层级就可以了。否则脚本不会生效,这边选择挂载到 Main Camera 上

  1. 新建的脚本内容如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Test : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{

}
}

新建的脚本列出了两个生命周期,具体其他的生命周期可以参考Unity 2019.4 脚本生命周期

  1. 作为测试样本,我们的想法是最好将交互放在某个点击事件中。这就涉及到新增 UI 了。这边以 Button 为例。选择某个层级右键选择 UI,然后选择某个控件

Button 绑定点击事件的方式有很多,这边只讲解在编辑器里面操作的方式。在脚本中新建一个 public 的点击方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{

}
public void ButtonClick(){
Debug.log("按钮被点击");
}
}

将脚本文件拖动到 Button 上

Button 属性列表中 onClick() 项点击加号添加事件 target 节点

将 Button 节点拖到 2 位置,然后在 3 位置选择 Test.ButtonClick(即我们定义的点击方法)

  1. Unity 调用 Java 方法
    UnityEngine 提供了两个类来分别访问 Java 的实例对象以及类对象:AndroidJavaObject 与AndroidJavaClass。前者表示 java.lang.Objec t或其子类,后者表示 java.lang.Class。他们提供相同的实例方法:
方法 返回值 说明
Call void 调用实例方法
Call T 调用实例方法
CallStatic void 调用类方法
allStatic T 调用类方法
Get T 获取成员变量
GetStatic T 获取类的成员变量
Set(T) void 设置成员变量
SetStatic(T) void 设置成员变量

利用上述的方法就能够实现与 java 的交互。写个简单的例子:
Java

1
2
3
4
5
6
7
8
9
10
11
12
package com.lxs;
public class Player {
private final static Player instance = new Player();
public static Player getInstance() {
return instance;
}

public float volume = 1.0f;
public int getDuration() {}
public void setDataSource(String dataSource) {}
public AudioInfomation getAudioInfomation() {}
}

脚本中

1
2
3
4
5
6
7
8
9
// AndroidJavaClass 参数为类的全路径
AndroidJavaObject player = new AndroidJavaClass("com。lxs.Player").CallStatic<AndroidJavaObject>("getInstance");
// 设置属性值
player.Set("volume", 0.8f);
// 调用有参方法
player.Call("setDataSource", "http://example.com/stream.m4a");
// 调用无参方法
int duration = player.Call<int>("getDuration");
AndroidJavaObject info = player.Call<AndroidJavaObject>("getAudioInfomation");

对于枚举类型可以使用下面的方式获取各个枚举值

java

1
2
3
4
5
6
7
8
package com.lxs.captcha;

public class CaptchaConfiguration{
public enum ModeType {
MODE_CAPTCHA,
MODE_SENSE,
}
}

脚本中

1
2
3
4
// AndroidJavaClass 参数为枚举所在的基类
AndroidJavaClass modeType = new AndroidJavaClass("com.lxs.captcha.CaptchaConfiguration$ModeType");
// 获取某个枚举值
AndroidJavaObject modeNoSense = modeType.GetStatic<AndroidJavaObject>("MODE_CAPTCHA");

针对于 Java 的回调接口,Unity 脚本这边提供了 AndroidJavaProxy。先对接口创建一个实现类

Java 接口

1
2
3
4
5
6
7
8
package com.lxs.captcha;

public interface CaptchaListener {
void onReady();
void onValidate(String result, String validate, String msg);
void onError(int code, String msg);
void onClose(Captcha.CloseType closeType);
}

脚本中,重点在 base,需要和原生对应。原生接口里面的枚举参数对应 AndroidJavaObject

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class AndroidPluginCallback : AndroidJavaProxy
{
public AndroidPluginCallback() : base("com.lxs.captcha.CaptchaListener") { }

public void onReady()
{
Debug.Log("onReady");
}
public void onValidate(string result, string validate, string msg)
{
Debug.Log("onValidate");
}
public void onError(int code, string msg)
{
Debug.Log("onError");
}
public void onClose(AndroidJavaObject obj)
{
Debug.Log("onClose");
}
}

使用的时候直接用 new AndroidPluginCallback()

常见问题

  1. 脚本中如何获取当前 Activity
1
2
AndroidJavaClass jClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject objActivity = jClass.GetStatic<AndroidJavaObject>("currentActivity");
  1. Unity 跑在 UnityMain,并不是 Android 的 UI 线程。对于需要跑在 UI 线程的方法就会报错,解决方法如下:
1
2
3
objActivity.Call("runOnUiThread", new AndroidJavaRunnable(() => {

}));

优化

现在我们已经可以实现 Unity 和 Sdk 的通信。但是作为一个客户交付产物,将脚本的实现代码和 aar 包一并交付给客户接入成本还是有点高。上述只能说是接入示例,不能严格意义上称之为交付产物。要达到交付产物的标准,首先我们得将调用脚本代码进一步封装,然后将所有的资源打包。

Unity 提供了 Package Manager 来将原生资源和脚本资源打包,封装的脚本代码放置在 Assets/Scripts 文件夹下(没有这个文件夹需要新建),然后在 Assets 右键选择 Export Package 来导出

内部可以选择需要导出的资源,Scenes 文件夹的内容不需要导出

导出的是 .unitypackage 格式的包,接入方只需要在 Assets 右键选择 Import Package 就能导入 package 资源包,接入成本极低


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!