文档

支持

Vivox Unity SDK

Vivox Unity SDK

参与者管理

Manage participant events when users join or leave channels.
阅读时间2 分钟最后更新于 13 天前

Vivox SDK 会发布有关频道中各个参与者的信息,这些信息对所有其他参与者可见。其中包括以下信息:
  • 用户加入频道的时间。
  • 用户离开频道的时间。
  • 用户状态发生重要变化的时间,如用户是否在说话或键入。
处理参与者事件是可选的。如果没有用户状态可视化(例如,显示启用了语音的用户),则游戏可以忽略这些事件。 若要提供用户状态信息的可视化,游戏必须处理以下消息:
  • VivoxService.Instance.ParticipantAddedToChannel
  • VivoxService.Instance.ParticipantRemovedFromChannel
  • VivoxParticipant.ParticipantMuteStateChanged
  • VivoxParticipant.ParticipantSpeechDetected
  • VivoxParticipant.ParticipantAudioEnergyChanged

VivoxParticipant

ParticipantAddedToChannel
ParticipantRemovedFromChannel
都带有 VivoxParticipant。VivoxParticipant 包含有关刚刚添加的参与者的信息,例如:
  • PlayerId
  • DisplayName
  • VivoxParticipant 所属的频道的 ChannelName
  • VivoxParticipant 是否为 IsSelf(表示频道内本地玩家的参与者)
VivoxParticipant 还包含该参与者的当前状态,包括:
  • IsMuted 状态
  • AudioEnergy
  • SpeechDetected(玩家的 AudioEnergy 是否已增加到 Vivox 认为是语音的程度)
VivoxParticipants 应该与参与者的 UI 表示形式紧密耦合,使用
VivoxParticipant.ParticipantMuteStateChanged
VivoxParticipant.ParticipantSpeechDetected
来表达本地玩家是否已将参与者静音,或者参与者当前是否正在频道中讲话。
您可以使用
VivoxParticipant.ParticipantAudioEnergyChanged
创建比 SpeechDetected 更准确的音量单位 (VU) 计量表。
以下代码(这是 Vivox ChatChannelSample 的简化代码段)是这些系统的一个示例:
public class RosterManager : MonoBehaviour{ private const string LobbyChannelName = "lobbyChannel"; private Dictionary<string, List<RosterItem>> rosterObjects = new Dictionary<string, List<RosterItem>>(); public GameObject rosterItemPrefab; private void Start() { VivoxService.Instance.ParticipantAddedToChannel += OnParticipantAdded; VivoxService.Instance.ParticipantRemovedFromChannel += OnParticipantRemoved; } public void ClearAllRosters() { foreach(List<RosterItem> rosterList in rosterObjects.Values) { foreach(RosterItem item in rosterList) { Destroy(item.gameObject); } rosterList.Clear(); } rosterObjects.Clear(); } public void ClearChannelRoster(string channelName) { List<RosterItem> rosterList = rosterObjects[channelName]; foreach(RosterItem item in rosterList) { Destroy(item.gameObject); } rosterList.Clear(); rosterObjects.Remove(channelName); } private void CleanRoster(string channelName) { RectTransform rt = this.gameObject.GetComponent<RectTransform>(); rt.sizeDelta = new Vector2(0, rosterObjects[channelName].Count * 50); } void UpdateParticipantRoster(VivoxParticipant participant, bool isAddParticipant) { if (isAddParticipant) { GameObject newRosterObject = GameObject.Instantiate(rosterItemPrefab, this.gameObject.transform); RosterItem newRosterItem = newRosterObject.GetComponent<RosterItem>(); List<RosterItem> thisChannelList; if (rosterObjects.ContainsKey(participant.ChannelName)) { //Add this object to an existing roster rosterObjects.TryGetValue(participant.ChannelName, out thisChannelList); newRosterItem.SetupRosterItem(participant); thisChannelList.Add(newRosterItem); rosterObjects[participant.ChannelName] = thisChannelList; } else { //Create a new roster to add this object to thisChannelList = new List<RosterItem>(); thisChannelList.Add(newRosterItem); newRosterItem.SetupRosterItem(participant); rosterObjects.Add(participant.ChannelName, thisChannelList); } CleanRoster(participant.ChannelName); } else { if (rosterObjects.ContainsKey(participant.ChannelName)) { RosterItem removedItem = rosterObjects[participant.ChannelName].FirstOrDefault(p => p.Participant.PlayerId == participant.PlayerId); if (removedItem != null) { rosterObjects[participant.ChannelName].Remove(removedItem); Destroy(removedItem.gameObject); CleanRoster(participant.ChannelName); } else { Debug.LogError("Trying to remove a participant that has no roster item."); } } } } void OnParticipantAdded(VivoxParticipant participant) { UpdateParticipantRoster(participant, true); } void OnParticipantRemoved(VivoxParticipant participant) { UpdateParticipantRoster(participant, false); }}public class RosterItem : MonoBehaviour{ // Player specific items. public VivoxParticipant Participant; public Text PlayerNameText; public Image ChatStateImage; public Sprite MutedImage; public Sprite SpeakingImage; public Sprite NotSpeakingImage; Button m_muteButton; private void UpdateChatStateImage() { if (Participant.IsMuted) { ChatStateImage.sprite = MutedImage; } else { if (Participant.SpeechDetected) { ChatStateImage.sprite = SpeakingImage; } else { ChatStateImage.sprite = NotSpeakingImage; } } } public void SetupRosterItem(VivoxParticipant participant) { //Set the Participant variable of this RosterItem to the VivoxParticipant added in the RosterManager Participant = participant; PlayerNameText.text = Participant.DisplayName; // Update the image to the active state of the user (either the SpeakingImage, the MutedImage, or the NotSpeakingImage) and then attach // the function to run if an event is fired denoting a change to that users state UpdateChatStateImage(); Participant.ParticipantMuteStateChanged += UpdateChatStateImage; Participant.ParticipantSpeechDetected += UpdateChatStateImage; //A button on the UI element itself is implemented to handle muting on the participant represented by the UI element m_muteButton = gameObject.GetComponent<Button>(); m_muteButton.onClick.AddListener(() => { // If already muted, unmute, and vice versa. if (Participant.IsMuted) { Participant.UnmutePlayerLocally(); } else { Participant.MutePlayerLocally(); } }); } void OnDestroy() { Participant.ParticipantMuteStateChanged -= UpdateChatStateImage; Participant.ParticipantSpeechDetected -= UpdateChatStateImage; m_muteButton.onClick.RemoveAllListeners(); }}