using System; using System.Collections.Generic; using DG.Tweening; using UnityEngine; using UnityEngine.Events; using UnityEngine.EventSystems; using UnityEngine.UI; [RequireComponent(typeof(EventTrigger))] public class CardCarousel : MonoBehaviour { [SerializeField] RectTransform[] items; [SerializeField] float spacing = 40f; [SerializeField] float swipeThreshold = 60f; [SerializeField] float snapDuration = 0.25f; [SerializeField] Ease snapEase = Ease.OutCubic; [Tooltip("Horizontal half-width of the center tap zone, in local units of this RectTransform.")] [SerializeField] float centerTapHalfWidth = 260f; [SerializeField] bool looped = true; [SerializeField] int selectedIndex; [Header("Focus Animation")] [SerializeField] float activeScale = 1.05f; [SerializeField] float inactiveScale = 0.88f; [SerializeField] float activePullDown = 35f; public UnityEvent onSelected = new UnityEvent(); EventTrigger eventTrigger; RectTransform rectTransform; RectTransform dragSpace; RectTransform duplicateClone; readonly Dictionary baseScales = new(); float slotStep; float focusX; float focusY; float focusPosition; float dragStartPosition; float pointerDownLocalX; bool isDragging; bool isAnimating; Tween snapTween; public int SelectedIndex => selectedIndex; void Awake() { rectTransform = (RectTransform)transform; eventTrigger = GetComponent(); RegisterTrigger(EventTriggerType.PointerDown, OnPointerDown); RegisterTrigger(EventTriggerType.Drag, OnDrag); RegisterTrigger(EventTriggerType.PointerUp, OnPointerUp); } void Start() { if (items == null || items.Length < 2) { Debug.LogWarning($"{nameof(CardCarousel)} on {name} needs at least 2 items.", this); return; } selectedIndex = Mathf.Clamp(selectedIndex, 0, items.Length - 1); dragSpace = items[0].parent as RectTransform; slotStep = items[0].rect.width + spacing; focusX = items[0].anchoredPosition.x; focusY = items[0].anchoredPosition.y; duplicateClone = CreateClone(items[0], "_carouselClone_Dup"); foreach (var item in items) CacheBaseScale(item); focusPosition = selectedIndex; ApplyLayout(); } void OnDestroy() { snapTween?.Kill(); } void RegisterTrigger(EventTriggerType type, Action handler) { var entry = new EventTrigger.Entry { eventID = type }; entry.callback.AddListener(handler.Invoke); eventTrigger.triggers.Add(entry); } RectTransform CreateClone(RectTransform source, string suffix) { var clone = Instantiate(source, source.parent); clone.name = source.name + suffix; DisableInteractions(clone); return clone; } static void DisableInteractions(RectTransform target) { foreach (var graphic in target.GetComponentsInChildren(true)) graphic.raycastTarget = false; foreach (var button in target.GetComponentsInChildren