기술 자료

지원

Vivox Unity SDK

Vivox Unity SDK

참여 관리

Manage participant events when users join or leave channels.
읽는 시간 2분최근 업데이트: 19일 전

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(Vivox가 스피치로 간주할 정도로 플레이어의 AudioEnergy가 증가했는지 여부)
VivoxParticipant.ParticipantMuteStateChanged
VivoxParticipant.ParticipantSpeechDetected
와 함께 참가자의 UI 표현과 긴밀하게 연결되어야 하며, VivoxParticipant는 로컬 플레이어에게 음소거된 참가자가 있는지 또는 참가자가 현재 채널에서 말하고 있는지 여부를 전달하는 데 사용됩니다.
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(); }}