Files
RoomAwareVR/Assets/VRMPAssets/Scripts/Player/OfflinePlayerAvatar.cs
T
2025-05-26 00:46:28 +02:00

203 lines
5.7 KiB
C#

using Unity.XR.CoreUtils;
using Unity.XR.CoreUtils.Bindings.Variables;
using UnityEngine;
using UnityEngine.Android;
namespace XRMultiplayer
{
/// <summary>
/// Represents the offline player avatar.
/// </summary>
public class OfflinePlayerAvatar : MonoBehaviour
{
public static BindableVariable<float> voiceAmp = new BindableVariable<float>();
/// <summary>
/// Gets or sets a value indicating whether the player is muted.
/// </summary>
public static bool muted
{
get => s_Muted;
set
{
if (Permission.HasUserAuthorizedPermission(Permission.Microphone))
s_Muted = value;
}
}
/// <summary>
/// A value indicating whether the player is muted.
/// </summary>
static bool s_Muted;
/// <summary>
/// The head transform.
/// </summary>
[SerializeField] Transform m_HeadTransform;
/// <summary>
/// The head renderer.
/// </summary>
[SerializeField] SkinnedMeshRenderer m_HeadRend;
/// <summary>
/// The voice amplitude curve.
/// </summary>
[SerializeField] AnimationCurve m_VoiceCurve;
/// <summary>
/// The head origin.
/// </summary>
Transform m_HeadOrigin;
/// <summary>
/// The mouth blend smoothing.
/// </summary>
[SerializeField] float m_MouthBlendSmoothing = 5.0f;
/// <summary>
/// The microphone loudness.
/// </summary>
float m_MicLoudness;
/// <summary>
/// The microphone device name.
/// </summary>
string m_Device;
/// <summary>
/// The sample window.
/// </summary>
int m_SampleWindow = 128;
/// <summary>
/// The clip record.
/// </summary>
AudioClip m_ClipRecord;
/// <summary>
/// The voice destination volume.
/// </summary>
float m_VoiceDestinationVolume;
bool m_MicInitialized = false;
/// <inheritdoc/>
void Start()
{
XROrigin rig = FindFirstObjectByType<XROrigin>();
m_HeadOrigin = rig.Camera.transform;
}
void OnEnable()
{
XRINetworkGameManager.LocalPlayerColor.Subscribe(UpdatePlayerColor);
VoiceChatManager.s_HasMicrophonePermission.Subscribe(MicrophonePermissionGranted);
XRINetworkGameManager.Connected.Subscribe(connected =>
{
gameObject.SetActive(!connected);
});
}
void OnDisable()
{
XRINetworkGameManager.LocalPlayerColor.Unsubscribe(UpdatePlayerColor);
VoiceChatManager.s_HasMicrophonePermission.Subscribe(MicrophonePermissionGranted);
StopMicrophone();
XRINetworkGameManager.Connected.Unsubscribe(connected =>
{
gameObject.SetActive(!connected);
});
}
/// <inheritdoc/>
private void LateUpdate()
{
m_HeadTransform.SetPositionAndRotation(m_HeadOrigin.position, m_HeadOrigin.rotation);
}
/// <inheritdoc/>
void Update()
{
if (!s_Muted)
{
m_MicLoudness = LevelMax();
m_VoiceDestinationVolume = Mathf.Clamp01(Mathf.Lerp(m_VoiceDestinationVolume, m_MicLoudness, Time.deltaTime * m_MouthBlendSmoothing));
float appliedCurve = m_VoiceCurve.Evaluate(m_VoiceDestinationVolume);
voiceAmp.Value = appliedCurve;
m_HeadRend.SetBlendShapeWeight(0, 100 - appliedCurve * 100);
}
else
{
voiceAmp.Value = 0.0f;
}
}
void MicrophonePermissionGranted(bool granted)
{
if (granted)
{
InitMic();
}
}
void UpdatePlayerColor(Color color)
{
m_HeadRend.materials[2].color = color;
}
/// <summary>
/// Initializes the microphone, called from <see cref="VoiceChatManager.s_HasMicrophonePermission" callback/>.
/// </summary>
void InitMic()
{
m_MicInitialized = true;
m_Device ??= Microphone.devices[0];
m_ClipRecord = Microphone.Start(m_Device, true, 999, 44100);
}
/// <summary>
/// Stops the microphone.
/// </summary>
void StopMicrophone()
{
m_MicInitialized = false;
if (Permission.HasUserAuthorizedPermission(Permission.Microphone))
{
Microphone.End(m_Device);
}
else
{
s_Muted = true;
}
}
/// <summary>
/// Gets the maximum level of the microphone input.
/// </summary>
/// <returns>The maximum level of the microphone input.</returns>
float LevelMax()
{
if (!m_MicInitialized) return 0;
float levelMax = 0;
float[] waveData = new float[m_SampleWindow];
int micPosition = Microphone.GetPosition(null) - (m_SampleWindow + 1); // null means the first microphone
if (micPosition < 0) return 0;
m_ClipRecord.GetData(waveData, micPosition);
// Getting a peak on the last 128 samples
for (int i = 0; i < m_SampleWindow; i++)
{
float wavePeak = waveData[i] * waveData[i];
if (levelMax < wavePeak)
{
levelMax = wavePeak;
}
}
return levelMax;
}
}
}