using UdonSharp; using UnityEngine; using VRC.SDK3.Image; using VRC.SDK3.UdonNetworkCalling; using VRC.SDK3.Video.Components.Base; using VRC.SDKBase; using VRC.Udon.Common.Interfaces; public enum ClueScreenType { Blank, Video, Map } //public enum SubMap //{ // NoLabels, // 0 // OneLabel, // 1 // TwoLabels, // 2 // ThreeLabels, // 3 // CorrectLit, // 4 // OnlyCorrect // 5 //} [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] public class CaseVideoSyncPlayer : UdonSharpBehaviour { [SerializeField] private GameManagerRound1 _GameManager; [UdonSynced, FieldChangeCallback(nameof(SubMapIndex))] private int _SubMapIndex = 0; [UdonSynced, FieldChangeCallback(nameof(ShowScreen))] private ClueScreenType _ShowScreen = ClueScreenType.Blank; [FieldChangeCallback(nameof(FlashCorrectAnswer))] private bool _FlashCorrectAnswer = false; [SerializeField] private BaseVRCVideoPlayer _VideoPlayer; [UdonSynced, FieldChangeCallback(nameof(VideoURL))] private VRCUrl _VideoURL; [UdonSynced, FieldChangeCallback(nameof(TimeAndOffset))] private Vector2 _TimeAndOffset; public float SyncFrequency = 5.0f; [SerializeField] private MeshRenderer _BlankScreenMesh; [SerializeField] private MeshRenderer _VideoScreenMesh; [SerializeField] private MeshRenderer _MapScreenMesh; [SerializeField] private Texture2D _PlaceholderMapTexture; private VRCImageDownloader _MapDownloader; private Texture2D[] _MapImages; private IUdonEventReceiver _UdonEventReceiverThis; private int[] _CachedMapIndices = new int[0]; private int _MapDownloadIndex = 0; private bool _MapDownloadsInProgress = false; private const int IMAGES_PER_MAP_ATLAS = 6; void Start() { _MapDownloader = new VRCImageDownloader(); _UdonEventReceiverThis = (IUdonEventReceiver)this; } void OnDestroy() { _MapDownloader.Dispose(); } [NetworkCallable] public void QueueMapDownloads(int[] MapIndices) { if (_MapDownloadsInProgress) { Debug.LogError("[CaseVideoSyncPlayer] Cannot currently requeue a map download while one is in progress."); return; } _MapDownloadsInProgress = true; _CachedMapIndices = MapIndices; _MapImages = new Texture2D[_CachedMapIndices.Length]; for (int i = 0; i < _MapImages.Length; i++) { _MapImages[i] = _PlaceholderMapTexture; } _SubMapIndex = 0; _MapDownloadIndex = 0; LoadMapFromIndex(_CachedMapIndices[_MapDownloadIndex]); } private void LoadMapFromIndex(int MapIndex) { VRCUrl MapURL = _GameManager.GetMapURL(MapIndex); TextureInfo AdditionalTextureInfo = new TextureInfo(); AdditionalTextureInfo.WrapModeU = TextureWrapMode.Clamp; AdditionalTextureInfo.WrapModeV = TextureWrapMode.Clamp; AdditionalTextureInfo.GenerateMipMaps = false; _MapDownloader.DownloadImage( MapURL, null, _UdonEventReceiverThis, AdditionalTextureInfo); } public override void OnImageLoadSuccess(IVRCImageDownload Result) { _MapImages[_MapDownloadIndex] = Result.Result; int MapPage = SubMapIndex / IMAGES_PER_MAP_ATLAS; if (MapPage == _CachedMapIndices[MapPage]) { _MapScreenMesh.sharedMaterial.SetTexture("_EmissionMap", _MapImages[MapPage]); } _MapDownloadIndex++; if (_MapDownloadIndex >= _CachedMapIndices.Length) { _MapDownloadsInProgress = false; } else { LoadMapFromIndex(_CachedMapIndices[_MapDownloadIndex]); } base.OnImageLoadSuccess(Result); } public override void OnImageLoadError(IVRCImageDownload Result) { base.OnImageLoadError(Result); } public void NextCorrectAnswerFrame() { if (FlashCorrectAnswer) { SubMapIndex = (SubMapIndex == 4) ? 3 : 4; SendCustomEventDelayedSeconds(nameof(NextCorrectAnswerFrame), 0.2f); } else { _VideoPlayer.Stop(); SubMapIndex = 0; ShowScreen = ClueScreenType.Blank; } } private void UpdateMap() { int MapPage = SubMapIndex / IMAGES_PER_MAP_ATLAS; int SubmapIndexWrapped = SubMapIndex % IMAGES_PER_MAP_ATLAS; switch (SubmapIndexWrapped) { case 0: _MapScreenMesh.sharedMaterial.SetVector("_MainTex_ST", new Vector4(0.5f, 0.33333333f, 0.0f, 0.66666666f)); break; case 1: _MapScreenMesh.sharedMaterial.SetVector("_MainTex_ST", new Vector4(0.5f, 0.33333333f, 0.5f, 0.66666666f)); break; case 2: _MapScreenMesh.sharedMaterial.SetVector("_MainTex_ST", new Vector4(0.5f, 0.33333333f, 0.0f, 0.33333333f)); break; case 3: _MapScreenMesh.sharedMaterial.SetVector("_MainTex_ST", new Vector4(0.5f, 0.33333333f, 0.5f, 0.33333333f)); break; case 4: _MapScreenMesh.sharedMaterial.SetVector("_MainTex_ST", new Vector4(0.5f, 0.33333333f, 0.0f, 0.0f)); break; case 5: _MapScreenMesh.sharedMaterial.SetVector("_MainTex_ST", new Vector4(0.5f, 0.33333333f, 0.5f, 0.0f)); break; } _MapScreenMesh.sharedMaterial.SetTexture("_EmissionMap", _MapImages[MapPage]); ShowScreen = ClueScreenType.Map; RequestSerialization(); } public void PlayVideo() { _VideoPlayer.PlayURL(VideoURL); RequestSerialization(); } public override void OnVideoStart() { ShowScreen = ClueScreenType.Video; UpdateTimeAndOffset(); base.OnVideoStart(); } private void UpdateTimeAndOffset() { if (Networking.IsOwner(gameObject)) { TimeAndOffset = new Vector2(_VideoPlayer.GetTime(), (float)Networking.GetServerTimeInSeconds()); if (SyncFrequency > 0.0f) { SendCustomEventDelayedSeconds(nameof(UpdateTimeAndOffset), SyncFrequency); } } else { Resync(); } RequestSerialization(); } public void Resync() { _VideoPlayer.SetTime(TimeAndOffset.x + ((float)Networking.GetServerTimeInSeconds() - TimeAndOffset.y)); } public void ClearScreen() { ShowScreen = ClueScreenType.Blank; FlashCorrectAnswer = false; } private void SwapToScreen(ClueScreenType Screen) { if (ShowScreen == Screen) return; switch (Screen) { case ClueScreenType.Blank: { _BlankScreenMesh.gameObject.SetActive(true); _VideoScreenMesh.gameObject.SetActive(false); _MapScreenMesh.gameObject.SetActive(false); break; } case ClueScreenType.Video: { _BlankScreenMesh.gameObject.SetActive(false); _VideoScreenMesh.gameObject.SetActive(true); _MapScreenMesh.gameObject.SetActive(false); break; } case ClueScreenType.Map: { _BlankScreenMesh.gameObject.SetActive(false); _VideoScreenMesh.gameObject.SetActive(false); _MapScreenMesh.gameObject.SetActive(true); break; } } RequestSerialization(); } public int SubMapIndex { set { _SubMapIndex = value; UpdateMap(); } get => _SubMapIndex; } public ClueScreenType ShowScreen { set { SwapToScreen(value); _ShowScreen = value; } get => _ShowScreen; } public bool FlashCorrectAnswer { set { _FlashCorrectAnswer = value; NextCorrectAnswerFrame(); } get => _FlashCorrectAnswer; } public VRCUrl VideoURL { set { if (_VideoURL != value) { _VideoURL = value; PlayVideo(); } } get => _VideoURL; } public Vector2 TimeAndOffset { set { _TimeAndOffset = value; if (!Networking.IsOwner(gameObject)) { Resync(); } } get => _TimeAndOffset; } }