跳到主要内容

XDSDK v7 Unity 快速接入

· 阅读需 7 分钟

配置

添加依赖

参考下面内容,在 manifest.json 中添加 SDK 依赖。代码示例为所有模块,具体使用哪些请和平台同事确认。

{
"dependencies": {
"com.xd.sdk.foundation": "7.1.0",
"com.xd.sdk.common": "7.1.0",
"com.xd.sdk.account": "7.1.0",
"com.xd.sdk.payment": "7.1.0",
"com.xd.sdk.mainland": "7.1.0",
"com.xd.sdk.announcement": "7.1.0",
"com.xd.sdk.tap": "4.8.1-xd.2",
"com.xd.sdk.tap.plus": "4.8.1-xd.2"
},
"scopedRegistries": [
{
"name": "NPMJS",
"url": "https://registry.npmmirror.com",
"scopes": [
"com.xd.sdk"
]
}
]
}

导入插件

  1. 将给到的 Vuplex 4.5 的 UnityPackage 导入项目。
  2. 将 asmdef 文件拷贝到 Vuplex/WebView/Core/Scripts/Vuplex.WebView.asmdef

参数文件

将给到的参数配置文件名改为XDConfig.json,放到 Assets/ 目录下。

初始化

打印日志

using XD.SDK.Common;
XDGLogger.LogDelegate = (level, info) =>
{
// 自行打印到控制台或文件
};

初始化SDK

  • 请将初始化代码放在尽可能靠前的位置,在初始化成功前请不要绘制登录相关的界面。
  • 如果初始化失败一般为配置问题,请根据错误信息检查配置是否正确或者咨询平台。
  • 初始化成功前大部分接口都不可使用。
using XD.SDK.Common;

// 渠道和语言根据实际情况修改
// PackageType 规则
// 国内 TapTap PC 平台请使用 PackageTypePCTapTap,
// 海外 Google Play Android 平台请使用 PackageTypeAndroidGooglePlay
// 其他平台请使用 PackageTypeDefault

XDGInitParam initParam = XDGInitParam.CreateToXDSDK("your_channel", LangType.ZH_CN, XDGPackageType.PackageTypePCTapTap);
XDGCommon.InitSDK(initParam,
(success, msg) =>
{
if (success)
{
// 初始化成功
}
else
{
// 初始化失败
}
});

// 判断是否初始化完成
bool isInitialized = XDGCommon.IsInitialized();

修改多语言

在初始化后可根据需要修改SDK语言。

using XD.SDK.Common;
XDGCommon.SetLanguage(LangType.EN);

登录

  • 在初始化成功后可以进入登录页面但暂时先不要绘制登录按钮,先设置好用户状态回调并调用自动登录,如果自动登录失败再绘制登录按钮。
  • 登录按钮点击后调用手动登录接口,需要传入对应的登录类型。登录失败后需要继续展示登录按钮让用户重新登录。
  • 自动登录在每次打开游戏时只能触发一次,失败后只能调用手动登录接口。
  • 收到用户退出登录的回调时需要回到登录页面并展示登录按钮。此时不要再调用自动登录接口。

状态回调

  • 需要在初始化前设置好
using XD.SDK.Account;   

// 设置用户状态回调
XDGAccount.AddUserStatusChangeCallback((code, message) =>
{
switch (code)
{
case XDGUserStatusCodeType.BIND:
// 绑定成功,没有更多登录方式可不处理
break;
case XDGUserStatusCodeType.UNBIND:
// 解绑成功,没有更多登录方式可不处理
break;
case XDGUserStatusCodeType.LOGOUT:
// 退出登录,回到登录页面,展示登录按钮
break;
case XDGUserStatusCodeType.ProtocolAgreedAfterLogout:
// 退出登录后同意协议,可不处理
break;
case XDGUserStatusCodeType.SupportHasUnRead:
// 客服有未读消息,未开通服务可以不处理
break;
case XDGUserStatusCodeType.SupportNoUnRead:
// 客服没有未读消息,未开通服务可以不处理
break;
default:
// 未知 Code,不需要处理
break;
}
});

发起登录

using XD.SDK.Account;

// 自动登录,在初始化成功后可以调用该方法,每次打开游戏只触发一次。失败之后只能调用其他登录方式。
XDGAccount.LoginByType(LoginType.Default,
user =>
{
// 自动登录成功,进入游戏
String userId = user.UserId;
},
error =>
{
// 自动登录失败,展示 TapTap 登录按钮
});

// 手动登录
XDGAccount.LoginByType(LoginType.TapTap,
user =>
{
// TapTap登录成功,进入游戏
String userId = user.UserId;
},
error =>
{
// TapTap 登录失败,展示 TapTap 登录按钮
});

// 登录后获取用户信息
XDGUser currentUser = XDGAccount.GetCurrentUser();

用户中心

using XD.SDK.Account;

XDGAccount.OpenUserCenter(null);

退出登录

using XD.SDK.Account;

XDGAccount.Logout();

账号注销

using XD.SDK.Account;

XDGAccount.OpenAccountDeletion();

打开客服

using XD.SDK.Account;

// 角色信息
XDGRoleInfo role = new XDGRoleInfo()
{
RoleId = "role_id",
RoleLevel = 10,
RoleName = "role_name",
ServerId = "server_id"
};
// 透传参数
var paramsMap = new Dictionary<string, object>
{
{ "key1", "value1" },
{ "key2", true },
{ "key3", 1234567890 }
};
XDGAccount.OpenCustomerService(role, "", paramsMap);

个人信息页

仅限国内可使用

using XD.SDK.Account;

XDGRoleInfo role = new XDGRoleInfo()
{
RoleId = "role_id",
RoleLevel = 10,
RoleName = "role_name",
ServerId = "server_id"
};
XDGAccount.OpenUserDashboard(role);

内购

查询商品价格

  • 在查询商品价格前请在后台配置好对应的商品信息。
  • Apple、Google Play 平台查询价格需要使用对应平台的商品 ID。
  • 其他平台查询价格使用 XD 平台的商品 ID。
using XD.SDK.Payment;

string[] productIdList = { "com.xd.demo.sku1", "com.xd.demo.sku2" };
XDGPayment.QueryWithProductIds(productIdList,
list =>
{
foreach (var product in list)
{
// 商品 ID
var productId = product.ProductId;
// 商品价格,带货币符号,可直接使用
var productPrice = product.DisplayPrice;
}
},
error =>
{
// 查询失败
});

发起内购

using XD.SDK.Payment;

var paymentParams = new XDGPaymentParams(
"game_order_id", // 游戏订单号
"product_id", // 商品 ID
"role_id", // 当前角色 ID
"server_id", // 当前服务器 ID
"extra", // 透传参数
1);// 商品数量,仅 Apple 购买可用
XDGPayment.PayWithParams(paymentParams,
orderInfo =>
{
// 购买完成,发货请等待服务端回调
String orderId = orderInfo.GameOrderId;
String productId = orderInfo.ProductId;
String roleId = orderInfo.RoleId;
String serverId = orderInfo.ServerId;
String extra = orderInfo.Extra;
},
error =>
{
// 购买失败
});

查询 Apple 未消费订单

  • 仅 Apple 平台可用,其他平台调用该接口会直接返回空列表。
  • 查询到的订单可能是用户之前购买成功但未成功消费的订单,也可能是 App Store 中兑换的礼包码订单。
using XD.SDK.Payment;

XDGPayment.QueryPendingPurchases(
list =>
{
// 查询成功
foreach (var purchase in list)
{
// 商品 ID
String productId = purchase.ProductId;
// 订单 token
String purchaseToken = purchase.PurchaseToken;
}
},
error =>
{
// 查询失败
});

消费 Apple 未消费订单

using XD.SDK.Payment;

var paymentParams = new XDGPaymentParams(
"game_order_id", // 游戏订单号
"product_id", // 商品 ID
"role_id", // 当前角色 ID
"server_id", // 当前服务器 ID
"extra", // 透传参数
1);// 商品数量,仅 Apple 购买可用

// 传入查询到的未消费订单 token
paymentParams.PendingPurchaseToken = "purchase_token";

XDGPayment.PayWithParams(paymentParams,
orderInfo =>
{
// 购买完成,发货请等待服务端回调
String orderId = orderInfo.GameOrderId;
String productId = orderInfo.ProductId;
String roleId = orderInfo.RoleId;
String serverId = orderInfo.ServerId;
String extra = orderInfo.Extra;
},
error =>
{
// 购买失败
});

工具

事件上报

using XD.SDK.Common;

// 事件上报
XDGCommon.TrackEvent("event_name", new Dictionary<string, object>()
{
{ "key1", "value1" },
{ "key2", "value2" }
});

// 角色信息上报
XDGRoleInfo role = new XDGRoleInfo()
{
RoleId = "role_id",
RoleLevel = 10,
RoleName = "role_name",
ServerId = "server_id"
};
XDGCommon.TrackRole(role);

商店评价

商店评价仅支持 Apple 和 Google Play 平台。

using XD.SDK.Common;

XDGCommon.StoreReview();

打开网页

using XD.SDK.Common;

XDGCommon.OpenWebPage("url", (actionType, objects) =>
{
// objects 是透传数据,在各个事件中均可能携带
if (actionType == WebActionEnum.CLOSE)
{
// 关闭网页
} else if (actionType == WebActionEnum.MESSAGE)
{
// 透传数据,具体数据内容根据和网页的约定而定
}
});

公告

展示公告

展示公告 UI,由 SDK 处理公告的获取和展示。可以在公告后台进行样式自定义。

using XD.SDK.Announcement;

XDGAnnouncementConfig config = new XDGAnnouncementConfig();
config.Channel = "渠道配置"; // 对应公告后台的 Google iOS PC 等
config.ServerCode = "服务器 code"; // 对应公告后台配置服务器时对应的 code

// 展示公告面板
XDGAnnouncementManager.OpenAnnouncementPage(config, (actionType, objects) =>
{
// objects 是透传数据,在各个事件中均可能携带
if (actionType == WebActionEnum.CLOSE)
{
// 关闭公告
} else if (actionType == WebActionEnum.MESSAGE)
{
// 透传数据
}
});

检查未读公告

检查是否有未读公告,请不要频繁调用该接口,可能影响性能。

using XD.SDK.Announcement;

XDGAnnouncementConfig config = new XDGAnnouncementConfig();
config.Channel = "渠道配置"; // 对应公告后台的 Google iOS PC 等
config.ServerCode = "服务器 code"; // 对应公告后台配置服务器时对应的 code

XDGAnnouncementManager.RequestAnnouncementUnread(config, hasUnread =>
{
if (hasUnread)
{
// 有未读公告
}
else
{
// 没有未读公告
}
});

TapSDK

  • TapSDK 内的功能模块均由 XDSDK 进行初始化和调用,游戏无需重复初始化 TapSDK。
  • 接口功能使用说明请参考 TapSDK 文档,但接口使用时请注意替换成 XDSDK 中的对应接口,一般在 XDGTapSDK 和 XDGTapPlusSDK 两个类中,函数名一般保持不变。

XDGTapSDK

using XD.SDK.Tap;

该模块下所有模块使用时需要替换成 XDGTapSDK.接口名 形式调用。

XDGTapPlusSDK

using XD.SDK.Tap.Plus;

该模块下所有模块使用时需要替换成 XDGTapPlusSDK.接口名 形式调用。

XDSDK 接入 TDS 实名认证防沉迷最佳实践

· 阅读需 7 分钟

准备工作

开发者只需要知道游戏在 TapDC 后台的 Client ID 参数用于防沉迷 SDK 的初始化。没错,准备工作就这一点。

游戏实名认证

请参考最佳实践

调用 SDK 提供的功能性接口

  • TapTap 快速认证

TapTap 快速认证服务顾名思义,是指让玩家直接授权其 TapTap 账号的实名信息完成实名认证,省去填写姓名、身份证号的繁琐步骤。

具体的代码示范如下:

// 参数:userIdentifier 玩家唯一标识,请填写用户的 XDID(即 XDGUser 中的 userId)
string userIdentifier = "玩家的唯一标识";
// 参数:isTapUser 对于是否是 Tap 用户,游戏可根据当前玩家选择的登录方式进行设置,即选择 Tap 登录方式的设置为 true, 其他类型设置为 false。
// 对于国服游戏一般只有 Tap 登录渠道的 isTapUser 参数设置 true 即可,具体情况请依旧游戏侧自己的需求来定,不是一成不变的。这个参数会影响实名认证弹窗的样式,具体样式区别接下来做详细展示说明。
bool isTapUser = true;
AntiAddictionUIKit.Startup(userIdentifier, isTapUser);
  • 正确调用 startup 接口移动端打包后可以看到如下的弹窗,以游戏铃兰之剑作为示例展示。(参数:isTapUser 设置为 true 的状态):

由上图可见,实名认证弹窗只提供一个「使用」按钮,玩家点击「使用」按钮后会触发 TapTap 快速认证的授权页面。

  • startup 接口参数:isTapUser 设置为 false 的状态:

由上图可见,实名认证弹窗会提供「使用」和「不实用」按钮,玩家点击「使用」按钮后会触发 TapTap 快速认证的授权页面;当玩家点击「不使用」按钮时,会触发需要玩家手动输入身份证信息的界面。如下图所示。

玩家输入姓名、身份证号后如果认证失败,会提示「认证未通过,请提交真实信息」,如果乱填写身份证号,则会提示「身份证号码非法」。这些也不需要开发者关心,认证失败时,「游戏实名认证」窗口是不会关闭的,除非玩家点击右上角的关闭按钮主动关闭。这些都是 SDK 内部封装好的,开发者重点需要关心的是文档中给出的回调类型,这个很重要。比如实名认证过程中,玩家点击了右上角的关闭按钮,则会触发 code 为 9002 的回调,该回调告知开发者玩家的动作,表示玩家并没有完成实名认证,开发者对此应该做相应的逻辑处理。

无版号游戏没有开通实名认证防沉迷服务是无法使用 TDS 的实名认证防沉迷功能的。没有开通服务的游戏接入实名认证防沉迷,当调用实名认证接口时会给出相对应的提示:「未查询到实名认证配置」

因此,至于 startup 接口参数 isTapUser 该如何赋值,具体参考游戏需求,如果游戏只有 TapTap 登录渠道,没有其他的登录渠道,则 isTapUser 可以设置为 true;如果游戏具有多个登录渠道,建议游戏根据登录类型来决定设置 isTapUser 的值。

防沉迷策略

仅允许未成年人在周五、周六、周日和法定节假日的 20:00 至 21:00 进行游戏。非允许游戏时间段内,SDK 封装的相应逻辑会被触发,弹出提示框提醒未成年无法继续游戏。此时的未成年玩家最多有两种选择:「退出游戏」或者「切换账号」。

如果初始化 SDK 时设置的一个参数 showSwitchAccount 为 false(表示不显示「切换账号」按钮),那此时的未成年玩家只能选择「退出游戏」了。

// 是否显示切换账号按钮
bool showSwitchAccount = false;

检查消费上限

根据年龄段的不同,未成年玩家的消费金额有不同的上限。 如果要启用消费限制功能,开发者只需要在未成年玩家消费前检查是否受限。如果游戏没有消费则可以忽略这部分说明。

游戏在收到玩家的付费请求后,调用以下接口检验当前玩家的付费行为是否被限制,游戏侧请 务必 在未成年玩家每次充值之前调用如下接口进行判断。

long amount = 100;    // 100 表示 100 分,即 1 元
AntiAddictionUIKit.CheckPayLimit(amount,
(result) => {
// status 为 1 时可以支付
int status = result.status;
if (status == 1) {
// 可以进行支付
} else {
// 说明玩家当前的这一笔消费不可以再继续了,如果继续就超过了限制,游戏侧则要拦截这笔消费。
}
},
(exception) => {
// 处理异常
}
);

消费金额的单位为分。

警告

当玩家支付成功后并且支付渠道给到支付回调后,XD Server 会 Server to Server 上报玩家当前支付金额给 TDS 防沉迷,游戏侧不需要再额外的去上报玩家消费金额。游戏侧需要做的就是客户端自行校验玩家消费是否达到上限即可。

至此,你基本上已经很好的完成了 TDS 实名认证防沉迷 SDK 的接入了,请给你自己一点掌声。

讲真,个人觉得 TDS 实名认证防沉迷 SDK 并不需要心动一方游戏服务端的过多参与,不知道屏幕前的你在完全了解后是否也有相同的感受。SDK 剩余的其他接口开发者请酌情按需调用即可。

获取玩家年龄段

实名认证后,可以统一调用如下接口获取当前玩家的年龄段:

int ageRange = AntiAddictionUIKit.AgeRange;
// ageRange 是一个整数,表示玩家所处年龄段的下限(最低年龄)。 特别地,-1 表示「未实名」。

具体年龄段返回数值及其对应年龄段如下表所示:

类型数值含义
-1未实名
00 到 7 岁
88 到 15 岁
1616 到 17 岁
18成年玩家

上报游戏时长

如果启用时长限制功能,需要上报游戏时长。已登录的玩家,开始游戏时调用此接口,之后 SDK 会自动轮询上报游戏时长。

AntiAddictionUIKit.EnterGame();

相应地,已登录的玩家,停止游戏时调用此接口,之后 SDK 停止轮询上报时长。

AntiAddictionUIKit.LeaveGame();

获取剩余时长

获取玩家当前剩余时长:

int remainingTimeInSeconds = AntiAddictionUIKit.RemainingTime;  // 单位:秒

int remainingTimeInMinutes = AntiAddictionUIKit.RemainingTimeInMinutes; // 单位:分钟

Checklist

游戏侧接入 TDS 实名认证防沉迷功能,开发者需要测试实名认证防沉迷流程是否正常,检查并知晓以下事项:

  • 游戏侧正确导入 TDS 实名认证防沉迷所需的包体。

  • SDK 的正确初始化,gameId 参数为 Tap Client ID,游戏侧根据需求决定是否显示「切换账号」按钮,若显示「切换账号」按钮请务必处理好这块的逻辑。

  • 充分了解实名认证防沉迷的各个回调方法所对应的含义,游戏侧对此做出相应的逻辑处理。

  • 充分了解实名认证 AntiAddictionUIKit.Startup(userIdentifier, isTapUser) 接口的两个参数含义。

  • SDK 的初始化 AntiAddictionUIKit.Init 和实名认证接口 AntiAddictionUIKit.Startup 保留毫秒级的间隔,以确保 SDK 完成初始化。

  • 游戏在收到未成年玩家付费请求后请 务必 先检查当前这笔消费是否已达上限,达到上限则拦截当前这笔消费。

  • 游戏前端请 务必 不要额外上报玩家消费的,XDSDK 服务端会自行上报。

关于不同登录方式同一邮箱自动绑定处理

· 阅读需 3 分钟

1、背景:

目前心动海外游戏在现有 TapTap、游客登录基础上,允许额外开通 Apple、Google 登录。注意:该功能仅针对用海外。

基于目前海外用户习惯,当用户使用不同方式登录(Apple、Google、TapTap)且为同一邮箱时,需要让其登录上同一账号,防止其账号裂开。详细背景

例如用户使用 TapTap (邮箱 A)登录并创建心动账号(XDID:1)后,再使用新的 Google (邮箱 A)账号登录时,会自动登录并绑定该 Google 账号至心动账号(XDID:1)。

2、处理事项

基于邮箱登录策略,防止用户账号裂开,以下场景项目组需要自行处理并告知用户:

场景一:用户未验证 TapTap 邮箱导致登录失败

前置条件:

  • 用户使用邮箱、密码注册 TapTap 账号时未通过邮件验证邮箱。

场景描述:

  • 当用户使用未验证邮箱的 TapTap 账号登录后,需要告知其邮箱未验证并弹窗引导其前往邮箱进行验证。

弹窗文案:

  • 标题:邮箱未验证
  • 内容:当前 {第三方平台名称} 账号所关联的邮箱( {从第三方平台获取的邮箱信息})未被验证,请先查看注册 {第三方平台名称} 账号时发送至 ( {从第三方平台获取的邮箱信息})的验证邮件,并根据邮件指引验证成功后再次尝试登录。

场景1

场景二:多个心动账号绑定同一邮箱

前置条件:

  • XDID:1 已绑定 TapTap 账号(邮箱 A)、Google 账号 (邮箱 B)
  • XDID:2 已绑定 Google 账号(邮箱 A)

用户流程:

  • 用户使用 Apple 账号 (邮箱 A)登录时,由于 SDK 并不知晓用户期望绑定哪一个 XDID 所以需要告知用户账号已存在并引导用户登录原有账号执行绑定、解绑操作。

弹窗文案:

  • 标题:账号已存在
  • 内容:当前 {第三方平台名称} 账号所关联的邮箱({从第三方平台获取的邮箱信息})已被用于另一个游戏账号,请使用该邮箱所关联的 {邮箱所关联心动账号下绑定该邮箱的其他登录方式} 登录游戏账号后进入「账号安全中心」进行账号绑定、解绑操作。

场景2

场景三:现有心动账号已绑定用户当前的登录方式

前置条件:

  • XDID:1 已绑定 TapTap 账号(邮箱 A)、Google 账号 (邮箱 B)

用户流程:

  • 用户使用 TapTap (邮箱 B)登录时,为了防止用户账号裂开情况,将告知用户账号已存在,并弹窗引导用户登录原有账号执行绑定、解绑操作。

弹窗文案

  • 标题:账号已存在
  • 内容:当前 {第三方平台名称}账号所关联的邮箱({从第三方平台获取的邮箱信息})对应的游戏账号已绑定其他 {第三方平台名称}账号 。请使用该邮箱所关联的 {邮箱所关联心动账号下绑定该邮箱的其他登录方式} 登录游戏账号后进入「账号安全中心」进行账号绑定、解绑操作。

场景3