using Newtonsoft.Json.Linq; using UdonSharp; using UnityEngine; using VRC.Udon.Common; [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] public class CameraTimedSwitcher : UdonSharpBehaviour { [SerializeField] private CameraControllerBase _CameraController; [SerializeField] private string[] _SwitchFunctions; [SerializeField] private float[] _TimeBetweenCuts = { 5.0f }; [Space] [SerializeField, Tooltip("When enabled, switcher will reset when activated, even if it wasn't deactivated first.")] private bool _AlwaysReactivate = false; [SerializeField] private bool _Loop = true; [SerializeField, Tooltip("Loop to this point in the camera sequence. Default is index 0 (the beginning)")] private int _LoopPoint = 0; [Space] [SerializeField, Tooltip("Set this to call a function on an UdonSharpBehaviour instead of looping the camera sequence")] private UdonSharpBehaviour _FirstLoopCallbackObject = null; [SerializeField] private string _FirstLoopCallbackFunction = ""; [UdonSynced] private bool _Active = false; private float _Timer = 0.0f; private bool _AwaitingNextSwitch = false; private int _NextCameraIndex = 0; private bool _LoopedOnce = false; void Update() { if (_Active && _AwaitingNextSwitch) { _Timer -= Time.deltaTime; if (_Timer <= 0.0f) { SwitchToNextCamera(); } } } public override void OnDeserialization(DeserializationResult Result) { _Activate_Synced(); base.OnDeserialization(Result); } public void Activate(bool Activate) { if (_Active != Activate || _AlwaysReactivate) { _Active = Activate; _Activate_Synced(); RequestSerialization(); } } private void _Activate_Synced() { _AwaitingNextSwitch = false; if (_Active) { _NextCameraIndex = 0; _LoopedOnce = false; SwitchToNextCamera(); } else { _Timer = 0.0f; } } public void SwitchToNextCamera() { _AwaitingNextSwitch = false; if (_Active) { int CurrentCameraIndex = _NextCameraIndex; Debug.Log("[CameraTimedSwitcher] Current camera index: " + CurrentCameraIndex); if (CurrentCameraIndex == _LoopPoint && _LoopedOnce) { if (_FirstLoopCallbackObject != null) { Debug.Log("[CameraTimedSwitcher] We're on a new loop and a callback is set; running function " + _FirstLoopCallbackFunction + " on object " + _FirstLoopCallbackObject.gameObject.name + " and deactivating"); _FirstLoopCallbackObject.SendCustomEvent(_FirstLoopCallbackFunction); Activate(false); return; } if (!_Loop) { Debug.Log("[CameraTimedSwitcher] On a second loop, but looping is disabled; deactivating"); Activate(false); return; } } string Function = _SwitchFunctions[_NextCameraIndex]; if (Function != "") { _CameraController.SendCustomEvent(Function); Debug.Log("[CameraTimedSwitcher] Executing function " + Function); } _NextCameraIndex = (_NextCameraIndex + 1) % _SwitchFunctions.Length; if (_NextCameraIndex == 0) { _LoopedOnce = true; _NextCameraIndex = _LoopPoint; Debug.Log("[CameraTimedSwitcher] Setting next camera index to loop point " + _LoopPoint); } _Timer = _TimeBetweenCuts[Mathf.Min(CurrentCameraIndex, _TimeBetweenCuts.Length - 1)]; _AwaitingNextSwitch = true; Debug.Log("[CameraTimedSwitcher] Camera index " + _NextCameraIndex + " will be activated in " + _Timer + " seconds"); } } }