Working flipping cards
This commit is contained in:
@@ -38,6 +38,7 @@ namespace UI.CardSystem
|
||||
[SerializeField] private float cardRevealDelay = 0.5f;
|
||||
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
||||
[SerializeField] private CinemachineImpulseSource impulseSource;
|
||||
[SerializeField] private ParticleSystem openingParticleSystem;
|
||||
|
||||
private int _availableBoosterCount;
|
||||
private BoosterPackDraggable _currentBoosterInCenter;
|
||||
@@ -314,6 +315,7 @@ namespace UI.CardSystem
|
||||
// Subscribe to tap events for visual feedback
|
||||
booster.OnTapped += OnBoosterTapped;
|
||||
booster.OnReadyToOpen += OnBoosterReadyToOpen;
|
||||
booster.OnBoosterOpened += OnBoosterOpened;
|
||||
|
||||
// Try to spawn a new booster to maintain 3 visible
|
||||
// Use decrementCount=true because this booster will be consumed
|
||||
@@ -363,6 +365,30 @@ namespace UI.CardSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when booster is opened - play particle effects
|
||||
/// </summary>
|
||||
private void OnBoosterOpened(BoosterPackDraggable booster)
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] Booster opened, playing particle effect");
|
||||
|
||||
// Reset and play particle system
|
||||
if (openingParticleSystem != null)
|
||||
{
|
||||
// Stop any existing playback
|
||||
if (openingParticleSystem.isPlaying)
|
||||
{
|
||||
openingParticleSystem.Stop();
|
||||
}
|
||||
|
||||
// Clear existing particles
|
||||
openingParticleSystem.Clear();
|
||||
|
||||
// Play the particle system
|
||||
openingParticleSystem.Play();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle tap-to-place: When player taps a booster in bottom slots, move it to center
|
||||
/// </summary>
|
||||
@@ -488,15 +514,21 @@ namespace UI.CardSystem
|
||||
cardRect.anchoredPosition = new Vector2(startX + (i * cardSpacing), 0);
|
||||
}
|
||||
|
||||
// Add button to handle reveal on click
|
||||
Button cardButton = cardObj.GetComponent<Button>();
|
||||
if (cardButton == null)
|
||||
// Get FlippableCard component and setup the card data
|
||||
FlippableCard flippableCard = cardObj.GetComponent<FlippableCard>();
|
||||
if (flippableCard != null)
|
||||
{
|
||||
cardButton = cardObj.AddComponent<Button>();
|
||||
// Setup the card data (stored but not revealed yet)
|
||||
flippableCard.SetupCard(_currentCardData[i]);
|
||||
|
||||
// Subscribe to reveal event to track when flipped
|
||||
int cardIndex = i; // Capture for closure
|
||||
flippableCard.OnCardRevealed += (card, data) => OnCardRevealed(cardIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[BoosterOpeningPage] FlippableCard component not found on card {i}!");
|
||||
}
|
||||
|
||||
int cardIndex = i; // Capture for closure
|
||||
cardButton.onClick.AddListener(() => OnCardClicked(cardIndex, cardObj));
|
||||
|
||||
_currentRevealedCards.Add(cardObj);
|
||||
|
||||
@@ -507,32 +539,11 @@ namespace UI.CardSystem
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle card click to reveal
|
||||
/// Handle card reveal (when flipped)
|
||||
/// </summary>
|
||||
private void OnCardClicked(int cardIndex, GameObject cardObj)
|
||||
private void OnCardRevealed(int cardIndex)
|
||||
{
|
||||
if (cardIndex >= _currentCardData.Length) return;
|
||||
|
||||
// Flip/reveal animation (placeholder - just show card data for now)
|
||||
CardDisplay cardDisplay = cardObj.GetComponent<CardDisplay>();
|
||||
if (cardDisplay != null)
|
||||
{
|
||||
cardDisplay.SetupCard(_currentCardData[cardIndex]);
|
||||
}
|
||||
|
||||
// Disable button so it can't be clicked again
|
||||
Button cardButton = cardObj.GetComponent<Button>();
|
||||
if (cardButton != null)
|
||||
{
|
||||
cardButton.interactable = false;
|
||||
}
|
||||
|
||||
// Scale punch animation
|
||||
Tween.LocalScale(cardObj.transform, Vector3.one * 1.2f, 0.15f, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () => {
|
||||
Tween.LocalScale(cardObj.transform, Vector3.one, 0.15f, 0f, Tween.EaseInBack);
|
||||
});
|
||||
|
||||
Debug.Log($"[BoosterOpeningPage] Card {cardIndex} revealed!");
|
||||
_revealedCardCount++;
|
||||
}
|
||||
|
||||
@@ -604,6 +615,7 @@ namespace UI.CardSystem
|
||||
{
|
||||
booster.OnReadyToOpen -= OnBoosterReadyToOpen;
|
||||
booster.OnTapped -= OnBoosterTapped;
|
||||
booster.OnBoosterOpened -= OnBoosterOpened;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,6 +624,7 @@ namespace UI.CardSystem
|
||||
{
|
||||
_currentBoosterInCenter.OnReadyToOpen -= OnBoosterReadyToOpen;
|
||||
_currentBoosterInCenter.OnTapped -= OnBoosterTapped;
|
||||
_currentBoosterInCenter.OnBoosterOpened -= OnBoosterOpened;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ namespace UI.CardSystem.DragDrop
|
||||
[SerializeField] private int maxTapsToOpen = 3;
|
||||
[SerializeField] private float tapPulseScale = 1.15f;
|
||||
[SerializeField] private float tapPulseDuration = 0.2f;
|
||||
[SerializeField] private ParticleSystem openingParticleSystem;
|
||||
|
||||
// ...existing code...
|
||||
public event System.Action<BoosterPackDraggable> OnBoosterOpened;
|
||||
@@ -106,12 +105,6 @@ namespace UI.CardSystem.DragDrop
|
||||
|
||||
_isOpening = true;
|
||||
|
||||
// Play particle effect
|
||||
if (openingParticleSystem != null)
|
||||
{
|
||||
openingParticleSystem.Play();
|
||||
}
|
||||
|
||||
OnBoosterOpened?.Invoke(this);
|
||||
|
||||
// The actual opening logic (calling CardSystemManager) should be handled
|
||||
|
||||
225
Assets/Scripts/UI/CardSystem/FlippableCard.cs
Normal file
225
Assets/Scripts/UI/CardSystem/FlippableCard.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using System;
|
||||
using AppleHills.Data.CardSystem;
|
||||
using Pixelplacement;
|
||||
using Pixelplacement.TweenSystem;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI.CardSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Flippable card wrapper that shows a card back, then flips to reveal the CardDisplay front.
|
||||
/// This component nests an existing CardDisplay prefab to reuse card visuals everywhere.
|
||||
/// </summary>
|
||||
public class FlippableCard : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
|
||||
{
|
||||
[Header("Card References")]
|
||||
[SerializeField] private GameObject cardBackObject; // The card back visual
|
||||
[SerializeField] private GameObject cardFrontObject; // Your CardDisplay prefab instance
|
||||
[SerializeField] private CardDisplay cardDisplay; // Reference to CardDisplay component
|
||||
|
||||
[Header("Idle Hover Animation")]
|
||||
[SerializeField] private bool enableIdleHover = true;
|
||||
[SerializeField] private float idleHoverHeight = 10f;
|
||||
[SerializeField] private float idleHoverDuration = 1.5f;
|
||||
[SerializeField] private float hoverScaleMultiplier = 1.05f;
|
||||
|
||||
[Header("Flip Animation")]
|
||||
[SerializeField] private float flipDuration = 0.6f;
|
||||
[SerializeField] private float flipScalePunch = 1.1f;
|
||||
|
||||
// State
|
||||
private bool _isFlipped = false;
|
||||
private bool _isFlipping = false;
|
||||
private bool _isHovering = false;
|
||||
private TweenBase _idleHoverTween;
|
||||
private CardData _cardData;
|
||||
private Vector2 _originalPosition; // Track original spawn position
|
||||
|
||||
// Events
|
||||
public event Action<FlippableCard, CardData> OnCardRevealed;
|
||||
|
||||
public bool IsFlipped => _isFlipped;
|
||||
public CardData CardData => _cardData;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Auto-find CardDisplay if not assigned
|
||||
if (cardDisplay == null && cardFrontObject != null)
|
||||
{
|
||||
cardDisplay = cardFrontObject.GetComponent<CardDisplay>();
|
||||
}
|
||||
|
||||
// Start with back showing, front hidden
|
||||
if (cardBackObject != null)
|
||||
cardBackObject.SetActive(true);
|
||||
if (cardFrontObject != null)
|
||||
cardFrontObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// Save the original position so we can return to it after hover
|
||||
RectTransform rectTransform = GetComponent<RectTransform>();
|
||||
if (rectTransform != null)
|
||||
{
|
||||
_originalPosition = rectTransform.anchoredPosition;
|
||||
}
|
||||
|
||||
// Start idle hover animation
|
||||
if (enableIdleHover && !_isFlipped)
|
||||
{
|
||||
StartIdleHover();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Setup the card data (stores it but doesn't reveal until flipped)
|
||||
/// </summary>
|
||||
public void SetupCard(CardData data)
|
||||
{
|
||||
_cardData = data;
|
||||
|
||||
// Setup the CardDisplay but keep it hidden
|
||||
if (cardDisplay != null)
|
||||
{
|
||||
cardDisplay.SetupCard(data);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flip the card to reveal the front
|
||||
/// </summary>
|
||||
public void FlipToReveal()
|
||||
{
|
||||
if (_isFlipped || _isFlipping)
|
||||
return;
|
||||
|
||||
_isFlipping = true;
|
||||
|
||||
// Stop idle hover
|
||||
StopIdleHover();
|
||||
|
||||
// Flip animation: rotate Y 0 -> 90 (hide back) -> 180 (show front)
|
||||
Transform cardTransform = transform;
|
||||
|
||||
// Phase 1: Flip to 90 degrees (edge view, hide back)
|
||||
Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 90, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
|
||||
completeCallback: () =>
|
||||
{
|
||||
// Switch visuals at the edge
|
||||
if (cardBackObject != null)
|
||||
cardBackObject.SetActive(false);
|
||||
if (cardFrontObject != null)
|
||||
cardFrontObject.SetActive(true);
|
||||
|
||||
// Phase 2: Flip from 90 to 180 (show front)
|
||||
Tween.LocalRotation(cardTransform, Quaternion.Euler(0, 180, 0), flipDuration * 0.5f, 0f, Tween.EaseInOut,
|
||||
completeCallback: () =>
|
||||
{
|
||||
_isFlipped = true;
|
||||
_isFlipping = false;
|
||||
|
||||
// Fire revealed event
|
||||
OnCardRevealed?.Invoke(this, _cardData);
|
||||
});
|
||||
});
|
||||
|
||||
// Scale punch during flip for extra juice
|
||||
Vector3 originalScale = cardTransform.localScale;
|
||||
Tween.LocalScale(cardTransform, originalScale * flipScalePunch, flipDuration * 0.5f, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () =>
|
||||
{
|
||||
Tween.LocalScale(cardTransform, originalScale, flipDuration * 0.5f, 0f, Tween.EaseInBack);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start idle hover animation (gentle bobbing)
|
||||
/// </summary>
|
||||
private void StartIdleHover()
|
||||
{
|
||||
if (_idleHoverTween != null)
|
||||
return;
|
||||
|
||||
RectTransform rectTransform = GetComponent<RectTransform>();
|
||||
if (rectTransform == null)
|
||||
return;
|
||||
|
||||
Vector2 originalPos = rectTransform.anchoredPosition;
|
||||
Vector2 targetPos = originalPos + Vector2.up * idleHoverHeight;
|
||||
|
||||
_idleHoverTween = Tween.Value(0f, 1f,
|
||||
(val) =>
|
||||
{
|
||||
if (rectTransform != null)
|
||||
{
|
||||
float t = Mathf.Sin(val * Mathf.PI * 2f) * 0.5f + 0.5f; // Smooth sine wave
|
||||
rectTransform.anchoredPosition = Vector2.Lerp(originalPos, targetPos, t);
|
||||
}
|
||||
},
|
||||
idleHoverDuration, 0f, Tween.EaseInOut, Tween.LoopType.Loop);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop idle hover animation
|
||||
/// </summary>
|
||||
private void StopIdleHover()
|
||||
{
|
||||
if (_idleHoverTween != null)
|
||||
{
|
||||
_idleHoverTween.Stop();
|
||||
_idleHoverTween = null;
|
||||
|
||||
// Reset to ORIGINAL position (not Vector2.zero!)
|
||||
RectTransform rectTransform = GetComponent<RectTransform>();
|
||||
if (rectTransform != null)
|
||||
{
|
||||
Tween.AnchoredPosition(rectTransform, _originalPosition, 0.3f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Pointer Event Handlers
|
||||
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
if (_isFlipped || _isFlipping)
|
||||
return;
|
||||
|
||||
_isHovering = true;
|
||||
|
||||
// Scale up slightly on hover
|
||||
Tween.LocalScale(transform, Vector3.one * hoverScaleMultiplier, 0.2f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
public void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
if (_isFlipped || _isFlipping)
|
||||
return;
|
||||
|
||||
_isHovering = false;
|
||||
|
||||
// Scale back to normal
|
||||
Tween.LocalScale(transform, Vector3.one, 0.2f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (_isFlipped || _isFlipping)
|
||||
return;
|
||||
|
||||
// Flip on click
|
||||
FlipToReveal();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
StopIdleHover();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Scripts/UI/CardSystem/FlippableCard.cs.meta
Normal file
3
Assets/Scripts/UI/CardSystem/FlippableCard.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffa05ec4ecbd4cc485e2127683c29f09
|
||||
timeCreated: 1762454507
|
||||
Reference in New Issue
Block a user