動画プレイヤーを作成するサンプルの解説
Udonスクリプト作成
サンプルのUdonSyncPlayerをU#に書き直し
Udon Graph
デカすぎるので省略。unity上で確認して下さい。
U#
using UdonSharp;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Video;
using VRC.SDK3.Components;
using VRC.SDK3.Components.Video;
using VRC.SDK3.Video.Components.Base;
using VRC.SDKBase;
using VRC.Udon;
public class UdonSyncPlayerBySharp : UdonSharpBehaviour
{
public BaseVRCVideoPlayer videoPlayer;
public VRCUrlInputField videoURLInputFiled; // VRC.SDK3.Componentsネームスペース
public float syncFrequency = 5;
public float syncThreshold = 1;
float _lastSyncTime = 0;
[UdonSynced]
float _videoStartNetworkTime = 0;
[UdonSynced]
VRCUrl _syncedURL;
[UdonSynced]
int _videoNumber;
int _loadedVideoNumber;
[UdonSynced]
bool _ownerPlaying;
bool _waitForSync;
public bool allowAnyoneToChange = true;
private void Start()
{
videoPlayer.Loop = false;
}
private void Update()
{
if (Networking.IsOwner(gameObject))
{
SyncVideoIfTime();
}
else
{
if (_waitForSync)
{
if (_ownerPlaying)
{
videoPlayer.Play();
_waitForSync = false;
SyncVideo();
}
}
else
{
SyncVideoIfTime();
}
}
}
private void SyncVideoIfTime()
{
if (Time.realtimeSinceStartup - _lastSyncTime > syncFrequency)
{
_lastSyncTime = Time.realtimeSinceStartup;
SyncVideo();
}
}
private void SyncVideo()
{
var offsetTime = Mathf.Clamp(((float)Networking.GetServerTimeInSeconds() - _videoStartNetworkTime), 0, videoPlayer.GetDuration());
if (Mathf.Abs(videoPlayer.GetTime() - offsetTime) > syncThreshold)
{
videoPlayer.SetTime(offsetTime);
}
Debug.Log($"Syncing Video to {offsetTime:N2}");
}
public void OnURLChanged()
{
if(Networking.IsOwner(gameObject) || allowAnyoneToChange)
{
Networking.SetOwner(Networking.LocalPlayer, gameObject);
_syncedURL = videoURLInputFiled.GetUrl();
_videoNumber++;
_loadedVideoNumber = _videoNumber;
videoPlayer.Stop();
videoPlayer.LoadURL(_syncedURL);
_ownerPlaying = false;
_videoStartNetworkTime = float.MaxValue;
Debug.Log($"Video URL Changed to {_syncedURL}");
RequestSerialization();
}
}
public override void OnVideoReady()
{
if (Networking.IsOwner(gameObject))
{
videoPlayer.Play();
}
else
{
if (_ownerPlaying)
{
videoPlayer.Play();
SyncVideo();
}
else
{
_waitForSync = true;
}
}
}
public override void OnVideoStart()
{
if (Networking.IsOwner(gameObject))
{
_videoStartNetworkTime = (float)Networking.GetServerTimeInMilliseconds();
_ownerPlaying = true;
}
else
{
if (!_ownerPlaying)
{
videoPlayer.Pause();
_waitForSync = true;
}
}
}
public override void OnVideoEnd()
{
if (Networking.IsOwner(gameObject))
{
_videoStartNetworkTime = 0;
_ownerPlaying = false;
RequestSerialization();
}
}
public override void OnDeserialization()
{
if (Networking.IsOwner(gameObject) || _videoNumber == _loadedVideoNumber || string.IsNullOrEmpty(_syncedURL.Get())) return;
// Time.timeSinceLevelLoadはフレームが開始された時間 (Read Only) 。最後のレベルが読み込まれてからの時間 (秒) です。
if (Time.timeSinceLevelLoad > 10)
{
videoPlayer.Stop();
videoPlayer.LoadURL(_syncedURL);
SyncVideo();
_loadedVideoNumber = _videoNumber;
Debug.Log($"Playing synced: {_syncedURL}");
}
}
public override void OnVideoError(VideoError videoError)
{
videoPlayer.Stop();
Debug.Log($"Video failed: {_syncedURL}");
}
public void StopVideo()
{
if (!Networking.IsOwner(gameObject)) return;
_videoStartNetworkTime = 0;
_ownerPlaying = false;
videoPlayer.Stop();
_syncedURL = VRCUrl.Empty;
RequestSerialization();
}
public override bool OnOwnershipRequest(VRCPlayerApi requestingPlayer, VRCPlayerApi requestedOwner)
{
return allowAnyoneToChange;
}
}
シーン作成
長いので重要なところだけ抜粋
スクリーン配置
Planeオブジェクトを配置してマテリアルをUnityVideoPlayerScreenに変更します。
UIの配置
UI -> Input Filedを配置して既存Input FIledコンポーネントをVRC Url Input Fieldコンポーネントに置き換えます。
スクリプトの配置
Video URLは初期に再生するビデオのURLです。
下記の赤枠3か所を正しく設定しないと動画が流れないです。(ここで詰まった)
音声の配置
作成した空のオブジェクトにAudio SourceとVRC Spatial Audio Sourceコンポーネントを追加。Use AudioSource Volumeにチェックマークを入れます。
参考文献
動画プレイヤーを作成する