---
code: unity_sdk_advanced
name: "进阶指南"
wikiToken: BMYXwREPHidgIokPXd3cibSdnqe
parentWikiToken: Vlbrwj9vYijvVIkwaCtcIABFnMf
updateTime: 1753673354000
sourceUrl: https://docs-v2.thinkingdata.cn/?version=v5.0&lan=zh-CN&code=unity_sdk_advanced
---
# 进阶指南

### 一、设置用户ID

SDK 实例默认会使用随机 UUID 作为每个用户的默认访客 ID，该 ID 将会作为用户在未登录状态下身份识别 ID。需要注意的是，访客 ID 在用户重新安装 App 以及更换设备时将会变更。

#### 1.1 设置访客 ID

::: tip 提示

 一般情况下，您不需要自定义访客 ID. 请确保已经理解用户识别规则，再进行访客 ID 的设置。

如果您需要替换访客 ID，则应当在初始化 SDK 结束之后立即进行调用，请勿多次调用，以免产生无用的账号

 :::

如果您的 App 对每个用户有自己的访客 ID 管理体系，则您可以调用`SetDistinctId` 来设置访客 ID：

```
// 将访客ID设置为Thinker
TDAnalytics.SetDistinctId("Thinker");
```

如果需要获得当前访客 ID，可以调用 `GetDistinctId` 获取：

```
//返回访客ID
String distinctId = TDAnalytics.GetDistinctId();
```

#### 1.2 设置账号 ID

在用户进行登录时，可调用 `login` 来设置用户的账号 ID， AE 平台将会以账号 ID 作为身份识别 ID，并且设置的账号 ID 将会在调用 `logout` 之前一直保留。多次调用 `login` 将覆盖先前的账号 ID 。

```
// 用户的登录唯一标识，此数据对应上报数据里的#account_id，此时#account_id的值为TA
TDAnalytics.Login("TA");
```

<!-- unsupported block: 34 -->

**该方法不会上传登录事件**

#### 1.3 清除账号ID

在用户产生登出行为之后，可调用 `Logout` 来清除账号 ID，在下次调用 `login` 之前，将会以访客 ID 作为身份识别 ID。

```
TDAnalytics.Logout();
```

我们推荐您在显性的登出事件时调用 `Logout`，比如用户产生了注销账号这一行为时才调用，而不需要在关闭 App 时调用。

<!-- unsupported block: 34 -->

**该方法不会上传登出事件**

### 二 、发送事件

在 SDK 初始化完成之后，您就可以进行数据埋点，收集用户的的行为信息。一般情况下普通事件即可满足业务场景需求，您也可以根据自己的实际业务场景使用首次、可更新等事件。

#### 2.1 普通事件

您可以调用 `Track` 来上传事件，建议您根据先前梳理的文档来设置事件的属性以及发送事件的条件，此处以用户购买某商品作为范例：

```
Dictionary<string, object> properties = new Dictionary<string, object>(){
    {"product_name", "商品名"}
};
TDAnalytics.Track("product_buy", properties);
```

#### 2.2 首次事件

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

```
Dictionary<string, object> properties = new Dictionary<string, object>() { 
    { "status", 1} 
};
TDFirstEventModel firstEvent = new TDFirstEventModel("first_event");
firstEvent.Properties = properties;
TDAnalytics.Track(firstEvent);
```

如果您希望以设备以外的其他维度来判断是否首次，则可以为首次事件自定义first_check_id:

```
// 将用户ID设置为首次事件的first_check_id，实现用户首次激活事件的采集
Dictionary<string, object> properties = new Dictionary<string, object>() { 
    { "status", 1} 
};
TDFirstEventModel firstEvent = new TDFirstEventModel("first_event", "any-user-id");
firstEvent.Properties = properties;
TDAnalytics.Track(firstEvent);
```

<!-- unsupported block: 34 -->

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

#### 2.3 可更新事件

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

```
// 示例： 上报可被更新的事件，假设事件名为 UPDATABLE_EVENT
// 上报后事件属性 status 为 3, price 为 100
TDUpdatableEventModel updatableEvent = new TDUpdatableEventModel("UPDATABLE_EVENT", "test_event_id");
updatableEvent.Properties = new Dictionary<string, object>{
    {"status", 3},
    {"price", 100}
};
TDAnalytics.Track(updatableEvent);

// 上报后事件属性 status 被更新为 5, price 不变
TDUpdatableEventModel updatableEvent_new = new TDUpdatableEventModel("UPDATABLE_EVENT", "test_event_id");
updatableEvent_new.Properties = new Dictionary<string, object>{
    {"status", 5}
};
TDAnalytics.Track(updatableEvent_new);
```

#### 2.4 可重写事件

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

```
// 示例： 上报可被重写的事件，假设事件名为 OVERWRITABLE_EVENT
// 上报后事件属性 status 为 3, price 为 100
TDOverwritableEventModel overWritableEvent = new TDOverwritableEventModel("OVERWRITABLE_EVENT", "test_event_id");
overWritableEvent.Properties = new Dictionary<string, object>{
    {"status", 3},
    {"price", 100}
};
TDAnalytics.Track(overWritableEvent);

// 上报后事件属性 status 被更新为 5, price 属性被删除
TDOverwritableEventModel overWritableEvent_new = new TDOverwritableEventModel("OVERWRITABLE_EVENT", "test_event_id");
overWritableEvent_new.Properties = new Dictionary<string, object>{
    {"status", 5}
};
TDAnalytics.Track(overWritableEvent_new);
```

#### 2.5 公共事件属性

公共事件属性指的就是每个事件都会上传的属性。根据属性更新频率，公共事件属性分为`静态公共事件属性`和`动态公共事件属性`。您可以根据具体的业务场景需求，选择不同的公共事件属性设置方法；我们推荐您在发送事件前，先设置公共事件属性。针对同一事件，当公共事件属性、事件自定义属性、预置属性的Key相同时，我们会按照如下优先级进行赋值:`自定义属性>动态公共事件属性>静态公共事件属性>预置属性`。

##### 2.5.1 静态公共事件属性

静态公共事件属性是低频变化且每个事件都会带有的属性，如用户会员等级。通过`setSuperProperties`设置静态公共事件属性之后，SDK将会在事件采集时获取设置的公共事件属性作为事件的属性。

```
Dictionary<string, object> superProperties = new Dictionary<string, object>(){ 
    {"vip_level", 2}
};
TDAnalytics.SetSuperProperties(superProperties);
```

静态公共事件属性将会被保存到缓存中，无需每次启动 App 时调用。如果该属性已存在，重新设置的属性将会覆盖原有属性值;如果之前不存在该属性，则会新建属性。除了属性设置，我们也提供其他API来操作静态公共事件属性，满足日常的业务需求。

```
// 清除属性名为 CHANNEL 的公共属性
TDAnalytics.UnsetSuperProperty("CHANNEL");
// 清空所有公共属性
TDAnalytics.ClearSuperProperties();
// 获取所有公共属性
TDAnalytics.GetSuperProperties();
```

##### 2.5.2 动态公共事件属性

动态公共事件属性是高频变化且每个事件都会带有的属性，如用户的金币数量。设置动态公共属性，需要先新建动态公共属性类并实现 `TDDynamicSuperPropertiesHandler` 接口，复写 `public Dictionary<string, object> GetDynamicSuperProperties()` 方法，该方法的返回值即是需要设置的动态公共属性，然后调用 `SetDynamicSuperProperties` 传入动态公共属性对象，样例如下：

```
// 1.定义动态公共属性实现，此例为设置金币动态变化的示例
public class DynamicProp : TDDynamicSuperPropertiesHandler
{    
    int coin = 0;
    public Dictionary<string, object> GetDynamicSuperProperties()
    {  
         coin++;
         return new Dictionary<string, object>() {
             {"coin",coin}
         };
    }
}
// 2.设置动态公共属性
TDAnalytics.SetDynamicSuperProperties(new DynamicProp());
```

#### 2.6 记录事件时长

如果您需要记录某个事件的持续时长，可以调用 `TimeEvent` 来开始计时。配置您想要计时的事件名称，当您上传该事件时，将会自动在您的事件属性中加入 `#duration` 这一属性来表示记录的时长，单位为秒。需要注意的是，同一个事件名只能有一个在计时的任务。

```
//以下示例，完成用户在某个商品页面停留时长的统计
//用户进入商品页面，开始计时
TDAnalytics.TimeEvent("stay_shop");
 /**do someting
    .......
 **/
//用户离开商品页面，计时结束，"stay_shop" 这一事件中将会带有表示事件时长的属性#duration
TDAnalytics.Track("stay_shop");
```

### 三、用户属性

AE 平台支持的用户属性设置API有: `UserSet`、`UserSetOnce`、`UserAdd`、`UserUnset`、`UserDelete`、`UserAppend`、`UserUniqAppend`。

#### 3.1 UserSet

对于一般的用户属性，您可以调用 `UserSet` 来进行设置，使用该接口上传的属性将会覆盖原有的属性值，如果之前不存在该用户属性，则会新建该用户属性。

```
//此时username为TA
TDAnalytics.UserSet(new Dictionary<string, object>(){
    {"user_name", "TA"}
});
//此时username为TE
TDAnalytics.UserSet(new Dictionary<string, object>(){
    {"user_name", "TE"}
});
```

#### 3.2 UserSetOnce

如果您要上传的用户属性只要设置一次，则可以调用 `UserSetOnce` 来进行设置，当该属性之前已经有值的时候，将会忽略这条信息：

```
//first_payment_time为2018-01-01 01:23:45.678
TDAnalytics.UserSetOnce(new Dictionary<string, object>(){
    {"first_payment_time","2018-01-01 01:23:45.678"}
});
 //first_payment_time仍然为2018-01-01 01:23:45.678
TDAnalytics.UserSetOnce(new Dictionary<string, object>(){
    {"first_payment_time","2018-12-31 01:23:45.678"}
});
```

#### 3.3 UserAdd

当您要上传数值型的属性时，您可以调用 `UserAdd` 来对该属性进行累加操作，如果该属性还未被设置，则会赋值 `0` 后再进行计算，可传入负值，等同于相减操作。

```
//此时total_revenue为30
TDAnalytics.UserAdd(new Dictionary<string, object>(){
    {"total_revenue",30}
});
//此时total_revenue为678
TDAnalytics.UserAdd(new Dictionary<string, object>(){
    {"total_revenue",648}
})
```

<!-- unsupported block: 34 -->

设置的属性key为字符串，Value 只允许为数值。

#### 3.4 UserUnset

如果您需要重置用户的某个属性，可以调用 `UserUnset` 将该用户指定用户属性的值清空，此接口支持传入字符串或者列表类型的参数:

```
// 删除单个用户属性
TDAnalytics.UserUnset("userPropertyName");
// 删除多个用户属性
List<string> listProps = new List<string>();
listProps.Add("aaa");
listProps.Add("bbb");
listProps.Add("ccc");

TDAnalytics.UserUnset(listProps);
```

<!-- unsupported block: 34 -->

UserUnset: 的传入值为被清空属性的 Key 值。

#### 3.5 UserDelete

如果您要删除某个用户，可以调用 `UserDelete` 将这名用户删除，您将无法再查询该名用户的用户属性，但该用户产生的事件仍然可以被查询到。

```
TDAnalytics.UserDelete();
```

#### 3.6 UserAppend

从 v1.4.0 开始，您可以调用 `UserAppend` 为 `List` 类型的用户属性追加元素:

```
List<string> stringList = new List<string>();
stringList.Add("apple");
stringList.Add("ball");
// 为属性名为user_list 的用户属性追加 3 个元素
TDAnalytics.UserAppend(new Dictionary<string, object>{
    {"user_list", stringList }
});
```

#### 3.7 UserUniqAppend

从 v2.4.0 开始，您可以调用 `UserUniqAppend` 为 `List` 类型的用户属性进行**去重追加**元素。调用 `UserUniqAppend` 接口会对追加的用户属性进行去重， `UserAppend` 接口不做去重，用户属性可存在重复。

```
//此时user_list的属性值为["apple"，"ball"]
List<string> stringList = new List<string>();
stringList.Add("apple");
stringList.Add("ball");
TDAnalytics.UserAppend(new Dictionary<string, object>{
    {"user_list", stringList}
});

List<string> stringList1 = new List<string>();
stringList1.Add("apple");
stringList1.Add("cube");
//此时user_list的属性值为["apple","apple","ball","cube"]
TDAnalytics.UserAppend(new Dictionary<string, object>{
    {"user_list", stringList1}
});
//此时user_list的属性值为["apple","ball","cube"]
TDAnalytics.UserUniqAppend(new Dictionary<string, object>{
    {"user_list", stringList1}
});
```

### 四、加密功能

从 v2.4.0 版本开始，SDK 支持使用AES+RSA加密数据。数据加密功能需要客户端和服务端配合完成，具体使用方法请咨询客户成功人员。

分别设置 `TDConfig` 对象的 `enableEncrypt` 属性为 `true`，并设置默认版本号和公钥。

```
TDConfig tdConfig = new TDConfig(appId, serverUrl);
// 开启加密传输（仅支持iOS/Android），设置默认版本号、公钥
tdConfig.EnableEncrypt("YOUR_ENCRYPT_PUBLIC_KEY", 1);
TDAnalytics.Init(tdConfig);
```

### 五、其他功能

#### 5.1 获取设备ID

您可以通过调用 `GetDeviceId` 来获取设备 ID：

```
TDAnalytics.GetDeviceId();
// 以设备ID作为访客ID
// TDAnalytics.SetDistinctId(TDAnalytics.GetDeviceId());
```

#### 5.2 设置默认时区

默认情况下,SDK 默认会使用接口调用时的本机时间作为事件发生时间上报。您也可以通过设置默认时区接口，指定默认的时区，这样所有的事件都将按照您设置的时区来对齐事件时间：

```
TDConfig tdConfig = new TDConfig(appId, serverUrl);
tdConfig.timezone = TDTimeZone.UTC;
TDAnalytics.Init(tdConfig);
```

<!-- unsupported block: 34 -->

用指定时区对齐事件时间，会丢掉设备本机时区信息。如果需要保留设备本机时区信息，目前需要您自己为事件添加相关属性。

#### 5.3 校准时间

SDK 默认会使用本机时间作为事件发生时间上报，如果用户手动修改设备时间会影响到您的业务分析，此时可以通过校准时间操作保证事件发生时间的准确性。我们提供`时间戳、NTP`两种时间校准方式。

- 您可以使用从服务端获取的当前时间戳对 SDK 的时间进行校准。此后，所有未指定时间的调用，包括事件数据和用户属性设置操作，都会使用校准后的时间作为发生时间。
```
// 1585633785954 为当前 unix 时间戳，单位为毫秒，对应北京时间 2020-03-31 13:49:45
TDAnalytics.CalibrateTime(1585633785954);
```

- 您也可以设置NTP服务器地址，之后 SDK 会尝试从传入的 NTP 服务地址中获取当前时间，并对 SDK 时间进行校准。如果在默认的超时时间（3 秒）之内，未获取正确的返回结果，后续将使用本地时间上报数据。
```
// 使用苹果公司 NTP 服务对时间进行校准
TDAnalytics.CalibrateTimeWithNtp("time.apple.com");
```

<!-- unsupported block: 34 -->

1、使用 NTP 服务进行时间校准存在一定的不确定性，建议您优先考虑用时间戳校准的方式

2、您需要谨慎地选择您的 NTP 服务器地址，以保证网络状况良好的情况下，用户设备可以很快的获取到服务器时间

#### 5.4 立即上报数据

在某些业务场景下，如果您期望数据立即上报到 AE 服务器，可以通过调用`Flush`接口完成

```
TDAnalytics.Flush();
```

#### 5.5 获取国家/地区代码

在某些业务场景下，如果您需要知道用户设备的国家/地区代码，可以通过`GetLocalRegion`来获取

```
TDAnalytics.GetLocalRegion();
```

#### 5.6 支持Lua方式调用

如果需要在Lua文件中直接调用，可以使用封装好的Lua API。点击下载

下载好之后，将`TDAnalytics.lua`和`TDAnalyticsProxy.cs`导入到项目中。

使用示例如下：

```
local config = {
    appId = "AppId",
    serverUrl = "ServerUrl",
    enableLog = true, --是否开启日志，默认为false--
    mode = 'debug' --默认为normal--
}
--SDK初始化--
TDAnalytics.init(config);

--如果用户已登录，可以设置用户的账号ID作为身份唯一标识
TDAnalytics.login("TA")

--设置公共事件属性以后，每个事件都会带有公共事件属性
local superProperties = {}
superProperties["channel"] = "ta" -- 字符串
superProperties["age"] = 1 -- 数字
superProperties["isSuccess"] = true -- 布尔
superProperties["birthday"] = os.date("%Y-%m-%d %H:%M:%S") -- 时间
superProperties["object"] = { key="value" } -- 对象
superProperties["object_arr"] = { { key="value" } } -- 对象组
superProperties["arr"] = { "value" } -- 数组
TDAnalytics.setSuperProperties(superProperties) -- 设置公共事件属性

--发送事件
TDAnalytics.track("product_buy", {
    product_name="商品名"
});

--设置用户属性
TDAnalytics.userSet({
    user_name = "TE"
})
```

#### 5.7 微信自动数据采集

针对于微信小游戏平台，目前可支持show事件，hide事件，launch事件自动采集，接入方式如下:

- 下载微信小游戏插件
页面栏 Window->Package Manager-> + -> Add package from git url

PackageManager(git安装URL): https://github.com/wechat-miniprogram/minigame-tuanjie-transform-sdk.git

- 自定义宏
页面栏 Edit -> Project Settings -> Scripting Define Symbols

新增 全局宏参数 TD_WEIXIN_GAME_MODE

点击 Apply 按钮完成设置

-  程序集添加依赖
project窗口布局 ThinkingAnalytics文件夹 -> TDAnalytics(Assembly Definition) -> Assembly Definition References -> + -> WxWasmSDKRuntime

使用实例如下:

```
// 开启自动采集事件，支持show, hide, launch事件
TDAnalytics.EnableAutoTrack(TDAutoTrackEventType.AppStart | TDAutoTrackEventType.AppEnd | TDAutoTrackEventType.AppInstall);
```

### 六、渠道SDK兼容

#### 6.1 腾讯广告

##### 6.1.1 方案简述

在集成了TDAnalytics SDK之后，您无需额外集成腾讯广告SDK。一旦您完成了TDAnalytics的初始化方法，系统会自动触发腾讯广告SDK的初始化。当您上报注册、付费等关键事件时，系统会根据您的配置自动将这些事件信息报送给腾讯广告。

##### 6.1.2 接入流程

1. 下载腾讯广告SDK，目前使用的是1.5.4版本，如果换成其他版本的也可以。
1. 初始化
<!-- unsupported block: 19 -->

TDAnalytics SDK的版本需 >= 3.1.1

```
TDConfig config = new TDConfig("APPID","SERVER");
config.reportingToTencentSdk = 2; //1 只上报给腾讯 2 上报给腾讯和AE 3 只上报给TE
6TDAnalytics.Init(config);
```

如果需要将数据上报到腾讯，需要在做以下操作：

导出微信小游戏项目之后，将dn-sdk-minigame.js文件导入至项目中，修改game.js引入dn-sdk并完成初始化

```
import { SDK } from "./dn-sdk-minigame.js";
try {
  // 初始化
  GameGlobal.dnSDK = new SDK({
    user_action_set_id: 123xxxxxx,
    secret_key: 'xxxxxxxxxxxxxxxxxxx',
    appid: 'xxxxxxxxxxxxx',
  });
  // 上报启动
  GameGlobal.dnSDK.onAppStart();
} catch {

}
```

1. 设置用户ID
- setOpenId
openid一般是调用后端接口异步获取的(获取openid方法)，请在获取到 openid 后调用* sdk.setOpenId() *方法设置。openid 和 unionid 只能设置一个，优先设置openid。

获取到openid之后调用

```
TDAnalytics.login(openid);
```

- setUnionId
unionid 一般是调用后端接口异步获取的(获取unionid方法)，请在获取到 unionid 后调用* sdk.setUnionId() *方法设置。没有openid才需使用此方法设置unionid。

获取到openid之后调用

```
TDAnalytics.setDistinctId(unionid);
```

1. 上报行为
```
Dictionary<string, object> properties = new Dictionary<string, object>(){{"product_name", "商品名"}};
TDAnalytics.Track("product_buy", properties);
```

如果是以下特定事件，需要上报指定的事件名称

事件

事件名

事件属性（需要包含其中的key）

##### 小游戏启动

##### START_APP

无

##### 付费

##### PURCHASE

{

        value: 600

}

##### 注册

##### REGISTER

##### 沉默唤起

##### RE_ACTIVE

{

        backFlowDay: 30

}

##### 收藏小游戏

##### ADD_TO_WISHLIST

{

        type: 'default',

}

##### 分享小游戏

##### SHARE

{

        target: 'APP_MESSAGE'

}

##### 创建角色

##### CREATE_ROL

{

        name: 'SuperMan'

}

##### 完成新手指引

##### TUTORIAL_FINISH

无

##### 游戏等级提升

##### UPDATE_LEVEL

{

        level: 2,

        power: 85,

}

##### 浏览商城页面

##### VIEW_CONTENT

{

// 关键场景访问：商城

        item: 'Mall',

}

##### 浏览游戏活动

##### VIEW_CONTENT

{

  // 关键场景访问：活动

  item: 'Activity',

}

比如上报游戏等级提升事件

```
Dictionary<string, object> properties = new Dictionary<string, object>();
properties["level"] = 2;
properties["power"] = 85;
2TDAnalytics.Track("product_buy", properties);
```
