370 lines
15 KiB
C#
370 lines
15 KiB
C#
using Pixelplacement;
|
|
using Pixelplacement.TweenSystem;
|
|
using UnityEngine;
|
|
using System;
|
|
using AppleHills.Core.Settings;
|
|
using Core;
|
|
|
|
namespace UI.CardSystem.StateMachine
|
|
{
|
|
/// <summary>
|
|
/// Handles common card animations that can be reused across states.
|
|
/// Centralizes animation logic to avoid duplication.
|
|
/// Animates the CARD ROOT TRANSFORM (all states follow the card).
|
|
/// </summary>
|
|
public class CardAnimator : MonoBehaviour
|
|
{
|
|
private Transform _transform;
|
|
private RectTransform _rectTransform;
|
|
private ICardSystemSettings _settings;
|
|
|
|
private void Awake()
|
|
{
|
|
_transform = transform;
|
|
_rectTransform = GetComponent<RectTransform>();
|
|
_settings = GameManager.GetSettingsObject<ICardSystemSettings>();
|
|
}
|
|
|
|
#region Scale Animations
|
|
|
|
/// <summary>
|
|
/// Animate scale to target value
|
|
/// </summary>
|
|
public TweenBase AnimateScale(Vector3 targetScale, float? duration = null, Action onComplete = null)
|
|
{
|
|
return Tween.LocalScale(_transform, targetScale, duration ?? _settings.DefaultAnimationDuration, 0f,
|
|
Tween.EaseInOut, completeCallback: onComplete);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pulse scale animation (scale up then back to normal)
|
|
/// </summary>
|
|
public void PulseScale(float pulseAmount = 1.1f, float duration = 0.2f, Action onComplete = null)
|
|
{
|
|
Vector3 originalScale = _transform.localScale;
|
|
Vector3 pulseScale = originalScale * pulseAmount;
|
|
|
|
Tween.LocalScale(_transform, pulseScale, duration, 0f, Tween.EaseOutBack,
|
|
completeCallback: () =>
|
|
{
|
|
Tween.LocalScale(_transform, originalScale, duration, 0f, Tween.EaseInBack,
|
|
completeCallback: onComplete);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pop-in animation (scale from 0 to 1 with overshoot)
|
|
/// </summary>
|
|
public TweenBase PopIn(float duration = 0.5f, Action onComplete = null)
|
|
{
|
|
_transform.localScale = Vector3.zero;
|
|
return Tween.LocalScale(_transform, Vector3.one, duration, 0f,
|
|
Tween.EaseOutBack, completeCallback: onComplete);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pop-out animation (scale from current to 0)
|
|
/// </summary>
|
|
public TweenBase PopOut(float duration = 0.3f, Action onComplete = null)
|
|
{
|
|
return Tween.LocalScale(_transform, Vector3.zero, duration, 0f,
|
|
Tween.EaseInBack, completeCallback: onComplete);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Position Animations (RectTransform)
|
|
|
|
/// <summary>
|
|
/// Animate anchored position (for UI elements)
|
|
/// </summary>
|
|
public TweenBase AnimateAnchoredPosition(Vector2 targetPosition, float? duration = null, Action onComplete = null)
|
|
{
|
|
if (_rectTransform == null)
|
|
{
|
|
Debug.LogWarning("CardAnimator: No RectTransform found for anchored position animation");
|
|
return null;
|
|
}
|
|
|
|
return Tween.AnchoredPosition(_rectTransform, targetPosition, duration ?? _settings.DefaultAnimationDuration, 0f,
|
|
Tween.EaseInOut, completeCallback: onComplete);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Animate local position
|
|
/// </summary>
|
|
public TweenBase AnimateLocalPosition(Vector3 targetPosition, float? duration = null, Action onComplete = null)
|
|
{
|
|
return Tween.LocalPosition(_transform, targetPosition, duration ?? _settings.DefaultAnimationDuration, 0f,
|
|
Tween.EaseInOut, completeCallback: onComplete);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Rotation Animations
|
|
|
|
/// <summary>
|
|
/// Animate local rotation to target
|
|
/// </summary>
|
|
public TweenBase AnimateLocalRotation(Quaternion targetRotation, float? duration = null, Action onComplete = null)
|
|
{
|
|
return Tween.LocalRotation(_transform, targetRotation, duration ?? _settings.DefaultAnimationDuration, 0f,
|
|
Tween.EaseInOut, completeCallback: onComplete);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rotate a child object (typically used by states for CardBackVisual, etc.)
|
|
/// </summary>
|
|
public TweenBase AnimateChildRotation(Transform childTransform, Quaternion targetRotation,
|
|
float duration, Action onComplete = null)
|
|
{
|
|
return Tween.LocalRotation(childTransform, targetRotation, duration, 0f,
|
|
Tween.EaseInOut, completeCallback: onComplete);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Flip Animations
|
|
|
|
/// <summary>
|
|
/// Play card flip animation - rotates card back from 0° to 90°, then card front from 180° to 0°
|
|
/// Based on FlippableCard.FlipToReveal()
|
|
/// </summary>
|
|
public void PlayFlip(Transform cardBack, Transform cardFront, float? duration = null, Action onComplete = null)
|
|
{
|
|
float flipDuration = duration ?? _settings.FlipDuration;
|
|
|
|
// Phase 1: Rotate both to 90 degrees (edge view)
|
|
if (cardBack != null)
|
|
{
|
|
Tween.LocalRotation(cardBack, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut);
|
|
}
|
|
|
|
if (cardFront != null)
|
|
{
|
|
Tween.LocalRotation(cardFront, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
|
|
completeCallback: () =>
|
|
{
|
|
// At edge (90°), switch visibility
|
|
if (cardBack != null)
|
|
cardBack.gameObject.SetActive(false);
|
|
if (cardFront != null)
|
|
cardFront.gameObject.SetActive(true);
|
|
|
|
// Phase 2: Rotate front from 90 to 0 (show at correct orientation)
|
|
Tween.LocalRotation(cardFront, Quaternion.Euler(0, 0, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
|
|
completeCallback: onComplete);
|
|
});
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Play scale punch during flip animation for extra juice
|
|
/// Based on FlippableCard.FlipToReveal()
|
|
/// </summary>
|
|
public void PlayFlipScalePunch(float? punchScale = null, float? duration = null)
|
|
{
|
|
float punch = punchScale ?? _settings.FlipScalePunch;
|
|
float flipDuration = duration ?? _settings.FlipDuration;
|
|
Vector3 originalScale = _transform.localScale;
|
|
|
|
Tween.LocalScale(_transform, originalScale * punch, flipDuration * 0.5f, 0f, Tween.EaseOutBack,
|
|
completeCallback: () =>
|
|
{
|
|
Tween.LocalScale(_transform, originalScale, flipDuration * 0.5f, 0f, Tween.EaseInBack);
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Enlarge/Shrink Animations
|
|
|
|
/// <summary>
|
|
/// Enlarge card to specified scale
|
|
/// Based on FlippableCard.EnlargeCard() and AlbumCard.EnlargeCard()
|
|
/// </summary>
|
|
public void PlayEnlarge(float? targetScale = null, float? duration = null, Action onComplete = null)
|
|
{
|
|
float scale = targetScale ?? _settings.NewCardEnlargedScale;
|
|
float scaleDuration = duration ?? _settings.ScaleDuration;
|
|
|
|
Tween.LocalScale(_transform, Vector3.one * scale, scaleDuration, 0f, Tween.EaseOutBack,
|
|
completeCallback: onComplete);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shrink card back to original scale
|
|
/// Based on AlbumCard.ShrinkCard() and FlippableCard.ReturnToNormalSize()
|
|
/// </summary>
|
|
public void PlayShrink(Vector3 targetScale, float? duration = null, Action onComplete = null)
|
|
{
|
|
float scaleDuration = duration ?? _settings.ScaleDuration;
|
|
|
|
Tween.LocalScale(_transform, targetScale, scaleDuration, 0f, Tween.EaseInBack,
|
|
completeCallback: onComplete);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Combined Animations
|
|
|
|
/// <summary>
|
|
/// Hover enter animation (lift and scale)
|
|
/// For RectTransform UI elements
|
|
/// </summary>
|
|
public void HoverEnter(float liftAmount = 20f, float scaleMultiplier = 1.05f,
|
|
float duration = 0.2f, Action onComplete = null)
|
|
{
|
|
if (_rectTransform != null)
|
|
{
|
|
Vector2 currentPos = _rectTransform.anchoredPosition;
|
|
Vector2 targetPos = currentPos + Vector2.up * liftAmount;
|
|
|
|
Tween.AnchoredPosition(_rectTransform, targetPos, duration, 0f, Tween.EaseOutBack);
|
|
Tween.LocalScale(_transform, Vector3.one * scaleMultiplier, duration, 0f,
|
|
Tween.EaseOutBack, completeCallback: onComplete);
|
|
}
|
|
else
|
|
{
|
|
// Fallback for non-RectTransform
|
|
Vector3 currentPos = _transform.localPosition;
|
|
Vector3 targetPos = currentPos + Vector3.up * liftAmount;
|
|
|
|
Tween.LocalPosition(_transform, targetPos, duration, 0f, Tween.EaseOutBack);
|
|
Tween.LocalScale(_transform, Vector3.one * scaleMultiplier, duration, 0f,
|
|
Tween.EaseOutBack, completeCallback: onComplete);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hover exit animation (return to original position and scale)
|
|
/// </summary>
|
|
public void HoverExit(Vector2 originalPosition, float duration = 0.2f, Action onComplete = null)
|
|
{
|
|
if (_rectTransform != null)
|
|
{
|
|
Tween.AnchoredPosition(_rectTransform, originalPosition, duration, 0f, Tween.EaseInBack);
|
|
Tween.LocalScale(_transform, Vector3.one, duration, 0f,
|
|
Tween.EaseInBack, completeCallback: onComplete);
|
|
}
|
|
else
|
|
{
|
|
Tween.LocalPosition(_transform, originalPosition, duration, 0f, Tween.EaseInBack);
|
|
Tween.LocalScale(_transform, Vector3.one, duration, 0f,
|
|
Tween.EaseInBack, completeCallback: onComplete);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Idle hover animation (gentle bobbing loop)
|
|
/// Returns the TweenBase so caller can stop it later
|
|
/// </summary>
|
|
public TweenBase StartIdleHover(float hoverHeight = 10f, float duration = 1.5f)
|
|
{
|
|
if (_rectTransform != null)
|
|
{
|
|
Vector2 originalPos = _rectTransform.anchoredPosition;
|
|
Vector2 targetPos = originalPos + Vector2.up * hoverHeight;
|
|
|
|
return Tween.Value(0f, 1f,
|
|
(val) =>
|
|
{
|
|
if (_rectTransform != null)
|
|
{
|
|
float t = Mathf.Sin(val * Mathf.PI * 2f) * 0.5f + 0.5f;
|
|
_rectTransform.anchoredPosition = Vector2.Lerp(originalPos, targetPos, t);
|
|
}
|
|
},
|
|
duration, 0f, Tween.EaseInOut, Tween.LoopType.Loop);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Flip Animations (Two-Phase)
|
|
|
|
/// <summary>
|
|
/// Flip animation: Phase 1 - Rotate card back to edge (0° to 90°)
|
|
/// Used by FlippingState to hide the back
|
|
/// </summary>
|
|
public void FlipPhase1_HideBack(Transform cardBackTransform, float duration, Action onHalfwayComplete)
|
|
{
|
|
Tween.LocalRotation(cardBackTransform, Quaternion.Euler(0, 90, 0), duration, 0f,
|
|
Tween.EaseInOut, completeCallback: onHalfwayComplete);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Flip animation: Phase 2 - Rotate card front from back to face (180° to 90° to 0°)
|
|
/// Used by FlippingState to reveal the front
|
|
/// </summary>
|
|
public void FlipPhase2_RevealFront(Transform cardFrontTransform, float duration, Action onComplete)
|
|
{
|
|
// First rotate from 180 to 90 (edge)
|
|
Tween.LocalRotation(cardFrontTransform, Quaternion.Euler(0, 90, 0), duration, 0f,
|
|
Tween.EaseInOut,
|
|
completeCallback: () =>
|
|
{
|
|
// Then rotate from 90 to 0 (face)
|
|
Tween.LocalRotation(cardFrontTransform, Quaternion.Euler(0, 0, 0), duration, 0f,
|
|
Tween.EaseInOut, completeCallback: onComplete);
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Scale punch during flip (makes flip more juicy)
|
|
/// </summary>
|
|
public void FlipScalePunch(float punchMultiplier = 1.1f, float totalDuration = 0.6f)
|
|
{
|
|
Vector3 originalScale = _transform.localScale;
|
|
Vector3 punchScale = originalScale * punchMultiplier;
|
|
|
|
Tween.LocalScale(_transform, punchScale, totalDuration * 0.5f, 0f, Tween.EaseOutBack,
|
|
completeCallback: () =>
|
|
{
|
|
Tween.LocalScale(_transform, originalScale, totalDuration * 0.5f, 0f, Tween.EaseInBack);
|
|
});
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Utility
|
|
|
|
/// <summary>
|
|
/// Stop all active tweens on this transform
|
|
/// </summary>
|
|
public void StopAllAnimations()
|
|
{
|
|
Tween.Stop(_transform.GetInstanceID());
|
|
if (_rectTransform != null)
|
|
Tween.Stop(_rectTransform.GetInstanceID());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset transform to default values
|
|
/// </summary>
|
|
public void ResetTransform()
|
|
{
|
|
StopAllAnimations();
|
|
_transform.localPosition = Vector3.zero;
|
|
_transform.localRotation = Quaternion.identity;
|
|
_transform.localScale = Vector3.one;
|
|
|
|
if (_rectTransform != null)
|
|
_rectTransform.anchoredPosition = Vector2.zero;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get current anchored position (useful for saving before hover)
|
|
/// </summary>
|
|
public Vector2 GetAnchoredPosition()
|
|
{
|
|
return _rectTransform != null ? _rectTransform.anchoredPosition : Vector2.zero;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
|