// PlatformSpinController.cs using System.Collections; using System.Collections.Generic; using UnityEngine; using DG.Tweening; /// /// Controls a DOTweenAnimation-based spinning platform via per-tween timeScale. /// - Keep your existing DOTweenAnimation component (like in your screenshot): /// * Target: this GameObject /// * Type: LocalRotate /// * Duration: e.g., 20 /// * Ease: Linear /// * Loops: -1 (Incremental) /// * RotationMode: FastBeyond360 /// * (SpeedBased on/off is fine; we use timeScale) /// /// Call ApplyTurbo/ApplySlow/ApplyFreeze to change spin temporarily. /// Works with stacked effects (e.g., Turbo + Slow overlapping). /// [RequireComponent(typeof(DOTweenAnimation))] public class PlatformSpinController : MonoBehaviour { [Header("Base Spin")] [Tooltip("Base timeScale for the spin tween (1 = authoring speed).")] public float baseTimeScale = 1f; [Header("Effect Multipliers")] [Tooltip("Multiplier applied during Turbo (e.g., 1.75 makes it ~75% faster).")] public float turboMultiplier = 1.75f; [Tooltip("Multiplier applied during Slow (e.g., 0.5 makes it half speed).")] public float slowMultiplier = 0.5f; private DOTweenAnimation spinAnim; private Tween spinTween; // Active scalar modifiers (we multiply them together with baseTimeScale) private readonly List activeScalars = new List(); // Freeze nesting counter (supports stacked freezes) private int freezeCount = 0; [Header("Time Frenzy")] public bool frenzyEnabled = false; public float frenzyInterval = 15f; // seconds public float frenzyStep = 1.15f; // +15% each step private float permanentScalar = 1f; private Coroutine frenzyCo; void Awake() { spinAnim = GetComponent(); } void Start() { EnsureTween(); ApplyEffectiveTimeScale(); // set initial scale } void OnEnable() { EnsureTween(); // If we were paused due to disable, resume if (spinTween != null && !spinTween.IsPlaying() && freezeCount == 0) spinTween.Play(); } public void ApplyReverse(float duration) { Debug.Log("ApplyReverse"); if (spinTween == null || !spinTween.IsActive()) return; StartCoroutine(ReverseRoutine(duration)); } private IEnumerator ReverseRoutine(float duration) { // Play backwards for 'duration', then forward again float prevScale = spinTween.timeScale; // keep current speed but reverse direction by playing backwards spinTween.timeScale = Mathf.Abs(prevScale); // ensure positive scale spinTween.PlayBackwards(); yield return new WaitForSeconds(duration); spinTween.PlayForward(); spinTween.timeScale = prevScale; } public void StartFrenzy() { Debug.Log("StartFrenzy"); if (frenzyCo != null) StopCoroutine(frenzyCo); frenzyEnabled = true; frenzyCo = StartCoroutine(FrenzyLoop()); } public void StopFrenzy() { frenzyEnabled = false; if (frenzyCo != null) StopCoroutine(frenzyCo); frenzyCo = null; } private IEnumerator FrenzyLoop() { while (frenzyEnabled) { yield return new WaitForSeconds(frenzyInterval); permanentScalar *= frenzyStep; // accumulate permanently during the round ApplyEffectiveTimeScale(); } } void OnDisable() { // leave DOTween to manage tween lifecycle; no hard kill here } /// /// Temporarily increases spin speed for 'duration' seconds. /// public void ApplyTurbo(float duration) { Debug.Log("ApplyTurbo"); if (duration <= 0f) return; StartCoroutine(ApplyScalarForDuration(turboMultiplier, duration)); } /// /// Temporarily slows spin for 'duration' seconds. /// public void ApplySlow(float duration) { Debug.Log("ApplySlow"); if (duration <= 0f) return; StartCoroutine(ApplyScalarForDuration(slowMultiplier, duration)); } /// /// Fully freezes spin (pauses tween) for 'duration' seconds. /// Supports stacking (nested freezes). /// public void ApplyFreeze(float duration) { Debug.Log("ApplyFreeze"); if (duration <= 0f) return; StartCoroutine(FreezeRoutine(duration)); } // ---------- Internals ---------- private void EnsureTween() { if (spinAnim == null) spinAnim = GetComponent(); // Ensure the DOTweenAnimation has created its tween // If your DOTweenAnimation is set to AutoPlay, it will have created the tween by now. // If not, we force it to play to get the tween instance. if (spinAnim != null) { // Try to fetch the tween reference the component manages spinTween = spinAnim.tween; if (spinTween == null || !spinTween.active) { // Kick it so it creates the tween and starts it spinAnim.DORestart(); spinTween = spinAnim.tween; } } if (spinTween == null) { Debug.LogWarning("[PlatformSpinController] Could not find/create tween on DOTweenAnimation."); } } private IEnumerator ApplyScalarForDuration(float scalar, float duration) { activeScalars.Add(scalar); ApplyEffectiveTimeScale(); yield return new WaitForSeconds(duration); // Remove one instance of this scalar (handles overlapping same-effect) int idx = activeScalars.IndexOf(scalar); if (idx >= 0) activeScalars.RemoveAt(idx); ApplyEffectiveTimeScale(); } private IEnumerator FreezeRoutine(float duration) { freezeCount++; PauseTween(); yield return new WaitForSeconds(duration); freezeCount = Mathf.Max(0, freezeCount - 1); if (freezeCount == 0) ResumeTween(); } private void PauseTween() { Debug.Log("PauseTween"); if (spinTween != null && spinTween.IsActive()) spinTween.Pause(); } private void ResumeTween() { Debug.Log("ResumeTween"); if (spinTween == null || !spinTween.IsActive()) return; ApplyEffectiveTimeScale(); // ensure correct scale on resume spinTween.Play(); } [ContextMenu("ApplyEffectiveTimeScale")] private void ApplyEffectiveTimeScale() { if (spinTween == null || !spinTween.IsActive()) return; // Multiply all active scalars on top of baseTimeScale float scale = baseTimeScale * permanentScalar; for (int i = 0; i < activeScalars.Count; i++) scale *= activeScalars[i]; // Clamp to avoid zero/negative timeScale issues scale = Mathf.Max(0.01f, scale); spinTween.timeScale = scale; // If frozen, keep paused even after timescale change if (freezeCount > 0 && spinTween.IsPlaying()) spinTween.Pause(); } }