// Rollercoaster1.cs using Simuspaces.Simulation; using UnityEngine; using System.Collections; public class Rollercoaster1 : SimuScriptBase { public SceneObject AnimatedObject; public SceneObject ButtonRandomFrame; public SceneObject ButtonPlay; public SceneObject ButtonStop; public string ClipName = ""; public float PlaybackSpeed = 1.0f; public bool Loop = true; public float SyncInterval = 1.0f; public float BootupDelay = 12.0f; private Animation anim; private AnimationState state; private Coroutine syncRoutine; private bool syncingPlayback; public override void Start() { SetupAnim(); MakeInteractable(ButtonRandomFrame, OnRandomFrame, true, null, true, 10f, "Random Frame"); MakeInteractable(ButtonPlay, OnPlay, true, null, true, 10f, "Play Anim"); MakeInteractable(ButtonStop, OnStop, true, null, true, 10f, "Stop Anim"); Log("[LegacyAnimSyncTester] Ready."); StartCoroutine(DelayedPlayStartup()); } private IEnumerator DelayedPlayStartup() { yield return new WaitForSeconds(BootupDelay); PlayAnim(); } private void SetupAnim() { if (AnimatedObject == null || !AnimatedObject.Exists) { LogError("AnimatedObject is not assigned."); return; } anim = AnimatedObject.GetComponentInChildren(true); if (anim == null) { LogError("No legacy Animation found."); return; } if (string.IsNullOrEmpty(ClipName)) { foreach (AnimationState s in anim) { ClipName = s.name; break; } } state = anim[ClipName]; if (state == null) { LogError("Clip not found: " + ClipName); return; } anim.playAutomatically = true; state.wrapMode = Loop ? WrapMode.Loop : WrapMode.Default; FreezeAtTime(0f); } private void OnRandomFrame(SVInteractionInfo info) { if (!EnsureAnim()) return; int maxFrame = Mathf.Max(1, Mathf.RoundToInt(state.clip.frameRate * state.clip.length)); int frame = Random.Range(0, maxFrame); float time = frame / state.clip.frameRate; FreezeAtTime(time); Log("Random synced frame: " + frame); } private void OnPlay(SVInteractionInfo info) { PlayAnim(); } private void PlayAnim() { if (!EnsureAnim()) return; syncingPlayback = true; state.enabled = true; state.weight = 1f; state.speed = PlaybackSpeed; state.wrapMode = Loop ? WrapMode.Loop : WrapMode.Default; anim.Play(state.name); SyncLegacyAnimationState(AnimatedObject); if (syncRoutine != null) StopCoroutine(syncRoutine); syncRoutine = StartCoroutine(SyncPlaybackLoop()); Log("Synced play at speed " + PlaybackSpeed); } private void OnStop(SVInteractionInfo info) { if (!EnsureAnim()) return; syncingPlayback = false; if (syncRoutine != null) { StopCoroutine(syncRoutine); syncRoutine = null; } FreezeAtTime(state.time); Log("Synced stop/freeze at time " + state.time); } private void FreezeAtTime(float time) { state.enabled = true; state.weight = 1f; state.speed = 0f; state.time = Mathf.Repeat(time, state.clip.length); anim.Play(state.name); anim.Sample(); // Important: sync before Stop(). speed=0 tells clients to sample/freeze. SyncLegacyAnimationState(AnimatedObject); } private IEnumerator SyncPlaybackLoop() { while (syncingPlayback && anim != null && state != null && anim.IsPlaying(state.name)) { SyncLegacyAnimationState(AnimatedObject); yield return new WaitForSeconds(SyncInterval); } syncRoutine = null; } private bool EnsureAnim() { if (anim == null || state == null) SetupAnim(); return anim != null && state != null; } }