浏览代码

Adding the automatic en/disabling of the volume controls based on whether the player is actually able to send voice (i.e. do they have a device available, are they connected to the channel). Adding logic to display a mic icon for the local user instead of the volume control, so they just have the mute toggle. It still needs a mic icon.

/main/staging/vivox_package_integration
nathaniel.buck@unity3d.com 3 年前
当前提交
fb9919c6
共有 6 个文件被更改,包括 221 次插入70 次删除
  1. 103
      Assets/Prefabs/UI/UserCardPanel.prefab
  2. 48
      Assets/Scripts/UI/LobbyUserVolumeUI.cs
  3. 1
      Assets/Scripts/Vivox/VivoxSetup.cs
  4. 97
      Assets/Scripts/Vivox/VivoxUserHandler.cs
  5. 11
      Assets/Scripts/UI/MuteUI.cs.meta
  6. 31
      Assets/Scripts/UI/MuteUI.cs

103
Assets/Prefabs/UI/UserCardPanel.prefab


m_Script: {fileID: 11500000, guid: 78d292f3bd9f1614cb744dcb4fe3ac12, type: 3}
m_Name:
m_EditorClassIdentifier:
m_MuteUI: {fileID: 5371667691979465528}
m_lobbyUserVolumeUI: {fileID: 7750493951695561362}
--- !u!1 &3010031715874034773
GameObject:

- component: {fileID: 3081577339344251787}
- component: {fileID: 7240929418107909207}
- component: {fileID: 5467963549174563591}
- component: {fileID: 5371667691979465528}
- component: {fileID: 6569968321909521185}
- component: {fileID: 5564390157418853622}
m_Layer: 5
m_Name: MuteArea

m_FlexibleWidth: -1
m_FlexibleHeight: -1
m_LayoutPriority: 3
--- !u!114 &5371667691979465528
--- !u!114 &6569968321909521185
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}

m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f722d3f4131ab52469d430cc5855ee83, type: 3}
m_Script: {fileID: 11500000, guid: ab355d5994635724dbde297a055fb586, type: 3}
m_voiceRings: {fileID: 1287953984161108202}
--- !u!225 &5564390157418853622
CanvasGroup:
m_ObjectHideFlags: 0

m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3518057056990530202}
m_CullTransparentMesh: 1
--- !u!1 &3906605741203742200
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 853418489715471536}
- component: {fileID: 5399015282885967859}
- component: {fileID: 6714245214549863763}
m_Layer: 5
m_Name: UnmutedIcon_Mic
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &853418489715471536
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3906605741203742200}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 4798292317257423901}
m_RootOrder: 1
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5399015282885967859
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3906605741203742200}
m_CullTransparentMesh: 1
--- !u!114 &6714245214549863763
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 3906605741203742200}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0.43185723, g: 0.7924528, b: 0.24296904, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &4250551697308330837
GameObject:
m_ObjectHideFlags: 0

m_LocalScale: {x: 1, y: 1, z: 1}
m_Children:
- {fileID: 1122323841334202136}
- {fileID: 853418489715471536}
- {fileID: 4530763983275839173}
m_Father: {fileID: 5144918951328900891}
m_RootOrder: 0

- component: {fileID: 478472562358024413}
- component: {fileID: 1918995816743806069}
- component: {fileID: 7750493951695561362}
- component: {fileID: 6299655602247130973}
- component: {fileID: 5190888420783209525}
m_Layer: 5
m_Name: VolumeArea

m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a06ec4a4943ad514da7a222eacb66528, type: 3}
m_Name:
m_EditorClassIdentifier:
m_volumeSliderContainer: {fileID: 6299655602247130973}
m_muteButtonContainer: {fileID: 6569968321909521185}
m_muteIcon: {fileID: 41255065417622773}
m_micIcon: {fileID: 3906605741203742200}
--- !u!114 &6299655602247130973
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4809966060551735762}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ab355d5994635724dbde297a055fb586, type: 3}
m_Name:
m_EditorClassIdentifier:
m_onVisibilityChange:

m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 4798292317257423901}
m_RootOrder: 1
m_RootOrder: 2
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}

48
Assets/Scripts/UI/LobbyUserVolumeUI.cs


namespace LobbyRelaySample.UI
{
// TODO: Necessary?
public class LobbyUserVolumeUI : UIPanelBase
public class LobbyUserVolumeUI : MonoBehaviour
[SerializeField]
private UIPanelBase m_volumeSliderContainer;
[SerializeField]
private UIPanelBase m_muteButtonContainer;
[SerializeField]
[Tooltip("This is shown for other players, to mute them.")]
private GameObject m_muteIcon;
[SerializeField]
[Tooltip("This is shown for the local player, to make it clearer that they are muting themselves.")]
private GameObject m_micIcon;
public bool IsLocalPlayer { private get; set; }
Show();
if (IsLocalPlayer)
{
m_volumeSliderContainer.Hide(0);
m_muteButtonContainer.Show();
m_muteIcon.SetActive(false);
m_micIcon.SetActive(true);
}
else
{
m_volumeSliderContainer.Show();
m_muteButtonContainer.Show();
m_muteIcon.SetActive(true);
m_micIcon.SetActive(false);
}
Hide();
m_volumeSliderContainer.Hide(0.4f);
m_muteButtonContainer.Hide(0.4f);
m_muteIcon.SetActive(true);
m_micIcon.SetActive(false);
}
/* TODO : If we can hook in the volume from a user, we can plug it in here.
/// <summary>
/// Controls the visibility of the volume rings to show activity levels of the voice channel on this user.
/// </summary>
/// <param name="normalizedVolume"></param>
public void OnSoundDetected(float normalizedVolume)
{
m_voiceRings.alpha = normalizedVolume;
}
*/
}
}

1
Assets/Scripts/Vivox/VivoxSetup.cs


m_loginSession.BeginLogin(m_loginSession.GetLoginToken(), SubscriptionMode.Accept, null, null, null, result =>
{
// TODO: Is BeginLogin guaranteed to call this callback?
try
{
m_loginSession.EndLogin(result);

97
Assets/Scripts/Vivox/VivoxUserHandler.cs


public class VivoxUserHandler : MonoBehaviour
{
[SerializeField]
private UI.MuteUI m_MuteUI;
[SerializeField]
private UI.LobbyUserVolumeUI m_lobbyUserVolumeUI;
private IChannelSession m_channelSession;

private const int k_volumeMin = -50, k_volumeMax = 20; // From the Vivox docs, the valid range is [-50, 50] but anything above 25 risks being painfully loud.
public void Start()
{
m_lobbyUserVolumeUI.DisableVoice();
}
Account account = new Account(id);
// Account account = new Account(id);
}
private void FindVivoxId()
{
if (m_vivoxId != null || m_channelSession == null)
return;
foreach (var participant in m_channelSession.Participants)
// SetID might be called after we've received the IChannelSession for remote players, which would mean after OnParticipant Added. So, duplicate the VivoxID work here.
if (m_channelSession != null)
if (!participant.Key.Contains(m_id))
continue;
m_vivoxId = participant.Key;
return;
foreach (var participant in m_channelSession.Participants)
{
if (m_id == participant.Account.DisplayName)
{
m_vivoxId = participant.Key;
m_lobbyUserVolumeUI.IsLocalPlayer = participant.IsSelf;
m_lobbyUserVolumeUI.EnableVoice();
break;
}
}
}
}

m_channelSession.Participants.AfterKeyAdded += OnParticipantAdded;
m_channelSession.Participants.BeforeKeyRemoved += BeforeParticipantRemoved;
m_channelSession.Participants.AfterValueUpdated += OnParticipantValueUpdated;
m_channelSession.Participants.AfterKeyAdded -= OnParticipantAdded;
m_channelSession.Participants.BeforeKeyRemoved -= BeforeParticipantRemoved;
m_channelSession.Participants.AfterValueUpdated -= OnParticipantValueUpdated;
/// <summary>
/// To be called whenever a new Participant is added to the channel, using the events from Vivox's custom dictionary.
/// </summary>
private void OnParticipantAdded(object sender, KeyEventArg<string> keyEventArg)
{
var source = (VivoxUnity.IReadOnlyDictionary<string, IParticipant>)sender;
var participant = source[keyEventArg.Key];
var username = participant.Account.DisplayName;
bool isThisUser = username == m_id;
if (isThisUser)
{ m_vivoxId = keyEventArg.Key; // Since we couldn't construct the Vivox ID earlier, retrieve it here.
m_lobbyUserVolumeUI.IsLocalPlayer = participant.IsSelf;
m_lobbyUserVolumeUI.EnableVoice();
}
}
private void BeforeParticipantRemoved(object sender, KeyEventArg<string> keyEventArg)
{
var source = (VivoxUnity.IReadOnlyDictionary<string, IParticipant>)sender;
var participant = source[keyEventArg.Key];
var username = participant.Account.DisplayName;
bool isThisUser = username == m_id;
if (isThisUser)
{ m_lobbyUserVolumeUI.DisableVoice();
}
}
private void OnParticipantValueUpdated(object sender, ValueEventArg<string, IParticipant> valueEventArg)
{
var source = (VivoxUnity.IReadOnlyDictionary<string, IParticipant>)sender;
var participant = source[valueEventArg.Key];
var username = participant.Account.DisplayName;
string property = valueEventArg.PropertyName;
if (username == m_id)
{
if (property == "UnavailableCaptureDevice")
{
if (participant.UnavailableCaptureDevice)
{ m_lobbyUserVolumeUI.DisableVoice();
participant.SetIsMuteForAll(m_vivoxId, true, null); // Note: If you add more places where a player might be globally muted, a state machine might be required for accurate logic.
}
else
{ m_lobbyUserVolumeUI.EnableVoice();
participant.SetIsMuteForAll(m_vivoxId, false, null);
}
}
else if (property == "IsMutedForAll")
{
if (participant.IsMutedForAll)
m_lobbyUserVolumeUI.DisableVoice();
else
m_lobbyUserVolumeUI.EnableVoice();
}
}
}
FindVivoxId();
if (m_channelSession == null || m_vivoxId == null) // Verify initialization, since SetId and OnChannelJoined are called at different times for local vs. remote clients.
return;

public void OnMuteToggle(bool isMuted)
{
FindVivoxId();
if (m_channelSession == null || m_vivoxId == null)
return;

11
Assets/Scripts/UI/MuteUI.cs.meta


fileFormatVersion: 2
guid: f722d3f4131ab52469d430cc5855ee83
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

31
Assets/Scripts/UI/MuteUI.cs


using UnityEngine;
namespace LobbyRelaySample.UI
{
public class MuteUI : UIPanelBase
{
[SerializeField]
CanvasGroup m_voiceRings;
public void EnableVoice()
{
Show();
}
public void DisableVoice()
{
Hide(0.4f);
}
/* TODO : If we can hook in the volume from a user, we can plug it in here.
/// <summary>
/// Controls the visibility of the volume rings to show activity levels of the voice channel on this user.
/// </summary>
/// <param name="normalizedVolume"></param>
public void OnSoundDetected(float normalizedVolume)
{
m_voiceRings.alpha = normalizedVolume;
}
*/
}
}
正在加载...
取消
保存