Metaverse WebGL build
Overview
The Metaverse sample can be build for a WebGL target.
Due to some Unity limitations regarding WebGL builds though, a few point need specific care to work properly and are detailed below.
Note that this WebGL build does not support WebXR (virtual reality in the browser): while it is achievable with some open source libraries, like unity-webxr-export, it is not yet supported by default in Unity, and thus not demonstrated here.
You can test Metaverse WebGL build (Fusion 1) here.
Asynchronous code: Task.Delay
On WebGL builds, some asnyc/await methods do not work as intended, leading to problems.
Mainly, the System.Threading.Tasks.Task.Delay
method won't work on WebGL builds.
To bypass this limitation, the Metaverse sample provides a WebGL compatible AsyncTask.Task
method.
It relies on Task.Yield
which is compatible for WebGL builds.
C#
public static class AsyncTask
{
public static async Task Delay(int milliseconds)
{
#if !UNITY_WebGL
await Task.Delay(milliseconds);
#else
// Unity 2021 do NOT support Task.Delay() in WebGL
float startTime = Time.realtimeSinceStartup;
float delay = (float)milliseconds / 1000f;
while (Time.realtimeSinceStartup - startTime < delay)
{
// Wait for the delay time to pass
await Task.Yield();
}
#endif
}
}
AudioSource and Lip synchronization
In current Unity version, for WebGL builds, Audiosource
components do not trigger the OnAudioFilterRead
callback on sibling components.
Hence, the VoiceDetection
component that compute the voice volume using this callback's data cannot work properly.
Instead, in WebGL contexts, the VoiceDetection
uses instead PhotonVoice callbacks to access raw audio data.
On the user networked prefab, the headset holds a Speaker
component, a part of PhotonVoice, which has a RemoveVoice
field.
Here, it is possible to subscribe to FloatFrameDecoded
, to be warned when audio data are received.
Once the speaker is initialized (IsLinked
returns true), the OnFloatFrameDecoded
callback can be added:
C#
if (speaker == null) speaker = audioSource.GetComponent<Speaker>();
if(HasSubscribedToVoice == false && speaker != null && speaker.IsLinked)
{
speaker.RemoteVoice.FloatFrameDecoded += OnFloatFrameDecoded;
HasSubscribedToVoice = true;
}
From there, the callback will provide all received audio data, allowing to compute an average voice level, to display lip synchronization accordingly:
C#
private void OnFloatFrameDecoded(FrameOut<float> frame)
{
float writeVoiceVolume = 0f;
foreach (var sample in frame.Buf)
{
writeVoiceVolume += Mathf.Abs(sample);
}
writeVoiceVolume /= frame.Buf.Length;
// audioWriteCalls and accumulatedVolume are reset during Update
audioWriteCalls++;
accumulatedVolume += writeVoiceVolume;
voiceVolume = accumulatedVolume / audioWriteCalls;
}
Note that this adaptation is used for the simple avatar model and ReadyPlayerMe avatars.
Regarding ReadyPlayerMe avatars, while on desktop and Quest builds, they rely on Oculus Lipsync, the underlying library was not available for WebGL builds.
So, a simplified version is used here : the default RPMLipSync
code provided by ReadyPlayerMe is adaptated to use PhotonVoice callbacks.
TextMeshPro materials
In some circumstances, TextMeshPro texts are not visible if a scene contains both TextMeshPro
and TextMeshProUGUI
components using the exact same font material.
To fix this issue, the material associated to a font must be duplicated.
Then, make sure that TextMeshPro
and TextMeshProUGUI
components in your scene do not use the same Material Preset
.