MiniGames/Assets/Scripts/ClawGrab/PlatformSpinController.cs

232 lines
7.0 KiB
C#

// PlatformSpinController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
/// <summary>
/// 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).
/// </summary>
[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<float> activeScalars = new List<float>();
// 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<DOTweenAnimation>();
}
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
}
/// <summary>
/// Temporarily increases spin speed for 'duration' seconds.
/// </summary>
public void ApplyTurbo(float duration)
{
Debug.Log("ApplyTurbo");
if (duration <= 0f) return;
StartCoroutine(ApplyScalarForDuration(turboMultiplier, duration));
}
/// <summary>
/// Temporarily slows spin for 'duration' seconds.
/// </summary>
public void ApplySlow(float duration)
{
Debug.Log("ApplySlow");
if (duration <= 0f) return;
StartCoroutine(ApplyScalarForDuration(slowMultiplier, duration));
}
/// <summary>
/// Fully freezes spin (pauses tween) for 'duration' seconds.
/// Supports stacking (nested freezes).
/// </summary>
public void ApplyFreeze(float duration)
{
Debug.Log("ApplyFreeze");
if (duration <= 0f) return;
StartCoroutine(FreezeRoutine(duration));
}
// ---------- Internals ----------
private void EnsureTween()
{
if (spinAnim == null) spinAnim = GetComponent<DOTweenAnimation>();
// 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();
}
}