Code up the card part
This commit is contained in:
@@ -153,6 +153,10 @@ namespace UI.CardSystem
|
||||
{
|
||||
if (boosterOpeningPage != null && UIPageController.Instance != null)
|
||||
{
|
||||
// Pass current booster count to the opening page
|
||||
int boosterCount = CardSystemManager.Instance?.GetBoosterPackCount() ?? 0;
|
||||
boosterOpeningPage.SetAvailableBoosterCount(boosterCount);
|
||||
|
||||
UIPageController.Instance.PushPage(boosterOpeningPage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AppleHills.Data.CardSystem;
|
||||
using Data.CardSystem;
|
||||
using Pixelplacement;
|
||||
using UI.Core;
|
||||
using UI.CardSystem.DragDrop;
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
@@ -11,7 +14,7 @@ namespace UI.CardSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// UI page for opening booster packs and displaying the cards received.
|
||||
/// Automatically triggers the opening when the page is shown.
|
||||
/// Manages the entire booster opening flow with drag-and-drop interaction.
|
||||
/// </summary>
|
||||
public class BoosterOpeningPage : UIPage
|
||||
{
|
||||
@@ -19,13 +22,26 @@ namespace UI.CardSystem
|
||||
[SerializeField] private CanvasGroup canvasGroup;
|
||||
[SerializeField] private Button closeButton;
|
||||
|
||||
[Header("Booster Management")]
|
||||
[SerializeField] private GameObject[] boosterPackInstances; // Booster prefabs/instances
|
||||
[SerializeField] private SlotContainer bottomRightSlots; // Holds waiting boosters
|
||||
[SerializeField] private DraggableSlot centerOpeningSlot; // Where booster goes to open
|
||||
|
||||
[Header("Card Display")]
|
||||
[SerializeField] private Transform cardDisplayContainer;
|
||||
[SerializeField] private CardDisplay cardDisplayPrefab;
|
||||
[SerializeField] private GameObject flippableCardPrefab; // Placeholder for card backs
|
||||
[SerializeField] private float cardSpacing = 150f;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private float cardRevealDelay = 0.5f;
|
||||
[SerializeField] private float cardSpacing = 50f;
|
||||
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
||||
|
||||
private int _availableBoosterCount;
|
||||
private BoosterPackDraggable _currentBoosterInCenter;
|
||||
private List<GameObject> _currentRevealedCards = new List<GameObject>();
|
||||
private CardData[] _currentCardData;
|
||||
private int _revealedCardCount;
|
||||
private bool _isProcessingOpening;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -51,6 +67,15 @@ namespace UI.CardSystem
|
||||
{
|
||||
closeButton.onClick.RemoveListener(OnCloseButtonClicked);
|
||||
}
|
||||
|
||||
// Unsubscribe from slot events
|
||||
if (centerOpeningSlot != null)
|
||||
{
|
||||
centerOpeningSlot.OnOccupied -= OnBoosterPlacedInCenter;
|
||||
}
|
||||
|
||||
// Unsubscribe from booster events
|
||||
UnsubscribeFromAllBoosters();
|
||||
}
|
||||
|
||||
private void OnCloseButtonClicked()
|
||||
@@ -61,6 +86,371 @@ namespace UI.CardSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the number of available booster packs before showing the page
|
||||
/// </summary>
|
||||
public void SetAvailableBoosterCount(int count)
|
||||
{
|
||||
_availableBoosterCount = count;
|
||||
}
|
||||
|
||||
public override void TransitionIn()
|
||||
{
|
||||
base.TransitionIn();
|
||||
InitializeBoosterDisplay();
|
||||
}
|
||||
|
||||
public override void TransitionOut()
|
||||
{
|
||||
CleanupPage();
|
||||
base.TransitionOut();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the booster pack display based on available count
|
||||
/// </summary>
|
||||
private void InitializeBoosterDisplay()
|
||||
{
|
||||
if (boosterPackInstances == null || boosterPackInstances.Length == 0)
|
||||
{
|
||||
Debug.LogWarning("BoosterOpeningPage: No booster pack instances assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate how many boosters to show (capped by array size)
|
||||
int visibleCount = Mathf.Min(_availableBoosterCount, boosterPackInstances.Length);
|
||||
|
||||
// Show/hide boosters and assign to slots
|
||||
for (int i = 0; i < boosterPackInstances.Length; i++)
|
||||
{
|
||||
if (boosterPackInstances[i] == null) continue;
|
||||
|
||||
bool shouldShow = i < visibleCount;
|
||||
boosterPackInstances[i].SetActive(shouldShow);
|
||||
|
||||
if (shouldShow)
|
||||
{
|
||||
// Get the booster draggable component
|
||||
BoosterPackDraggable booster = boosterPackInstances[i].GetComponent<BoosterPackDraggable>();
|
||||
if (booster != null)
|
||||
{
|
||||
// Reset state
|
||||
booster.ResetTapCount();
|
||||
booster.SetTapToOpenEnabled(false); // Disable tap-to-open until in center
|
||||
|
||||
// Subscribe to events
|
||||
booster.OnReadyToOpen += OnBoosterReadyToOpen;
|
||||
|
||||
// Assign to bottom-right slot if slots available
|
||||
if (bottomRightSlots != null && i < bottomRightSlots.SlotCount)
|
||||
{
|
||||
DraggableSlot slot = bottomRightSlots.GetSlotAtIndex(i);
|
||||
if (slot != null)
|
||||
{
|
||||
booster.AssignToSlot(slot, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe to center slot events
|
||||
if (centerOpeningSlot != null)
|
||||
{
|
||||
centerOpeningSlot.OnOccupied += OnBoosterPlacedInCenter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a booster is placed in the center opening slot
|
||||
/// </summary>
|
||||
private void OnBoosterPlacedInCenter(DraggableObject draggable)
|
||||
{
|
||||
BoosterPackDraggable booster = draggable as BoosterPackDraggable;
|
||||
if (booster == null) return;
|
||||
|
||||
_currentBoosterInCenter = booster;
|
||||
|
||||
// Lock the slot so it can't be dragged out
|
||||
centerOpeningSlot.SetLocked(true);
|
||||
|
||||
// Enable tap-to-open and reset tap count
|
||||
booster.ResetTapCount();
|
||||
booster.SetTapToOpenEnabled(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle tap-to-place: When player taps a booster in bottom slots, move it to center
|
||||
/// </summary>
|
||||
public void OnBoosterTappedInBottomSlot(BoosterPackDraggable booster)
|
||||
{
|
||||
if (_currentBoosterInCenter != null || centerOpeningSlot == null)
|
||||
return; // Center slot already occupied
|
||||
|
||||
// Move booster to center slot
|
||||
booster.AssignToSlot(centerOpeningSlot, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when booster is ready to open (after max taps)
|
||||
/// </summary>
|
||||
private void OnBoosterReadyToOpen(BoosterPackDraggable booster)
|
||||
{
|
||||
if (_isProcessingOpening) return;
|
||||
|
||||
StartCoroutine(ProcessBoosterOpening(booster));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the booster opening sequence
|
||||
/// </summary>
|
||||
private IEnumerator ProcessBoosterOpening(BoosterPackDraggable booster)
|
||||
{
|
||||
_isProcessingOpening = true;
|
||||
|
||||
// Call CardSystemManager to open the pack
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
List<CardData> revealedCardsList = CardSystemManager.Instance.OpenBoosterPack();
|
||||
_currentCardData = revealedCardsList.ToArray();
|
||||
|
||||
// Animate booster disappearing
|
||||
yield return StartCoroutine(AnimateBoosterDisappear(booster));
|
||||
|
||||
// Show card backs
|
||||
SpawnCardBacks(_currentCardData.Length);
|
||||
|
||||
// Wait for player to reveal all cards
|
||||
yield return StartCoroutine(WaitForCardReveals());
|
||||
|
||||
// Check if more boosters available
|
||||
_availableBoosterCount--;
|
||||
|
||||
if (_availableBoosterCount > 0)
|
||||
{
|
||||
// Show next booster
|
||||
yield return StartCoroutine(ShowNextBooster());
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more boosters, auto-close page
|
||||
yield return new WaitForSeconds(1f);
|
||||
if (UIPageController.Instance != null)
|
||||
{
|
||||
UIPageController.Instance.PopPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_isProcessingOpening = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Animate the booster pack disappearing
|
||||
/// </summary>
|
||||
private IEnumerator AnimateBoosterDisappear(BoosterPackDraggable booster)
|
||||
{
|
||||
if (booster == null) yield break;
|
||||
|
||||
// Scale down and fade out
|
||||
Transform boosterTransform = booster.transform;
|
||||
|
||||
Tween.LocalScale(boosterTransform, Vector3.zero, boosterDisappearDuration, 0f, Tween.EaseInBack);
|
||||
|
||||
// Also fade the visual if it has a CanvasGroup
|
||||
CanvasGroup boosterCg = booster.GetComponentInChildren<CanvasGroup>();
|
||||
if (boosterCg != null)
|
||||
{
|
||||
Tween.Value(1f, 0f, (val) => boosterCg.alpha = val, boosterDisappearDuration, 0f);
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(boosterDisappearDuration);
|
||||
|
||||
// Destroy the booster
|
||||
Destroy(booster.gameObject);
|
||||
_currentBoosterInCenter = null;
|
||||
|
||||
// Unlock center slot
|
||||
centerOpeningSlot.SetLocked(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn card back placeholders for revealing
|
||||
/// </summary>
|
||||
private void SpawnCardBacks(int count)
|
||||
{
|
||||
if (flippableCardPrefab == null || cardDisplayContainer == null)
|
||||
{
|
||||
Debug.LogWarning("BoosterOpeningPage: Missing card prefab or container!");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentRevealedCards.Clear();
|
||||
_revealedCardCount = 0;
|
||||
|
||||
// Calculate positions
|
||||
float totalWidth = (count - 1) * cardSpacing;
|
||||
float startX = -totalWidth / 2f;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
GameObject cardObj = Instantiate(flippableCardPrefab, cardDisplayContainer);
|
||||
RectTransform cardRect = cardObj.GetComponent<RectTransform>();
|
||||
|
||||
if (cardRect != null)
|
||||
{
|
||||
cardRect.anchoredPosition = new Vector2(startX + (i * cardSpacing), 0);
|
||||
}
|
||||
|
||||
// Add button to handle reveal on click
|
||||
Button cardButton = cardObj.GetComponent<Button>();
|
||||
if (cardButton == null)
|
||||
{
|
||||
cardButton = cardObj.AddComponent<Button>();
|
||||
}
|
||||
|
||||
int cardIndex = i; // Capture for closure
|
||||
cardButton.onClick.AddListener(() => OnCardClicked(cardIndex, cardObj));
|
||||
|
||||
_currentRevealedCards.Add(cardObj);
|
||||
|
||||
// Animate cards flying in
|
||||
cardRect.localScale = Vector3.zero;
|
||||
Tween.LocalScale(cardRect, Vector3.one, 0.3f, i * 0.1f, Tween.EaseOutBack);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle card click to reveal
|
||||
/// </summary>
|
||||
private void OnCardClicked(int cardIndex, GameObject cardObj)
|
||||
{
|
||||
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);
|
||||
});
|
||||
|
||||
_revealedCardCount++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until all cards are revealed
|
||||
/// </summary>
|
||||
private IEnumerator WaitForCardReveals()
|
||||
{
|
||||
while (_revealedCardCount < _currentCardData.Length)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// All cards revealed, wait a moment
|
||||
yield return new WaitForSeconds(1f);
|
||||
|
||||
// Clear cards
|
||||
foreach (GameObject card in _currentRevealedCards)
|
||||
{
|
||||
if (card != null)
|
||||
{
|
||||
// Animate out
|
||||
Tween.LocalScale(card.transform, Vector3.zero, 0.3f, 0f, Tween.EaseInBack,
|
||||
completeCallback: () => Destroy(card));
|
||||
}
|
||||
}
|
||||
|
||||
_currentRevealedCards.Clear();
|
||||
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the next booster pack
|
||||
/// </summary>
|
||||
private IEnumerator ShowNextBooster()
|
||||
{
|
||||
// Find the next inactive booster and activate it
|
||||
for (int i = 0; i < boosterPackInstances.Length; i++)
|
||||
{
|
||||
if (boosterPackInstances[i] != null && !boosterPackInstances[i].activeSelf)
|
||||
{
|
||||
boosterPackInstances[i].SetActive(true);
|
||||
|
||||
BoosterPackDraggable booster = boosterPackInstances[i].GetComponent<BoosterPackDraggable>();
|
||||
if (booster != null)
|
||||
{
|
||||
booster.ResetTapCount();
|
||||
booster.SetTapToOpenEnabled(false);
|
||||
booster.OnReadyToOpen += OnBoosterReadyToOpen;
|
||||
|
||||
// Assign to first available slot
|
||||
DraggableSlot slot = bottomRightSlots?.GetAvailableSlots().FirstOrDefault();
|
||||
if (slot != null)
|
||||
{
|
||||
booster.AssignToSlot(slot, true);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up the page when hidden
|
||||
/// </summary>
|
||||
private void CleanupPage()
|
||||
{
|
||||
UnsubscribeFromAllBoosters();
|
||||
|
||||
// Clear any remaining cards
|
||||
foreach (GameObject card in _currentRevealedCards)
|
||||
{
|
||||
if (card != null)
|
||||
Destroy(card);
|
||||
}
|
||||
_currentRevealedCards.Clear();
|
||||
|
||||
_currentBoosterInCenter = null;
|
||||
_isProcessingOpening = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from all booster events
|
||||
/// </summary>
|
||||
private void UnsubscribeFromAllBoosters()
|
||||
{
|
||||
if (boosterPackInstances == null) return;
|
||||
|
||||
foreach (GameObject boosterObj in boosterPackInstances)
|
||||
{
|
||||
if (boosterObj == null) continue;
|
||||
|
||||
BoosterPackDraggable booster = boosterObj.GetComponent<BoosterPackDraggable>();
|
||||
if (booster != null)
|
||||
{
|
||||
booster.OnReadyToOpen -= OnBoosterReadyToOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DoTransitionIn(System.Action onComplete)
|
||||
{
|
||||
// Simple fade in animation
|
||||
|
||||
3
Assets/Scripts/UI/CardSystem/DragDrop.meta
Normal file
3
Assets/Scripts/UI/CardSystem/DragDrop.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 053a2ff2538541699b134b07a07edecb
|
||||
timeCreated: 1762420654
|
||||
120
Assets/Scripts/UI/CardSystem/DragDrop/BoosterPackDraggable.cs
Normal file
120
Assets/Scripts/UI/CardSystem/DragDrop/BoosterPackDraggable.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.CardSystem.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Booster pack specific implementation of DraggableObject.
|
||||
/// Manages booster pack behavior and opening logic.
|
||||
/// </summary>
|
||||
public class BoosterPackDraggable : DraggableObject
|
||||
{
|
||||
[Header("Booster Pack Settings")]
|
||||
[SerializeField] private bool canOpenOnDrop = true;
|
||||
[SerializeField] private bool canOpenOnDoubleClick = true;
|
||||
|
||||
[Header("Tap to Open")]
|
||||
[SerializeField] private bool canTapToOpen = true;
|
||||
[SerializeField] private int maxTapsToOpen = 3;
|
||||
|
||||
// Events
|
||||
public event System.Action<BoosterPackDraggable> OnBoosterOpened;
|
||||
public event System.Action<BoosterPackDraggable, int, int> OnTapped; // (booster, currentTap, maxTaps)
|
||||
public event System.Action<BoosterPackDraggable> OnReadyToOpen; // Final tap reached
|
||||
|
||||
private bool _isOpening;
|
||||
private float _lastClickTime;
|
||||
private int _currentTapCount;
|
||||
|
||||
public bool IsOpening => _isOpening;
|
||||
public int CurrentTapCount => _currentTapCount;
|
||||
|
||||
protected override void OnPointerUpHook(bool longPress)
|
||||
{
|
||||
base.OnPointerUpHook(longPress);
|
||||
|
||||
// Handle tap-to-open logic (only when in slot and not dragged)
|
||||
if (canTapToOpen && !_wasDragged && !longPress && CurrentSlot != null)
|
||||
{
|
||||
_currentTapCount++;
|
||||
|
||||
OnTapped?.Invoke(this, _currentTapCount, maxTapsToOpen);
|
||||
|
||||
if (_currentTapCount >= maxTapsToOpen)
|
||||
{
|
||||
OnReadyToOpen?.Invoke(this);
|
||||
}
|
||||
|
||||
return; // Don't process double-click if tap-to-open is active
|
||||
}
|
||||
|
||||
// Check for double click
|
||||
if (canOpenOnDoubleClick && !longPress && !_wasDragged)
|
||||
{
|
||||
float timeSinceLastClick = Time.time - _lastClickTime;
|
||||
|
||||
if (timeSinceLastClick < 0.3f) // Double click threshold
|
||||
{
|
||||
TriggerOpen();
|
||||
}
|
||||
|
||||
_lastClickTime = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDragEndedHook()
|
||||
{
|
||||
base.OnDragEndedHook();
|
||||
|
||||
// Optionally trigger open when dropped in specific zones
|
||||
if (canOpenOnDrop)
|
||||
{
|
||||
// Could check if dropped in an "opening zone"
|
||||
// For now, just a placeholder
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger the booster pack opening animation and logic
|
||||
/// </summary>
|
||||
public void TriggerOpen()
|
||||
{
|
||||
if (_isOpening)
|
||||
return;
|
||||
|
||||
_isOpening = true;
|
||||
|
||||
OnBoosterOpened?.Invoke(this);
|
||||
|
||||
// The actual opening logic (calling CardSystemManager) should be handled
|
||||
// by the UI page or controller that manages this booster pack
|
||||
|
||||
// Visual feedback would be handled by the BoosterPackVisual
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the opening state
|
||||
/// </summary>
|
||||
public void ResetOpeningState()
|
||||
{
|
||||
_isOpening = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset tap count (useful when starting a new opening sequence)
|
||||
/// </summary>
|
||||
public void ResetTapCount()
|
||||
{
|
||||
_currentTapCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable tap-to-open functionality at runtime
|
||||
/// </summary>
|
||||
public void SetTapToOpenEnabled(bool enabled)
|
||||
{
|
||||
canTapToOpen = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f95c1542aaa549d1867b43f6dc21e90f
|
||||
timeCreated: 1762420681
|
||||
189
Assets/Scripts/UI/CardSystem/DragDrop/BoosterPackVisual.cs
Normal file
189
Assets/Scripts/UI/CardSystem/DragDrop/BoosterPackVisual.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using Pixelplacement;
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI.CardSystem.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Visual representation for BoosterPackDraggable.
|
||||
/// Displays the booster pack sprite and handles opening animations.
|
||||
/// </summary>
|
||||
public class BoosterPackVisual : DraggableVisual
|
||||
{
|
||||
[Header("Booster Pack Visual")]
|
||||
[SerializeField] private Image packImage;
|
||||
[SerializeField] private Sprite packSprite;
|
||||
[SerializeField] private ParticleSystem glowEffect;
|
||||
[SerializeField] private Transform glowTransform;
|
||||
|
||||
[Header("Opening Animation")]
|
||||
[SerializeField] private float openingScalePunch = 0.5f;
|
||||
[SerializeField] private float openingRotationPunch = 360f;
|
||||
[SerializeField] private float openingDuration = 0.5f;
|
||||
|
||||
private BoosterPackDraggable _boosterDraggable;
|
||||
|
||||
public override void Initialize(DraggableObject parent)
|
||||
{
|
||||
base.Initialize(parent);
|
||||
|
||||
_boosterDraggable = parent as BoosterPackDraggable;
|
||||
|
||||
// Get pack image if not assigned
|
||||
if (packImage == null)
|
||||
{
|
||||
packImage = GetComponentInChildren<Image>();
|
||||
}
|
||||
|
||||
// Set initial sprite
|
||||
if (packImage != null && packSprite != null)
|
||||
{
|
||||
packImage.sprite = packSprite;
|
||||
}
|
||||
|
||||
// Subscribe to booster events
|
||||
if (_boosterDraggable != null)
|
||||
{
|
||||
_boosterDraggable.OnBoosterOpened += HandleBoosterOpened;
|
||||
_boosterDraggable.OnTapped += HandleTapped;
|
||||
}
|
||||
|
||||
// Start glow effect if available
|
||||
if (glowEffect != null && !glowEffect.isPlaying)
|
||||
{
|
||||
glowEffect.Play();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateVisualContent()
|
||||
{
|
||||
// Update glow rotation for visual interest
|
||||
if (glowTransform != null)
|
||||
{
|
||||
glowTransform.Rotate(Vector3.forward * 30f * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleBoosterOpened(BoosterPackDraggable booster)
|
||||
{
|
||||
PlayOpeningAnimation();
|
||||
}
|
||||
|
||||
private void HandleTapped(BoosterPackDraggable booster, int currentTap, int maxTaps)
|
||||
{
|
||||
PlayShakeAnimation(currentTap, maxTaps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play progressive shake animation based on tap intensity
|
||||
/// </summary>
|
||||
public void PlayShakeAnimation(int intensity, int maxIntensity)
|
||||
{
|
||||
float normalizedIntensity = (float)intensity / maxIntensity;
|
||||
float shakeAmount = Mathf.Lerp(5f, 30f, normalizedIntensity);
|
||||
float shakeDuration = 0.15f;
|
||||
|
||||
// Shake rotation
|
||||
Vector3 shakeRotation = new Vector3(
|
||||
Random.Range(-shakeAmount, shakeAmount),
|
||||
Random.Range(-shakeAmount, shakeAmount),
|
||||
Random.Range(-shakeAmount, shakeAmount)
|
||||
);
|
||||
|
||||
Tween.Rotation(transform, transform.eulerAngles + shakeRotation,
|
||||
shakeDuration, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () => {
|
||||
Tween.Rotation(transform, Vector3.zero,
|
||||
shakeDuration, 0f, Tween.EaseInBack);
|
||||
});
|
||||
|
||||
// Scale punch (gets bigger with each tap)
|
||||
float punchScale = 1f + (normalizedIntensity * 0.2f);
|
||||
Tween.LocalScale(transform, Vector3.one * punchScale,
|
||||
shakeDuration / 2f, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () => {
|
||||
Tween.LocalScale(transform, Vector3.one,
|
||||
shakeDuration / 2f, 0f, Tween.EaseInBack);
|
||||
});
|
||||
|
||||
// Extra glow burst on final tap
|
||||
if (intensity == maxIntensity && glowEffect != null)
|
||||
{
|
||||
var emission = glowEffect.emission;
|
||||
emission.rateOverTimeMultiplier = 50f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the booster pack opening animation
|
||||
/// </summary>
|
||||
public void PlayOpeningAnimation()
|
||||
{
|
||||
// Scale punch
|
||||
Vector3 targetScale = transform.localScale * (1f + openingScalePunch);
|
||||
Tween.LocalScale(transform, targetScale, openingDuration / 2f, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () => {
|
||||
Tween.LocalScale(transform, Vector3.one, openingDuration / 2f, 0f, Tween.EaseInBack);
|
||||
});
|
||||
|
||||
// Rotation
|
||||
Tween.Rotation(transform, transform.eulerAngles + Vector3.forward * openingRotationPunch,
|
||||
openingDuration, 0f, Tween.EaseOutBack);
|
||||
|
||||
// Glow burst
|
||||
if (glowEffect != null)
|
||||
{
|
||||
glowEffect.Stop();
|
||||
glowEffect.Play();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the booster pack sprite
|
||||
/// </summary>
|
||||
public void SetPackSprite(Sprite sprite)
|
||||
{
|
||||
packSprite = sprite;
|
||||
if (packImage != null)
|
||||
{
|
||||
packImage.sprite = packSprite;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerEnterVisual()
|
||||
{
|
||||
base.OnPointerEnterVisual();
|
||||
|
||||
// Extra glow when hovering
|
||||
if (glowEffect != null)
|
||||
{
|
||||
var emission = glowEffect.emission;
|
||||
emission.rateOverTimeMultiplier = 20f;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerExitVisual()
|
||||
{
|
||||
base.OnPointerExitVisual();
|
||||
|
||||
// Restore normal glow
|
||||
if (glowEffect != null)
|
||||
{
|
||||
var emission = glowEffect.emission;
|
||||
emission.rateOverTimeMultiplier = 10f;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_boosterDraggable != null)
|
||||
{
|
||||
_boosterDraggable.OnBoosterOpened -= HandleBoosterOpened;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7d9474ece3b4d2ebad19ae178b22f4d
|
||||
timeCreated: 1762420699
|
||||
62
Assets/Scripts/UI/CardSystem/DragDrop/CardDraggable.cs
Normal file
62
Assets/Scripts/UI/CardSystem/DragDrop/CardDraggable.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using AppleHills.Data.CardSystem;
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.CardSystem.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Card-specific implementation of DraggableObject.
|
||||
/// Manages card data and card-specific drag behavior.
|
||||
/// </summary>
|
||||
public class CardDraggable : DraggableObject
|
||||
{
|
||||
[Header("Card Data")]
|
||||
[SerializeField] private CardData cardData;
|
||||
|
||||
// Events
|
||||
public event System.Action<CardDraggable, CardData> OnCardDataChanged;
|
||||
|
||||
public CardData CardData => cardData;
|
||||
|
||||
/// <summary>
|
||||
/// Set the card data for this draggable card
|
||||
/// </summary>
|
||||
public void SetCardData(CardData data)
|
||||
{
|
||||
cardData = data;
|
||||
OnCardDataChanged?.Invoke(this, cardData);
|
||||
|
||||
// Update visual if it exists
|
||||
if (_visualInstance != null && _visualInstance is CardDraggableVisual cardVisual)
|
||||
{
|
||||
cardVisual.RefreshCardDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDragStartedHook()
|
||||
{
|
||||
base.OnDragStartedHook();
|
||||
// Card-specific drag started behavior
|
||||
}
|
||||
|
||||
protected override void OnDragEndedHook()
|
||||
{
|
||||
base.OnDragEndedHook();
|
||||
// Card-specific drag ended behavior
|
||||
}
|
||||
|
||||
protected override void OnSelectionChangedHook(bool selected)
|
||||
{
|
||||
base.OnSelectionChangedHook(selected);
|
||||
// Card-specific selection behavior
|
||||
}
|
||||
|
||||
protected override void OnSlotChangedHook(DraggableSlot previousSlot, DraggableSlot newSlot)
|
||||
{
|
||||
base.OnSlotChangedHook(previousSlot, newSlot);
|
||||
// Card-specific slot changed behavior
|
||||
// Could trigger events for card collection reordering, etc.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a2741bb7299441b9f9bd44d746ebb4b
|
||||
timeCreated: 1762420654
|
||||
121
Assets/Scripts/UI/CardSystem/DragDrop/CardDraggableVisual.cs
Normal file
121
Assets/Scripts/UI/CardSystem/DragDrop/CardDraggableVisual.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using AppleHills.Data.CardSystem;
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.CardSystem.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Visual representation for CardDraggable.
|
||||
/// Uses the existing CardDisplay component to render the card.
|
||||
/// </summary>
|
||||
public class CardDraggableVisual : DraggableVisual
|
||||
{
|
||||
[Header("Card Visual Components")]
|
||||
[SerializeField] private CardDisplay cardDisplay;
|
||||
[SerializeField] private Transform shadowTransform;
|
||||
[SerializeField] private float shadowOffset = 20f;
|
||||
|
||||
private Vector3 _shadowInitialPosition;
|
||||
private CardDraggable _cardDraggable;
|
||||
|
||||
public CardDisplay CardDisplay => cardDisplay;
|
||||
|
||||
public override void Initialize(DraggableObject parent)
|
||||
{
|
||||
base.Initialize(parent);
|
||||
|
||||
_cardDraggable = parent as CardDraggable;
|
||||
|
||||
// Get CardDisplay component if not assigned
|
||||
if (cardDisplay == null)
|
||||
{
|
||||
cardDisplay = GetComponentInChildren<CardDisplay>();
|
||||
}
|
||||
|
||||
// Initialize shadow
|
||||
if (shadowTransform != null)
|
||||
{
|
||||
_shadowInitialPosition = shadowTransform.localPosition;
|
||||
}
|
||||
|
||||
// Subscribe to card data changes
|
||||
if (_cardDraggable != null)
|
||||
{
|
||||
_cardDraggable.OnCardDataChanged += HandleCardDataChanged;
|
||||
|
||||
// Initial card setup
|
||||
if (_cardDraggable.CardData != null && cardDisplay != null)
|
||||
{
|
||||
cardDisplay.SetupCard(_cardDraggable.CardData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateVisualContent()
|
||||
{
|
||||
// CardDisplay handles its own rendering, no need to update every frame
|
||||
// This is called every frame but we only update when card data changes
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the card display with current data
|
||||
/// </summary>
|
||||
public void RefreshCardDisplay()
|
||||
{
|
||||
if (cardDisplay != null && _cardDraggable != null && _cardDraggable.CardData != null)
|
||||
{
|
||||
cardDisplay.SetupCard(_cardDraggable.CardData);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCardDataChanged(CardDraggable draggable, CardData data)
|
||||
{
|
||||
RefreshCardDisplay();
|
||||
}
|
||||
|
||||
protected override void OnPointerDownVisual()
|
||||
{
|
||||
base.OnPointerDownVisual();
|
||||
|
||||
// Move shadow down when pressed
|
||||
if (shadowTransform != null)
|
||||
{
|
||||
shadowTransform.localPosition = _shadowInitialPosition + (-Vector3.up * shadowOffset);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerUpVisual(bool longPress)
|
||||
{
|
||||
base.OnPointerUpVisual(longPress);
|
||||
|
||||
// Restore shadow position
|
||||
if (shadowTransform != null)
|
||||
{
|
||||
shadowTransform.localPosition = _shadowInitialPosition;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDragStartedVisual()
|
||||
{
|
||||
base.OnDragStartedVisual();
|
||||
// Card-specific visual effects when dragging starts
|
||||
}
|
||||
|
||||
protected override void OnDragEndedVisual()
|
||||
{
|
||||
base.OnDragEndedVisual();
|
||||
// Card-specific visual effects when dragging ends
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_cardDraggable != null)
|
||||
{
|
||||
_cardDraggable.OnCardDataChanged -= HandleCardDataChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a4c3884410d44f98182cd8119a972a4
|
||||
timeCreated: 1762420668
|
||||
Reference in New Issue
Block a user