MID SDK Unity 桥接接入文档(HarmonyOS)

1. 文档说明

1.1 功能描述

本文档描述如何在 HarmonyOS NEXT 平台上,通过团结引擎(Tuanjie Engine)内嵌 Unity 的方式接入 MID SDK。bridge_unity 模块提供了 Unity C# 与 MID SDK ArkTS 层之间的双向通信桥梁。

1.2 阅读对象

接入 MID SDK 的 Unity(团结引擎)客户端开发者。

1.3 与 Android 版的对应关系

对比项 Android HarmonyOS
ArkTS/Java → Unity UnityPlayer.UnitySendMessage tuanjieLib.TuanjieSendMessage
Unity → ArkTS/Java Unity C# 直接调用 Activity 方法 OpenHarmonyJSObject.Call() + .etslib 插件
回调格式 {"method":"类型","code":N,"data":...} 相同
构建工具 Gradle hvigorw + ohpm
包格式 AAR HAR
Native C++ 层 (纯 ArkTS + 团结引擎官方 API)

2. 通信架构

  1. ┌──────────────────────┐ ┌──────────────────────┐
  2. Unity C# │ │ ArkTS │
  3. OpenHarmonyJSObject
  4. 调用 SDK 功能 ──────┼────────────────────>│ MIDBaseUnityInterface
  5. .etslib 插件 (调用 MIDBaseSDK)
  6. 接收回调结果 <──────┼────────────────────│ MIDUnityCallbackManager
  7. OnOMGCallback(json) TuanjieSendMessage callUnity(json)
  8. └──────────────────────┘ └──────────────────────┘

调用方向(Unity → SDK):
Unity C# → OpenHarmonyJSObject("MIDBridge").Call(method, params) → .etslib 插件 → ArkTS dispatchFromUnity()

回调方向(SDK → Unity):
MIDBaseSDK → ArkTS callback() → tuanjieLib.TuanjieSendMessage("OMGSdk", "OnOMGCallback", json) → Unity C#

3. 接入准备

3.1 获取 SDK 文件

从 MID SDK 团队获取以下文件:

文件 放置位置 说明
WPMIDBase.har HarmonyOS 项目 libs/ MID SDK 核心库
channel_xxx.har HarmonyOS 项目 libs/ 渠道实现(华为/途游等)
MIDBridge.etslib Unity 项目 Assets/Plugins/OpenHarmony/ ArkTS 导出插件

3.2 HarmonyOS 项目配置

entry/oh-package.json5

  1. {
  2. "dependencies": {
  3. "@acegame/wpmidbase": "file:../libs/WPMIDBase.har",
  4. "@acegame/channel-huawei": "file:../libs/channel_huawei.har"
  5. }
  6. }

执行 ohpm install 安装依赖。

3.3 ArkTS 侧初始化

EntryAbility.ets 中初始化 Bridge 管理器并注册渠道:

  1. import { MIDBridgeManager } from '@acegame/bridge-unity';
  2. import '@acegame/channel-huawei'; // 渠道 side-effect import
  3. export default class EntryAbility extends UIAbility {
  4. onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
  5. // 初始化 Bridge 管理器(Unity 调用前必须完成)
  6. MIDBridgeManager.init(this.context);
  7. const midBridge = MIDBridgeManager.getInstance();
  8. // SDK 生命周期
  9. MIDBaseSDK.getInstance(this.context).onCreate();
  10. }
  11. onDestroy() {
  12. MIDBaseSDK.getInstance(this.context).onDestroy();
  13. }
  14. }

3.4 Unity 项目配置

MIDBridge.etslib 文件复制到 Unity 项目的 Assets/Plugins/OpenHarmony/ 目录下。团结引擎会自动识别并加载此插件。

4. Unity C# 侧接入

4.1 调用 SDK(Unity → ArkTS)

通过团结引擎的 OpenHarmonyJSObject 调用 .etslib 插件中注册的方法:

  1. using UnityEngine;
  2. public class MIDSDKManager : MonoBehaviour
  3. {
  4. // 通过插件名 "MIDBridge" 获取 ArkTS 桥接对象
  5. // OpenHarmonyJSObject 由团结引擎提供
  6. void Start()
  7. {
  8. // 初始化 SDK(传入 Unity 回调目标)
  9. var initParams = JsonUtility.ToJson(new {
  10. unityObjectName = "OMGSdk",
  11. unityCallBackFunc = "OnOMGCallback",
  12. resVer = "1.0.0"
  13. });
  14. CallArkTS("init", initParams);
  15. // 开启调试日志
  16. CallArkTS("setLogs", "{\"enable\":true}");
  17. }
  18. /// <summary>
  19. /// 调用 ArkTS 侧方法
  20. /// </summary>
  21. private void CallArkTS(string method, string paramsJson = "")
  22. {
  23. // 团结引擎 OpenHarmonyJSObject 调用示例
  24. // var midBridge = new OpenHarmonyJSObject("MIDBridge");
  25. // midBridge.Call(method, paramsJson);
  26. Debug.Log($"[MIDSDK] Call {method}: {paramsJson}");
  27. }
  28. /// <summary>
  29. /// 调用 ArkTS 侧方法并获取返回值
  30. /// </summary>
  31. private string CallArkTSWithReturn(string method, string paramsJson = "")
  32. {
  33. // var midBridge = new OpenHarmonyJSObject("MIDBridge");
  34. // return midBridge.Call<string>(method, paramsJson);
  35. return "";
  36. }
  37. }

4.2 接收回调(ArkTS → Unity)

所有 SDK 回调统一发往 OMGSdk 对象的 OnOMGCallback 方法,JSON 格式为:

  1. {"method": "类型", "code": 状态码, "data": 数据}
  1. /// <summary>
  2. /// 统一回调入口,挂在名为 "OMGSdk" 的 GameObject 上
  3. /// </summary>
  4. public void OnOMGCallback(string json)
  5. {
  6. Debug.Log($"[MIDSDK] Callback: {json}");
  7. var callback = JsonUtility.FromJson<SDKCallback>(json);
  8. switch (callback.method)
  9. {
  10. case "init":
  11. OnSDKInit(callback.code, callback.data);
  12. break;
  13. case "login":
  14. OnSDKLogin(callback.code, callback.data);
  15. break;
  16. case "switchLogin":
  17. OnSDKSwitchLogin(callback.code, callback.data);
  18. break;
  19. case "logout":
  20. OnSDKLogout(callback.code, callback.data);
  21. break;
  22. case "exitGame":
  23. OnSDKExitGame(callback.code, callback.data);
  24. break;
  25. case "pay":
  26. OnSDKPay(callback.code, callback.data);
  27. break;
  28. case "userAgree":
  29. OnSDKUserAgree(callback.code);
  30. break;
  31. case "getcdn":
  32. OnSDKGetCDN(callback.code, callback.data);
  33. break;
  34. case "giftCode":
  35. OnSDKGiftCode(callback.code, callback.data);
  36. break;
  37. case "onGetGameNotice":
  38. OnSDKGetGameNotice(callback.code, callback.data);
  39. break;
  40. }
  41. }
  42. [System.Serializable]
  43. public class SDKCallback
  44. {
  45. public string method;
  46. public int code;
  47. public string data; // JSON string,按 method 解析
  48. }

5. 接口说明

5.1 SDK 初始化【必接】

调用:

  1. CallArkTS("init", JsonUtility.ToJson(new {
  2. unityObjectName = "OMGSdk", // 接收回调的 GameObject 名称
  3. unityCallBackFunc = "OnOMGCallback", // 接收回调的方法名
  4. resVer = "1.0.0" // 游戏资源版本号,无可传空
  5. }));

回调:

method code data 说明
init 1 {"arg1": "success"} 初始化成功
init 0 {"arg1": "错误描述"} 初始化失败

5.2 登录【必接】

调用:

  1. CallArkTS("login");

回调:

method code data 说明
login 1 见下表 登录成功
login 0 "msg:登录失败" 登录失败

登录成功 data 字段(对标 Android):

字段 类型 说明
uid string 用户ID(游戏应以此作为唯一标识)
uidV1 string 用户ID V1
token string 用户token(需上传到游戏服务器验证)
channelid string 渠道ID
ext string 扩展信息
username string 用户名
nickname string 昵称
logintype string 登录类型(speedy/common/phone/email/thirdHidden/accessToken)
returnJson string 渠道返回的原始数据
userServiceCode string 用户ServiceCode
isFirstLoginStatus bool 是否首次登录
firstLoginTimestamp string 首次登录时间戳
bindChannelIds string 绑定渠道ID列表

5.3 注销【必接】

调用:

  1. CallArkTS("logout");

回调:

method code data 说明
logout 1 {"arg1": "msg"} 注销成功,游戏应返回登录界面

5.4 切换账号【必接】

调用:

  1. CallArkTS("switchAccount");

回调: 格式同登录,method 为 switchLogin

5.5 退出游戏【必接】

调用:

  1. CallArkTS("exitGame");

回调:

method code data 说明
exitGame 1 {"arg1": ""} 退出游戏

5.6 角色信息上报【必接】

调用:

  1. // 注册角色
  2. CallArkTS("createRole", JsonUtility.ToJson(new {
  3. Rolename = "玩家名", Roleid = "10001", RoleLv = "1",
  4. RoleVipLv = "0", ServerId = "s1", ServerName = "一区", GuildName = ""
  5. }));
  6. // 登录角色
  7. CallArkTS("loginRole", JsonUtility.ToJson(new {
  8. Rolename = "玩家名", Roleid = "10001", RoleLv = "30",
  9. RoleVipLv = "3", ServerId = "s1", ServerName = "一区", GuildName = ""
  10. }));
  11. // 角色等级变化
  12. CallArkTS("lvUpRole", JsonUtility.ToJson(new {
  13. Rolename = "玩家名", Roleid = "10001", RoleLv = "31",
  14. RoleVipLv = "3", ServerId = "s1", ServerName = "一区", GuildName = ""
  15. }));

参数说明:

参数 类型 说明
Rolename string 角色名称
Roleid string 角色ID
RoleLv string 角色等级
RoleVipLv string VIP等级,无则传 "0"
ServerId string 服务器ID
ServerName string 服务器名称
GuildName string 公会名称

注册角色 → createRole,登录角色 → loginRole,等级变化 → lvUpRole。首次登录需先调 createRole 再调 loginRole

5.7 支付【必接】

调用:

  1. CallArkTS("pay", JsonUtility.ToJson(new {
  2. productId = "item_001",
  3. productName = "钻石礼包",
  4. productDes = "100钻石",
  5. price = "600",
  6. buyNum = "100",
  7. currencyType = "1", // 1=人民币(分) 2=美元(美分) 3=日元(円)...
  8. currency = "钻石",
  9. deliverUrl = "",
  10. playerLv = "30",
  11. playerVipLv = "3",
  12. extension = "自定义透传数据"
  13. }));

currencyType 货币类型:

货币 单位 货币 单位
1 人民币 6 新加坡币
2 美元 美分 7 越南盾
3 日元 8 台币
4 港币 9 韩元
5 英镑 便士 10 泰铢 萨当

回调:

method code data 说明
pay 1 {"sdkOrderId": "订单号", "payType": "商品ID"} 支付成功
pay 0 同上 支付失败
pay 2 同上 支付结果未知,以服务端通知为准

5.8 礼包码兑换【选接】

调用:

  1. CallArkTS("giftCodeExchange", JsonUtility.ToJson(new {
  2. giftCode = "礼包码",
  3. url = "http://发货地址",
  4. extendParams = "透传参数"
  5. }));

回调:

method code data 说明
giftCode 1 "兑换成功" 兑换成功
giftCode 非1 "错误描述" 兑换失败

5.9 获取游戏公告【选接】

调用:

  1. CallArkTS("getGameNotice", "{\"type\":0}"); // 0=登录公告, 1=活动公告

回调:

method code data 说明
onGetGameNotice 1 JSON 数组 获取成功
onGetGameNotice 0 "fail" 获取失败

5.10 获取CDN资源更新地址【选接】

调用:

  1. CallArkTS("getCDNPath");

回调:

method code data 说明
getcdn 0 "http://cdn1,http://cdn2" 获取成功,逗号分隔
getcdn -1 "fail" 获取失败

5.11 隐私协议【必接】

调用:

  1. CallArkTS("agreePrivacy");

回调:

method code data 说明
userAgree 1 "" 用户同意协议
userAgree 0 "" 用户拒绝协议

5.12 心跳【必接】

调用:

  1. CallArkTS("startHeartbeat"); // 登录成功后开启
  2. CallArkTS("stopHeartbeat"); // 退出时停止

5.13 调试日志

调用:

  1. CallArkTS("setLogs", "{\"enable\":true}"); // 开启
  2. CallArkTS("setLogs", "{\"enable\":false}"); // 关闭

注意: 出正式包时务必关闭。

5.14 获取产品参数

调用(同步返回):

  1. string serviceId = CallArkTSWithReturn("getServiceId");
  2. string channelId = CallArkTSWithReturn("getChannelId");
  3. string deviceGroupId = CallArkTSWithReturn("getDeviceGroupId");
  4. string serviceCode = CallArkTSWithReturn("getServiceCode");

5.15 游戏自定义打点日志【选接】

调用:

  1. // 不带额外参数
  2. CallArkTS("analytics", JsonUtility.ToJson(new {
  3. logID = "4001",
  4. key = "sdk-init-start"
  5. }));
  6. // 带自定义参数
  7. CallArkTS("analytics", JsonUtility.ToJson(new {
  8. logID = "4002",
  9. key = "role_login",
  10. content = new { roleLevel = "30", vipLevel = "3", serverId = "s1" }
  11. }));

参数说明:

参数 类型 必须 说明
logID string 日志ID
key string 日志KEY
content object 自定义 key-value 参数

无回调,打点数据发送到服务端统计系统。

5.16 实名查询【选接】

调用:

  1. CallArkTS("realNameQuery");

回调:

method code data 说明
realNameQuery 0 实名信息 JSON 字符串 查询成功
realNameQuery 非0 {"msg": "错误描述"} 查询失败

5.17 实名验证【选接】

调用:

  1. CallArkTS("realNameVerification", JsonUtility.ToJson(new {
  2. id = "身份证号",
  3. name = "姓名"
  4. }));

参数说明:

参数 类型 必须 说明
id string 身份证号
name string 姓名

回调:

method code data 说明
realNameVerification 0 验证结果 JSON 字符串 验证成功
realNameVerification 非0 {"msg": "错误描述"} 验证失败

5.18 销毁

调用:

  1. CallArkTS("onDestroy");

6. 典型调用流程

  1. void Start()
  2. {
  3. // 1. 初始化 SDK(设置回调目标和游戏版本)
  4. CallArkTS("init", JsonUtility.ToJson(new {
  5. unityObjectName = "OMGSdk",
  6. unityCallBackFunc = "OnOMGCallback",
  7. resVer = "1.0.0"
  8. }));
  9. // 2. 开启调试(可选)
  10. CallArkTS("setLogs", "{\"enable\":true}");
  11. }
  12. // 回调: method="init", code=1
  13. void OnSDKInit(int code, string data)
  14. {
  15. if (code == 1) {
  16. // 3. 登录
  17. CallArkTS("login");
  18. }
  19. }
  20. // 回调: method="login", code=1
  21. void OnSDKLogin(int code, string data)
  22. {
  23. if (code == 1) {
  24. var loginData = JsonUtility.FromJson<LoginData>(data);
  25. string uid = loginData.uid; // 用户唯一标识
  26. string token = loginData.token; // 上传游戏服务器验证
  27. // 4. 注册角色 + 登录角色
  28. CallArkTS("createRole", roleJson);
  29. CallArkTS("loginRole", roleJson);
  30. // 5. 开启心跳
  31. CallArkTS("startHeartbeat");
  32. }
  33. }
  34. // 支付
  35. void OnPayButtonClick()
  36. {
  37. CallArkTS("pay", productJson);
  38. }
  39. // 退出
  40. void OnApplicationQuit()
  41. {
  42. CallArkTS("stopHeartbeat");
  43. CallArkTS("onDestroy");
  44. }

7. 服务端登录验证

登录成功后,游戏客户端拿到 token 后需上传到游戏服务器,游戏服务器通过 token 向 MID 用户中心服务器发起登录验证请求,验证通过后游戏服务器再通知游戏客户端登录成功。

  1. 游戏客户端 ──login──> SDK ──> 渠道SDK ──> 返回 token+用户信息
  2. ├── 上传 token 到游戏服务器
  3. └── 游戏服务器 ──验证──> MID 用户中心 ──> 返回用户信息 ──> 通知客户端

8. 回调汇总表

所有回调统一格式:{"method":"类型", "code":状态码, "data":数据}

method code data 触发时机
init 1/0 {"arg1":"msg"} 初始化结果
login 1/0 成功:用户JSON / 失败:"msg:..." 登录结果
switchLogin 1/0 同 login 切换账号结果
logout 1 {"arg1":"msg"} 注销成功
exitGame 1 {"arg1":""} 退出游戏
pay 1/0/2 {"sdkOrderId":"...","payType":"..."} 支付结果
userAgree 1/0 "" 隐私协议结果
getcdn 0/-1 成功:域名列表 / 失败:"fail" CDN地址
giftCode 1/N 成功:"兑换成功" / 失败:"msg" 礼包码兑换
onGetGameNotice 1/0 成功:JSON数组 / 失败:code 游戏公告
realNameQuery 0/N 成功:实名信息JSON / 失败:{"msg":"..."} 实名查询结果
realNameVerification 0/N 成功:验证结果JSON / 失败:{"msg":"..."} 实名验证结果

9. 状态码表

code 含义
1 成功
0 失败 / 错误
2 取消 / 结果未知(仅支付)
-1 异常

10. 可用方法汇总表

method 参数 返回值 说明
init {unityObjectName, unityCallBackFunc, resVer} 初始化
login 登录
logout 注销
switchAccount 切换账号
exitGame 退出游戏
createRole {Rolename, Roleid, RoleLv, RoleVipLv, ServerId, ServerName, GuildName} 注册角色
loginRole 同 createRole 登录角色
lvUpRole 同 createRole 角色升级
pay {productId, productName, productDes, price, buyNum, currencyType, currency, deliverUrl, playerLv, playerVipLv, extension} 支付
setLogs {enable: bool} 调试日志
giftCodeExchange {giftCode, url, extendParams} 礼包码兑换
getCDNPath 获取CDN
getGameNotice {type: int} 获取公告
agreePrivacy 同意隐私
startHeartbeat 开启心跳
stopHeartbeat 停止心跳
getServiceId string 获取 serviceId
getDeviceGroupId string 获取机型组ID
getServiceCode string 获取 ServiceCode
getChannelId string 获取渠道ID
analytics {logID, key, content} 游戏自定义打点
realNameQuery 实名查询(异步回调)
realNameVerification {id, name} 实名验证(异步回调)
onDestroy 销毁

11. 注意事项

  1. 回调 GameObject 名称必须匹配init 时传入的 unityObjectName(默认 "OMGSdk")必须与 Unity 场景中挂载 OnOMGCallback 方法的 GameObject 名称一致
  2. 回调方法名必须匹配unityCallBackFunc(默认 "OnOMGCallback")必须是 GameObject 上的 public 方法
  3. 初始化时序:必须先调 init 设置回调目标,后续回调才能正确送达
  4. 正式包务必关闭调试日志setLogs {enable: false}
  5. ArkTS 主线程:团结引擎的 TuanjieSendMessage 已处理线程安全,无需额外转发