# Vivox Unity SDK 16.0.0 升级指南

> Upgrade from previous Vivox versions to version 16.0.0.

> **Note:**
>
> 如果要从 Vivox Unity 版本 15.1.x 或更低版本进行升级，请遵循本指南。

本指南详细介绍如何升级到 Vivox Unity SDK 版本 16.0.0，并重点讨论两个 SDK 版本之间的差异。

版本 16.0.0 推出了简化的功能，并在以下相关方面也进行了更改：

* SDK 初始化
* 登录/注销
* 屏蔽玩家
* 连接到频道
* 频道参与者管理
* 音频设备管理
  * 设置活动设备
  * 设置设备增益/音量
  * 设备静音

## SDK 设置和初始化##sdk-setup-and-initialization

将 `using VivoxUnity;` 替换为 `using Unity.Services.Vivox;` 以访问 Vivox API。

VivoxUnity 命名空间不再存在。

此外，客户端（用于初始化 SDK 的原始方式）已不复存在。

### 使用 Authentication##using-authentication

> **Note:**
>
> 如果您使用的是 Vivox 开发者门户中的凭据，请跳过此部分并跳转到“Vivox 开发者门户凭据”部分。

如果您使用的是 Unity Authentication 包，请依次调用 `await UnityServices.InitializeAsync()`、`await AuthenticationService.Instance.SignInAnonymously()`、[`await VivoxService.Instance.InitializeAsync()`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_InitializeAsync_Unity_Services_Vivox_VivoxConfigurationOptions_)，如以下示例所示：

```cs
async void InitializeAsync()
{
    await UnityServices.InitializeAsync();
    await AuthenticationService.Instance.SignInAnonymouslyAsync();
    await VivoxService.Instance.InitializeAsync();
}

```

还可以使用 `AuthenticationService.Instance` 提供的任何其他登录选项。

如果项目使用 Unity Dashboard（Unity 后台）凭据但未使用 AuthenticationService 包，则可以启用**测试模式**并省略 `AuthenticationService` 登录调用。要打开**测试模式**，请在 Unity 编辑器中转到 **Project Settings > Services > Vivox** 并选中标有 **Test Mode** 的框。

> **Note:**
>
> 启用测试模式会在客户端上缓存项目的令牌签名密钥，只能用于测试目的。一旦配置了可以向游戏客户端供应 Vivox 访问令牌的安全服务器，就应关闭测试模式。禁用测试模式将从项目中移除令牌签名密钥。

以下示例显示了未使用 AuthenticationService 包但启用了**测试模式**的初始化设置：

```cs
async void InitializeAsync()
{
    await UnityServices.InitializeAsync();
    await VivoxService.Instance.InitializeAsync();
}
```

### 使用 Vivox 开发者门户凭据##using-vivox-developer-portal-credentials

如果您有旧版 Vivox 开发者门户凭据，请通过 `InitializationOptions` 向 [`VivoxService.Instance.InitializeAsync`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_InitializeAsync_Unity_Services_Vivox_VivoxConfigurationOptions_) 提供这些凭据。这些凭据可以包含用于测试目的的签名密钥。

以下示例显示了使用 Vivox 开发者门户凭据的初始化设置：

```cs
async void InitializeAsync()
{
    InitializationOptions options = new InitalizationOptions();
    // _key can be excluded from SetVivoxCredentials if an implementation of IVivoxTokenProvider has been provided to the SDK.
    options.SetVivoxCredentials(_server, _domain, _issuer, _key);
    await UnityServices.InitializeAsync(options);
    await VivoxService.Instance.InitializeAsync();
}
```

> **Note:**
>
> 在发布应用程序之前，请从 `options.SetVivoxCredentials(...)` 调用中移除签名密钥（上述示例中的“\_key”）。此处还假设已经预配了能够向游戏客户端供应 Vivox 访问令牌的服务器。

## 登录生命周期更改##login-lifecycle-changes

`ILoginSession.BeginLogin` 已替换为 [`VivoxService.Instance.LoginAsync(LoginOptions options = null)`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_LoginAsync_Unity_Services_Vivox_LoginOptions_) 用于登录 Vivox 服务。

`LoginOptions` 包含以下各项的属性：

* DisplayName
* 启用文本转语音
* 加入新频道时禁用自动频道传输交换
* 缓存登录时要为当前玩家屏蔽（交叉静音）的 PlayerID 列表
* 设置 SDK 接收参与者更新事件的速率

```cs
 async void LoginToVivoxAsync(List<string> blockedList, string displayName)
{
    LoginOptions options = new LoginOptions();
    options.DisplayName = displayName;
    options.BlockedUserList = blockedList;
    await VivoxService.Instance.LoginAsync(options);
}
```

`ILoginSession.Logout()` 已替换为 [`VivoxService.Instance.LogoutAsync`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_LogoutAsync) 用于注销 Vivox 服务。

将任何绑定到 `LoginSession.PropertyChanged` 事件的逻辑（其 PropertyName 为“State”）和 `LoginState.LoggedIn` 或 `LoginState.LoggedOut` 都移动到其他函数。拆分 login 和 logout 的逻辑以分别在 [`VivoxService.Instance.LoggedIn`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_LoggedIn) 和 [`VivoxService.Instance.LoggedOut`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_LoggedOut) 上触发，如以下示例所示。

```cs
void Start()
{
    VivoxService.Instance.LoggedIn += OnUserLoggedIn;
    VivoxService.Instance.LoggedOut += OnUserLoggedOut;
}

void OnDestroy()
{
    //Cleanup events if the GameObject is destroyed to prevent unexpected behavior.
    VivoxService.Instance.LoggedIn -= OnUserLoggedIn;
    VivoxService.Instance.LoggedOut -= OnUserLoggedOut;
}

void OnUserLoggedIn()
{
    Debug.Log("The user has successfully logged in");
    // Other logic such as attempting a default channel join or changing the state of voice UIs to indicate the character is logged in can be done here.
}


void OnUserLoggedOut()
{
    Debug.Log("The user has logged out");
    // The LoggedOut event will fire after a VivoxService.Instance.LogoutAsync call has been completed successfully
    // or after an internet disruption has caused Automatic Connection Recovery to declare the attempted recovery a failure.
}
```

## 屏蔽玩家##blocking-players

`ILoginSession.SetCrossMutedCommunications` 已被 [`VivoxServince.Instance.BlockPlayerAsync(string playerId)`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_BlockPlayerAsync_System_String_) 和 [`VivoxService.Instance.UnblockPlayerAsync(string playerId)`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_UnblockPlayerAsync_System_String_) 取代，后两者现在需要 `playerId` 而不是 `AccountId` 作为输入。

## 频道生命周期更改##channel-lifecycle-changes

### 加入和离开频道##joining-and-leaving-channels

所有与 `ChannelSession.BeginConnect` 相关的逻辑都已替换为以下方法：

* [`JoinGroupChannelAsync(string channelName, ChatCapability chatCapability, ChannelOptions channelOptions = null);`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_JoinGroupChannelAsync_System_String_Unity_Services_Vivox_ChatCapability_Unity_Services_Vivox_ChannelOptions_)
* [`JoinPositionalChannelAsync(string channelName, ChatCapability chatCapability, Channel3DProperties positionalChannelProperties, ChannelOptions channelOptions = null);`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_JoinPositionalChannelAsync_System_String_Unity_Services_Vivox_ChatCapability_Unity_Services_Vivox_Channel3DProperties_Unity_Services_Vivox_ChannelOptions_)
* [`JoinEchoChannelAsync(string channelName, ChatCapability chatCapability, ChannelOptions channelOptions = null);`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_JoinEchoChannelAsync_System_String_Unity_Services_Vivox_ChatCapability_Unity_Services_Vivox_ChannelOptions_)

> **Note:**
>
> 根据频道通信能力，将 ChatCapability 设置为 Text、Audio 或 TextandAudio。

`ChannelSession.Disconnect()` 已替换为：

* [`VivoxService.Instance.LeaveChannelAsync(string channelName)`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_LeaveChannelAsync_System_String_)，用于离开特定频道。
* [`VivoxService.Instance.LeaveAllChannelsAsync()`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_LeaveAllChannelsAsync)，用于离开所有活动频道。如果任何时候只加入一个频道，我们建议使用此方法。

`ILoginSession.ChannelSessions` 功能被替换为 [`VivoxService.Instance.ActiveChannels`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_ActiveChannels)。

`VivoxService.Instance.ActiveChannels` 是一个字典，其中所有当前活动频道作为键，所有频道的列表 `VivoxParticipants` 作为值。

以前在实现中依赖 `AudioState` 和 `TextState` 来确定用户是否已连接到频道。现在已将跟踪这两个属性的状态更改的事件替换为与 `VivoxService.Instance.ChannelJoined` 和 `VivoxService.Instance.ChannelLeft` 相关的函数。

[`VivoxService.Instance.ChannelJoined`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_ChannelJoined) 和 [`VivoxService.Instance.ChannelLeft`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_ChannelLeft) 将向绑定到事件的任何回调提供加入或离开的频道的频道名称。

以下代码示例是这些更改的示例：

```cs
void BindToChannelActions()
{
    //Connecting/Disconnecting channel actions can be done upon VivoxService.Instance.LoggedIn and LoggedOut events being fired, respectively,
    //for basic lifecycle control
    VivoxService.Instance.ChannelJoined += OnChannelJoined;
    VivoxService.Instance.ChannelLeft += OnChannelLeft;
}

void UnbindChannelActions()
{
    VivoxService.Instance.ChannelJoined -= OnChannelJoined;
    VivoxService.Instance.ChannelLeft -= OnChannelLeft;
}

void OnChannelJoined(string channelName)
{
    //The channel with name channelName has been successfully joined, and any UI updates should be done for it
    //Additionally, if multiple channels are being handled, keeping track of the channelName and knowing which channel is for which purpose should be done
    //on the side of the implementation based on this event.
}

void OnChannelLeft(string channelName)
{
    //The channel with name channelName has been successfully left, and any UI updates should be done for it
    //This also means that the channelName can likely be safely lost, although if the channelName is specific and might be rejoined,
    //it should be held on to by the implementation.
}
```

## 频道传输##channel-transmission

默认情况下，频道传输（语音进入的频道）切换到最近加入的频道。若要禁用此功能，可在使用该 LoginOptions 对象作为输入调用 `VivoxService.Instance.LoginAsync` 之前将 `LoginOptions.DisableAutomaticChannelTransmissionSwap` 设置为 **true**。

可以使用 `VivoxService.Instance.SetChannelTransmissionModeAsync(TransmissionMode transmissionMode, string channelName = null)` 方法手动更改频道传输模式。

此 API 可以根据该方法的输入，将传输频道设置为特定频道、所有频道或无任何传输频道。

## 频道参与者管理##channel-participant-management

VivoxParticipant 已取代 ChannelParticipant。

如果使用了正确的键（频道名称），可以通过从 `VivoxService.Instance.ActiveChannels` 索引中获取值来访问每个活动频道的 VivoxParticipant 集合。

使用 [`VivoxService.Instance.ParticipantAddedToChannel`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_ParticipantAddedToChannel) 和 [`VivoxServince.Instance.ParticipantRemovedFromChannel`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_ParticipantRemovedFromChannel) 可以了解何时在频道中添加或移除了 VivoxParticipant。

这些事件将提供相关的 VivoxParticipant 作为绑定到这些事件的任何回调的输入。VivoxParticipant 对象包含参与者的 PlayerId、参与者加入或离开的频道名称以及其他有价值的信息，包括参与者是否为本地玩家或玩家的显示名称。以下示例显示了这些类型的事件：

```cs
void BindToParticipantEvents()
{
    //These events can be connected at the same time as the channel events from the previous sections for convenience purposes
    VivoxService.Instance.ParticipantAddedToChannel += OnParticipantAdded;
    VivoxService.Instance.ParticipantRemovedFromChannel += OnParticipantRemoved;
}

void UnbindParticipantEvents()
{
    VivoxService.Instance.ParticipantAddedToChannel -= OnParticipantAdded;
    VivoxService.Instance.ParticipantRemovedFromChannel -=OnParticipantRemoved;
}

void OnParticipantAdded(VivoxParticipant participant)
{
    // Use VivoxParticipant to establish some UI element tied to that specific VivoxParticipant, which can then be used to control things such as muting
    // volume, and blocking, along with UI that can track certain states - such as whether the player is currently muted or is currently speaking.
}


void OnParticipantRemoved(VivoxParticipant participant)
{
    // At this point, any UI created to display information about this VivoxParticipant can be safely disposed of.
}
```

## VivoxParticipant 对象##vivoxparticipant-objects

[`VivoxParticipant`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.VivoxParticipant.html) 包含有关参与者当前状态的信息，例如：

* `VivoxParticipant.IsSelf`，指明参与者是否代表本地玩家。
* `VivoxParticipant.SpeechDetected`，指明参与者的音频能量是否超过了 Vivox SDK 将音频视为语音的阈值。

VivoxParticipant 还包含以下事件：

* `ParticipantMuteStateChanged`
* `ParticipantSpeechDetected`
* `ParticipantAudioEnergyChanged`

这些事件可用于创建功能音量单位 (VU) 计量表或语音指示器。请在每个参与者的单个 UI 级别连接这些事件。

> **Note:**
>
> 这些事件所属的对象应具有对所表示的参与者的引用，以便轻松更新 UI。

以下示例类是 ChatChannelSample 中的 RosterItem 的简化版本：

```cs
public class RosterItem : MonoBehaviour
{
    public VivoxParticipant Participant;
    public Text PlayerNameText;


    public void SetupRosterItem(VivoxParticipant participant)
    {
        Participant = participant;
        PlayerNameText.text = Participant.DisplayName;
        Participant.ParticipantMuteStateChanged += OnMuteStateChanged;
        Participant.ParticipantSpeechDetected += OnSpeechDetected;
    }

    void OnMuteStateChanged()
    {
        if(Participant.IsMuted)
        {
            //Participant has been muted
        }
        else
        {
            //Participant has been unmuted
        }
    }

    void OnSpeechDetected()
    {
        //Participant AudioEnergy has exceeded the value required to be considered speech
    }
}
```

## 将频道参与者静音##muting-channel-participants

[`VivoxParticipant.MutePlayerLocally()`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.VivoxParticipant.html#Unity_Services_Vivox_VivoxParticipant_MutePlayerLocally) 和 [`VivoxParticipant.UnmutePlayerLocally()`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.VivoxParticipant.html#Unity_Services_Vivox_VivoxParticipant_UnmutePlayerLocally) 可用于将频道中的特定参与者静音/取消静音。

> **Note:**
>
> 这些 VivoxParticipant 静音/取消静音 API 不会影响本地玩家。

## 音频设备管理##audio-device-management

设备管理不再通过利用 Client 对象来实现。现在可以进行任何设备调整，例如静音、更改音量或使用 `VivoxService.Instance` 切换活动设备。

## 将本地玩家静音##muting-the-local-player

本地玩家静音已更改为使用 [`VivoxService.Instance.MuteInputDevice()`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_MuteInputDevice) 和 [`VivoxService.Instance.UnmuteInputDevice()`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_UnmuteInputDevice) 函数，而不必通过 IAudioDevice 对象上的 API 或将频道传输切换为 none 来实现此目的。这些 API 会将本地用户的输入设备静音或取消静音。

## 调节输入/输出设备音量##adjusting-input/output-device-volume

现在可以使用 [`VivoxService.Instance.SetInputDeviceVolume(int value)`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_SetInputDeviceVolume_System_Int32_) 和 [`VivoxService.Instance.SetOutputDeviceVolume(int value)`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_SetOutputDeviceVolume_System_Int32_) 来调整设备音量。

## 更改活动输入/输出设备##changing-the-active-input/output-device

可以通过调用 [`VivoxService.Instance.SetActiveOutputDeviceAsync(VivoxOutputDevice device)`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_SetActiveOutputDeviceAsync_Unity_Services_Vivox_VivoxOutputDevice_) 或 [`VivoxService.Instance.SetActiveInputDeviceAsync(VivoxInputDevice device)`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_SetActiveInputDeviceAsync_Unity_Services_Vivox_VivoxInputDevice_) 来调整活动输入/输出设备。

可以通过访问属性 [`VivoxService.Instance.AvailableInputDevices`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_AvailableInputDevices) 和 [`VivoxService.Instance.AvailableOututDevices`](https://docs.unity3d.com/Packages/com.unity.services.vivox@latest/index.html?subfolder=/api/Unity.Services.Vivox.IVivoxService.html#Unity_Services_Vivox_IVivoxService_AvailableOutputDevices) 来获取 `VivoxInputDevices` 和 `VivoxOutputDevices` 的集合。

以下示例演示如何使用 Vivox SDK 音频设备 API 来填充输入设备的下拉列表，而该列表可用于更改活动输入设备。

```cs
using System.Linq;
using Unity.Services.Vivox;
using UnityEngine;
using UnityEngine.UI;


public class AudioDeviceSettings : MonoBehaviour
{
    public Dropdown InputDeviceDropdown;

    private void Start()
    {
        VivoxService.Instance.AvailableInputDevicesChanged += RefreshInputDeviceList;
        InputDeviceDropdown.onValueChanged.AddListener((i) =>
        {
            InputDeviceValueChanged(i);
        });
    }


    void OnEnable()
    {
        RefreshInputDeviceList();
    }


    void OnDestroy()
    {
        // Unbind all UI actions
        InputDeviceDropdown.onValueChanged.RemoveAllListeners();
        VivoxService.Instance.AvailableInputDevicesChanged -= RefreshInputDeviceList;
    }

    private void RefreshInputDeviceList()
    {
        InputDeviceDropdown.Hide();
        InputDeviceDropdown.ClearOptions();
        InputDeviceDropdown.options.AddRange(VivoxService.Instance.AvailableInputDevices.Select(v => new Dropdown.OptionData() { text = v.DeviceName }));
        InputDeviceDropdown.SetValueWithoutNotify(InputDeviceDropdown.options.FindIndex(option => option.text == VivoxService.Instance.ActiveInputDevice.DeviceName));
        InputDeviceDropdown.RefreshShownValue();
    }

    void InputDeviceValueChanged(int index)
    {
        VivoxService.Instance.SetActiveInputDeviceAsync(VivoxService.Instance.AvailableInputDevices.Where(device => device.DeviceName == InputDeviceDropdown.options[index].text).First());
    }
}
```

> **Note:**
>
> 这是 ChatChannelSample 示例中使用的 AudioDeviceSettings 类的简化版本，演示了如何更改活动输入/输出设备、调节输入/输出设备音量以及对设备列表更改做出反应。
