#Egret 白鹭引擎小游戏 SDK 使用指南

#Egret 白鹭引擎小游戏 SDK 使用指南

# Egret 白鹭引擎小游戏 SDK 使用指南 提示

在接入前, 请先阅读接入前准备。

您可以在 GitHub (opens new window) 获取 SDK 源码。

最新版本为: 2.0.1

更新时间为: 2021-11-15

下载地址 (opens new window)

# 一、集成 SDK # 1.1 引入 SDK 下载并解压 Egret SDK (opens new window).

引入 SDK 库与引入其他第三方库过程相同,在 egretProperties.json 中引入该库并编译引擎。

{

"name": "ThinkingAnalyticsSDK",

"path": "./libs/ThinkingAnalyticsSDK"

}

引入到项目中之后编译引擎即可使用 SDK 库。

# 1.2 初始化 SDK 集成 TA SDK 后,您可以在代码中直接使用 ThinkingAnalyticsAPI:

// TA SDK 配置对象

var config = {

appId: "YOUR_APPID", // 项目 APP ID

serverUrl: "YOUR_SERVER_URL", // 上报地址

autoTrack: {

appShow: true, // 自动采集 ta_mg_show

appHide: true // 自动采集 ta_mg_hide

}

};

// 创建 TA 实例

var ta = new ThinkingAnalyticsAPI(config);

// 初始化

ta.init();

// 上报一个简单事件, 事件名为 test_event

ta.track("test_event");

TA 配置对象参数说明如下:

appId: 您项目的 APP ID,必需, 可以在 TA 后台项目管理页查看 serverUrl: 数据上报 URL,必需 如果您使用的是云服务,填入: https://receiver.ta.thinkingdata.cn 如果您使用的是私有化部署的版本,请与运维同学确认上报地址 autoTrack:可选, 表示是否开启自动采集功能,每一个元素分别代表如下的自动采集事件,默认全部关闭:

appShow:自动采集小游戏启动,或从后台进入前台 appHide:自动采集小游戏从前台进入后台,并记录本次访问(启动至调入后台)的时间 enableNative: 允许调用 Native 代码(详见:Native 支持) 注意

在上报数据之前,请先在微信公众平台或其他平台的开发设置中,将数据传输 URL 加入到服务器域名的 request 列表中.

关于自动采集事件的详细信息,可以查看自动采集事件一节

# 二、设置用户 ID 在集成 SDK 之后,SDK 会使用 UUID 作为每个用户的默认访客 ID,该 ID 将会作为用户在未登录状态下身份识别 ID。需要注意的是,UUID 在用户更换设备及清理缓存时将会变更。

# 2.1 设置访客 ID 如果您希望使用其他 ID 来作为用户的访客 ID,您可以调用 identify 或者 authorizeOpenID 来设置访客 ID(两个方法是等价的,根据使用习惯保留两个接口名):

// 传入值为 openid

ta.authorizeOpenID("OpenID");

// identify 等价于 authorizeOpenID

ta.identify("OpenID");

需要注意以下几点:

设置后的 OpenID 将会被设置为访客 ID #distinct_id,如果使用 login 传入账号 ID,则根据用户识别规则,会优先以账号 ID 为准 如果需要进行设置,必须在 init 之前调用本接口 # 2.2 设置账号 ID 在用户产生登录行为时,可调用 login 来设置用户的账号 ID。TA 平台优先以账号 ID 作为身份标识,设置后的账号 ID 将会被保存,多次调用 login 将会覆盖先前的账号 ID:

// 用户的登录唯一标识,此数据对应上报数据里的 "#account_id",此时 "#account_id" 的值为 "ABC_123456"

ta.login("ABC_123456");

// 再次调用 login 调整账号 ID,此时"#account_id" 的值为 "XYZ_987654"

ta.login("XYZ_987654");

请注意,该方法不会上传用户登录的事件

# 2.3 清除账号 ID 在用户产生登出行为之后,可调用 logout 来清除账号 ID,在下次调用 login 之前,将会以访客 ID 作为身份识别 ID:

// 去除上报数据里的 "#account_id",之后的数据将不带有 "#account_id"

ta.logout();

请注意,该方法不会上传用户登出的事件

# 三、发送事件 您可以直接调用 track 上传自定义事件,建议您根据先前梳理的文档来设置事件的属性以及发送信息的条件,此处以购买商品为范例:

ta.track(

"Purchase", // 追踪事件的名称

{

Item: "商品A",

ItemNum: 1,

Cost: 100,

Elements: ["apple", "ball", "cat"]

} // 需要上传的事件属性

);

track 接口共有两个参数,第一个参数为事件的名称,第二个参数为事件的属性 事件的名称是字符串,只能以字母开头,可包含数字,字母和下划线“_”,长度最大为 50 个字符,对字母大小写不敏感。 事件的属性是 JS 对象,每个元素代表一个属性。 元素的 name 对应属性的名称,规定只能以字母开头,包含数字,字母和下划线“_”,长度最大为 50 个字符,对字母大小写不敏感。 元素的 value 为该属性的值,支持 String、Number、Boolean、Date 和 Array. 注意: Array 类型属性需要配合 TA 平台 2.5 及之后的版本.

# 3.1 设置事件上报时间 事件触发的时间默认取本机时间,但在一些情况下,可能需要手动设置事件的时间,可以使用以下方法进行调用:

// 第三个参数,可以输入 Date 类型的参数,替换事件触发时间

ta.track("event", { parakey: "paravalue" }, new Date());

// 如果没有 properties 需要上传,请传入一个空对象

ta.track("event", {}, new Date());

第三个参数为事件触发时间,必须是 Date 类型,将会替换事件触发的时间,如果不传该参数,则事件触发时间默认选取用户的本机时间 # 3.2 记录事件时长 您可以调用 timeEvent 来开始计时,配置您想要计时的事件名称,当您上传该事件时,将会自动在您的事件属性中加入 #duration 这一属性来表示记录的时长,单位为秒。

// 用户进入商品页面,开始计时,记录的事件为 "Enter_Shop"

ta.timeEvent("Enter_Shop");

//...

// 上传事件,计时结束,"Enter_Shop" 这一事件中将会带有表示事件时长的属性 "#duration"

ta.track("Enter_Shop", { product_id: "A1354" });

# 3.3 设置公共属性 对于一些重要的属性,譬如用户的设备 ID、来源渠道、用户状态等,这些属性需要设置在每个事件中,此时您可以将这些属性设置为公共属性,即每个事件中都带有的属性。我们推荐您在发送事件前,先设置公共属性。

公共属性包含两种,事件公共属性和动态公共属性。在事件上报时,公共属性将会被插入到数据的 properties 中。如果此时公共属性与事件中设置的自定义属性有相同 key 值,则属性会根据下述优先级判断该取什么值:

用户自定义事件属性 > 动态公共属性 > 事件公共属性

# 3.3.1 设置事件公共属性 事件公共属性指的是静态的公共属性,在设置时只能传入常量,适合设置稳定不变的属性,您可以调用 setSuperProperties 来设置公共事件属性,公共事件属性的格式要求与事件属性一致。

根据属性优先级,自定义属性优先级高于事件公共属性,因此事件公共属性也可以作为某个属性的缺省值,在需要修改的事件中设置同名 Key 覆盖缺省值。

// 设置公共事件属性

ta.setSuperProperties({ channel: "渠道" });

// 使用track上传事件,此时事件中会带有公共事件属性

ta.track(

"Purchase", // 追踪事件的名称

{

Item: "商品A",

ItemNum: 1,

Cost: 100

} // 需要上传的事件属性

);

/* 等价于在事件中加入这些公共属性

ta.track(

'Purchase', // 追踪事件的名称

{

Item:'商品A',

ItemNum:1,

Cost:100,

channel:'渠道' // 相当于在事件中加入这个属性

}

);

*/

如果多次调用 setSuperProperties 设置公共事件属性,则同名字段后面的调用会覆盖之前的,不同名字段。

如果您需要删除某个公共事件属性,可以调用 unsetSuperProperty() 清除其中一个公共事件属性;如果您想要清空所有公共事件属性,则可以调用 clearSuperProperties();如果您想要获取所有公共事件属性,可以调用getSuperProperties;

// 清除属性名为 CHANNEL 的公共属性

ta.unsetSuperProperty("CHANNEL");

//清除公共事件属性

ta.clearSuperProperties();

//获取静态公共事件属性

ta.getSuperProperties();

# 3.3.2 设置动态公共属性 动态公共属性会在事件上报时,执行一个 function ,并把返回值作为该动态公共属性的值,加入到事件中。您可以调用 setDynamicSuperProperties 接口设置动态公共属性。该接口接受一个 function 作为参数。

// 通过动态公共属性设置 UTC 时间作为事件属性上报

ta.setDynamicSuperProperties(() => {

var localDate = new Date();

return {

utcTime: new Date(

localDate.getTime() + localDate.getTimezoneOffset() * 60000

)

};

});

function 必须返回一个 JS 对象,其中每个元素代表一个属性。 属性格式要求与事件属性一致。

# 四、用户属性 # 4.1 设置用户属性(userSet) 对于一般的用户属性,您可以调用 userSet 来进行设置,使用该接口上传的属性将会覆盖原有的属性值,如果之前不存在该用户属性,则会新建该用户属性

// 设置用户属性,会员等级

ta.userSet({ vip_level: "钻石会员" });

与上传事件时的事件属性一致,此处传入的用户属性也是对象类型,name 对应属性的名称,规定只能以字母开头,包含数字,字母和下划线“_”,长度最大为 50 个字符,对字母大小写不敏感,value 为该属性的值,支持 String、Number、Boolean、Date 和 Array。

注意:Array 类型需要配合 TA 平台 2.5 及之后版本使用.

# 4.2 初始化用户属性(userSetOnce) 如果您要上传的用户属性只要设置一次,则可以调用 userSetOnce 来进行设置,当该属性之前已经有值的时候,将会忽略这条信息。

// 以设置用户名为例,如果用户名已设置,则忽略本次设置,如果不存在,则设置为传入值

ta.userSetOnce({ user_name: "TA" });

userSetOnce 的参数要求与 userSet 一致。

# 4.3 累加用户属性(userAdd) 当您要上传数值型的属性时,您可以调用 userAdd 来对该属性进行累加操作,如果该属性还未被设置,则会赋值 0 后再进行计算

// 以付费为例,用户每次付费时调用此接口,则 'total_revenue' 字段每次会做累加付费金额的处理

ta.userAdd({ total_revenue: 50 });

与上传事件时的事件属性一致,此处传入的用户属性也是对象类型,name 对应属性的名称,规定只能以字母开头,包含数字,字母和下划线“_”,长度最大为 50 个字符,对字母大小写不敏感,value 为该属性需要累加的值,只支持 Number 类型,传入负数相当于做减法。

# 4.4 重置用户属性(userUnset) 当您要清空用户的某个用户属性值时,您可以调用 userUnset 来对指定属性进行清空操作,如果该属性还未在集群中被创建,则 userUnset 不会创建该属性

// 清空属性名为 userPropertykey 的用户属性值该用户,即设置成 NULL

ta.userUnset("userPropertykey");

# 4.5 删除用户(userDel) 如果您要删除某个用户,可以调用 userDel 将这名用户删除,您将无法再查询该名用户的用户属性,但该用户产生的事件仍然可以被查询到

ta.userDel();

# 4.6 为 Array 类型的用户属性追加元素 (userAppend) 您可以调用 userAppend 对 Array (List) 类型的用户数据追加元素。

ta.userAppend({ Elements: [("a": 1), ("b": 2)] });

注意:该特性需要配合 TA 平台 2.5 及之后版本使用

# 五、自动采集事件 在创建实例的 config 中开启您需要自动采集的事件,SDK 将会自动采集小游戏的一些行为,现在主要有以下几种事件支持自动采集:

现在支持的自动化收集数据有:

小游戏回到前台的事件 小游戏调入后台,并记录本次访问(启动至调入后台)的时间 接下来将会详细介绍每种数据的采集方法

# 5.1 开启自动采集事件 在 config 中,参数 autoTrack 中的元素表示每个自动采集事件的开关,设置为 true 为开启自动采集:

var config = {

appid: "YOUR_APPID",

server_url: "YOUR_SERVER_URL",

autoTrack: {

appShow: true, // 自动采集 ta_mp_show

appHide: true // 自动采集 ta_mp_hide

}

};

appShow:自动采集小游戏启动,或从后台进入前台 appHide:自动采集小游戏从前台进入后台 # 5.2 自动采集事件详解 # 5.2.1 小游戏启动 小游戏启动将会在小游戏被启动、或小游戏被从后台调回前台时触发,详细的事件介绍如下:

事件名:ta_mg_show 自动采集属性: #scene,场景值,取自微信提供的场景值 小游戏启动由于会受到调出前后台的影响(条数较多),因此不太适合直接进行分析,但是可以在行为路径中标识用户的一次使用,可以作为用户行为路径的初始行为

# 5.2.2 小游戏隐藏 小游戏隐藏将会在小游戏被调入后台时触发,并记录本次使用的时长,详细的事件介绍如下:

事件名:ta_mg_hide 自动采集属性:

#scene,场景值,取自微信提供的场景值 #duration,数值型,表示本次启动(ta_mp_show)到隐藏的持续时长 小游戏隐藏事件会记录使用时长(单位为秒),因此可以直接计算用户使用总时长以及人均时长,也可以除以初始化次数计算单次使用时长。

# 六、其他功能 # 6.1 多实例 本 SDK 支持多实例。我们称通过上文描述的方法完成初始化的实例 ta 为主实例,通过本节描述的方法创建的实例为子实例。

多个实例之间共享设备相关的预置属性(包括设备 ID),其他的属性均不共享,包括:

#distinct_id 访客 ID #account_id 账号 ID 公共事件属性、动态公共属性 timeEvent 监控的事件 您可以通过创建子实例,向另一个项目中上报数据,或者以另一套用户 ID 上报数据。

// 创建子实例 tt, 子实例默认配置与主实例相同

ta.initInstance("tt");

ta.lightInstance("tt").identify("another_distinct_id");

ta.lightInstance("tt").track("event_from_tt_instance");

// 创建不同配置的子实例

var config = {

appid: "ANOTHER-APP-ID",

enablePersistence: true // 为子实例开启本地缓存

};

ta.initInstance("tt_1", config);

ta.lightInstance("tt_1").track("event_from_tt1_instance");

# 6.2 获取设备 ID 您可以调用 getDeviceId() 获取设备 ID,由于运行环境的原因,设备 ID 会保存在本地缓存,一旦用户删除缓存,则设备 ID 将会变更,因此设备 ID 不能保证稳定不变

var deviceId = ta.getDeviceId();

# 6.3 onCompelete 回调函数 对于 track, userSet, userSetOnce, userAdd, userDel 等接口,支持传入 onComplete 回调.

可以直接在原参数列表后传入 onComplete, 也可以使用参数对象的方式. 如果使用参数对象,参数对象中必须包含 onComplete, 否则会出现参数错误.

以上传事件为例:

// 以参数列表的形式传入回调

ta.track("test", { testkey: 123 }, new Date(), res => {

console.log(res);

});

// 以参数对象的形式传入回调

ta.track({

eventName: "test", // 必填

properties: { testkey: 123 }, // 可选

time: new Date(), // 可选

onComplete: res => {

console.log(res);

} // 必填

});

onComplete 的参数 res 为 object 类型,有两个属性 code 和 msg.

res.code 为 int 类型,定义如下:

0: 成功 -1: 数据格式不正确 -2: APP ID 无效 -3: 网络或服务端异常 Debug 模式定义如下:

0: 成功 -1: 参数或者权限校验的问题 1: 表示字段基本的错误, 会给出详细的错误字段和原由 2: 表示整条错误 -3: 网络或服务端异常 res.msg 是对 res.code 的文字说明。

# 6.4 开启 Debug 模式 从 v1.6.0 版本开始,客户端 SDK 支持 Debug 模式,需要配合 TA 平台 2.5 之后的版本使用。

Debug 模式可能会影响数据采集质量和 App 的稳定性,只用于集成阶段数据验证,不要在线上环境使用。

当前 SDK 实例支持三种运行模式:

"none": 不开启 Debug "debug": 开启 Debug 模式,并入库 "debugOnly": 开启 Debug 模式,不入库 SDK 初始化:

var config = {

appid: "YOUR_APPID",

server_url: "YOUR_SERVER_URL",

debugMode: "debug"

};

ta = new ThinkingDataAPI(config);

ta.init();

为了避免 Debug 模式在生产环境上线,规定只有指定的设备才能开启 Debug 模式。只有在客户端开启了 Debug 模式,并且设备 ID 在 TA 后台的"埋点管理"页的"Debug 数据"板块中配置了的设备才能开启 Debug 模式。

设备 ID 可以通过以下三种方式获取:

TA 平台中事件数据中的 #device_id 属性 通过实例接口调用:获取设备 ID # 七、相关预置属性 # 7.1 所有事件带有的预置属性 以下预置属性,是小游戏 SDK 中所有事件(包括自动采集事件)都会带有的预置属性

属性名 中文名 说明 #ip IP 地址 用户的 IP 地址,TA 将以此获取用户的地理位置信息 #country 国家 用户所在国家,根据 IP 地址生成 #country_code 国家代码 用户所在国家的国家代码(ISO 3166-1 alpha-2,即两位大写英文字母),根据 IP 地址生成 #province 省份 用户所在省份,根据 IP 地址生成 #city 城市 用户所在城市,根据 IP 地址生成 #device_model 设备型号 用户设备的型号,如 iPhone 8 等 #device_id 设备 ID 用户的设备 ID,取初始化时生成的 UUID #screen_height 屏幕高度 用户设备的屏幕高度,如 1920 等 #screen_width 屏幕宽度 用户设备的屏幕高度,如 1080 等 #manufacturer 设备制造商 用户设备的制造商,如 Apple,vivo 等 #os_version 操作系统版本 iOS 11.2.2、Android 8.0.0 等 #os 操作系统 如 Android、iOS 等 #network_type 网络状态 上传事件时的网络状态,如 WIFI、3G、4G 等 #lib SDK 类型 您接入 SDK 的类型,如 MG(小游戏) #lib_version SDK 版本 您接入 SDK 的版本 #scene 场景值 微信小游戏启动时传入的场景值 #mp_platform 小游戏平台 标识应用所在的平台 #zone_offset 时区偏移 数据时间相对 UTC 时间的偏移小时数 # 7.2 自动采集事件的预置属性 以下预置属性,是各个自动采集事件中所特有的预置属性

小游戏隐藏(ta_mp_hide)的预置属性 属性名 中文名 说明 #duration 事件时长 表示本次启动 ta_mp_show 到隐藏 ta_mp_hide 的持续时长,单位是秒 # 7.3 获取预制属性 服务端埋点需要 App 端的一些预置属性时,可以通过此方法获取 客户端 端的预置属性,再传给服务端。

//获取属性对象

var presetProperties = ta.getPresetProperties();

//生成事件预置属性

var properties = presetProperties.toEventPresetProperties();

/*

{

"#device_model":"iPhone 5",

"#device_id":"3204487163-1624513721217",

"#screen_width":320,

"#screen_height":568,

"#os":"iOS",

"#os_version":"10.0.1",

"#network_type":"wifi",

"#zone_offset":8,

"#manufacturer":"Apple"

}

*/

//获取某个预置属性

var os = presetProperties.os;//os类型,如Android

var osVersion = presetProperties.osVersion;//系统版本号

var networkType = presetProperties.networkType;//网络类型

var manufacture = presetProperties.manufacture;//设备制造商

var deviceModel= presetProperties.deviceModel;//设备型号

var screenWidth = presetProperties.screenWidth;//屏幕宽度

var screenHeight = presetProperties.screenHeight;//屏幕高度

var deviceId = presetProperties.deviceId;//设备ID

var zoneOffset = presetProperties.zoneOffset;//时区偏移值

IP,国家城市信息由服务端解析生成,客户端不提供接口获取这些属性

# 八、进阶功能 从 v1.7.0 开始,SDK 支持上报三种特殊类型事件: 首次事件、可更新事件、可重写事件。这三种事件需要配合 TA 系统 2.8 及之后的版本使用。由于特殊事件只在某些特定场景下适用,所以请在数数科技的客户成功和分析师的帮助下使用特殊事件上报数据。

# 8.1 首次事件 首次事件是指针对某个设备或者其他维度的 ID,只会记录一次的事件。例如在一些场景下,您可能希望记录在某个设备上第一次发生的事件,则可以用首次事件来上报数据。

// 示例:上报设备首次事件, 假设事件名为 DEVICE_FIRST

ta.trackFirstEvent({

eventName: "DEVICE_FIRST",

properties: { INT_PROPERTY: 0 }

});

如果您希望以设备以外的其他维度来判断是否首次,则可以为首次事件设置 FIRST_CHECK_ID. 例如您需要记录某个账号的首次事件,可以将账号 ID 设置为首次事件的 FIRST_CHECK_ID:

// 示例:上报用户账号的首次事件, 假设事件名为 USER_FIRST

// 将用户 ID 设置为首次事件的 FIRST_CHECK_ID

ta.trackFirstEvent({

eventName: "USER_FIRST",

firstCheckId: "YOUR_ACCOUNT_ID",

properties: { INT_PROPERTY: 0 }

});

注意:由于在服务端完成对是否首次的校验,首次事件默认会延时 1 小时入库。

# 8.2 可更新事件 您可以通过可更新事件实现特定场景下需要修改事件数据的需求。可更新事件需要指定标识该事件的 ID,并在创建可更新事件对象时传入。TA 后台将根据事件名和事件 ID 来确定需要更新的数据。

// 示例: 上报可被更新的事件,假设事件名为 UPDATABLE_EVENT

ta.trackUpdate({

eventName: "UPDATABLE_EVENT",

properties: { status: 3, price: 100 },

eventId: "test_event_id"

});

// 上报后事件属性 status 为 3, price 为 100

ta.trackUpdate({

eventName: "UPDATABLE_EVENT",

properties: { status: 5 },

eventId: "test_event_id"

});

// 上报后事件属性 status 被更新为 5, price 不变

# 8.3 可重写事件 可重写事件与可更新事件类似,区别在于可重写事件会用最新的数据完全覆盖历史数据,从效果上看相当于删除前一条数据,并入库最新的数据。TA 后台将根据事件名和事件 ID 来确定需要更新的数据。

// 示例: 上报可被重写的事件,假设事件名为 OVERWRITE_EVENT

ta.trackOverwrite({

eventName: "OVERWRITE_EVENT",

properties: { status: 3, price: 100 },

eventId: "test_event_id"

});

// 上报后事件属性 status 为 3, price 为 100

ta.trackOverwrite({

eventName: "OVERWRITE_EVENT",

properties: { status: 5 },

eventId: "test_event_id"

});

// 上报后事件属性 status 被更新为 5, price 属性被删除

# 九、Native 支持 # 9.1 iOS Native 支持 构建 iOS 平台应用

# 9.1.1 配置 iOS 工程 添加 iOS 工程依赖文件

EgretProxyApi.h EgretProxyApi.mm ThinkingSDK.framework Build Settings 设置

Other Linker Flags 添加 -ObjC # 9.2 Android Native 支持 构建 Android 平台应用

# 9.2.1 配置 Android 工程 添加 Adnroid 工程依赖文件

EgretProxyApi.java 在 Project 级别的 build.gradle 文件中添加如下配置依赖 buildscript {

repositories {

jcenter()

mavenCentral()

}

}

在 Module 工程目录下的 build.gradle文件中添加依赖项 dependencies {

implementation 'cn.thinkingdata.android:ThinkingAnalyticsSDK:2.7.3'

}

# 9.3 开启 Native 支持 初始化 SDK 时,在 config 中加入 enableNative: true,即可开启 Native 支持。

// TA SDK 配置对象

var config = {

appId: "YOUR_APPID", // 项目 APP ID

serverUrl: "YOUR_SERVER_URL", // 上报地址

enableNative: true // 允许调用 Native 代码

};

// 创建 TA 实例

var ta = new ThinkingAnalyticsAPI(config);

// 初始化

ta.init();

// 上报一个简单事件, 事件名为 test_event

ta.track("test_event");

# Release Notes v2.0.1 2021/11/15

代码优化 v2.0.0 2021/09/15

支持 iOS/Android Native 支持暂停/开始、停止/恢复数据上报 支持事件黑名单 代码优化 v1.8.2 2021/06/24

支持预制属性获取 fix bug:原生/web平台运行时,断网重连导致的数据发送失败问题 v1.8.0 2020/11/20

新增哔哩哔哩小游戏支持 完善游戏引擎支持:支持 ts 项目接入 完善游戏引擎支持:支持常见小游戏、快游戏平台(华为、小米、OPPO、VIVO)、H5 游戏 其他代码细节层面的优化 v1.7.0 2020/08/24

支持首次事件,可更新事件,可重写事件 v1.6.0 2020/06/10

支持 debug 模式 v1.5.1 2020/03/21

支持白鹭引擎小游戏平台:微信小游戏、百度小游戏、QQ 小游戏、VIVO 小游戏、OPPO 小游戏 支持 LAYABOX 引擎小游戏平台:微信小游戏、百度小游戏、QQ 小游戏、VIVO 小游戏、OPPO 小游戏 支持 CocosCreator 引擎小游戏平台:微信小游戏、百度小游戏、VIVO 小游戏、OPPO 小游戏

更多创意

百度、搜狗、360搜索引擎的收录时间
365bet体育

百度、搜狗、360搜索引擎的收录时间

📅 09-24 🔥 3650
快手互粉方法:快手平台的粉丝互动策略
77365bet体育在线投注

快手互粉方法:快手平台的粉丝互动策略

📅 10-02 🔥 9609
微信儿童手表版到底为何而战?
365bet体育

微信儿童手表版到底为何而战?

📅 08-20 🔥 8662