動画プレイヤーを作成するサンプルの解説

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にチェックマークを入れます。

参考文献

VRChat公式ドキュメント Video Players

動画プレイヤーを作成する

投稿ナビゲーション


コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)