# Drag and Drop Card System A robust, touch-compatible drag-and-drop system for Unity UI, inspired by Balatro's visual feel. Supports cards, booster packs, and any other draggable objects with smooth visual effects. ## Architecture Overview The system is built on a separation of concerns: - **Logic Layer** (`DraggableObject`) - Handles dragging, slot snapping, events - **Visual Layer** (`DraggableVisual`) - Follows the logic object with lerping, tilting, animations - **Slot System** (`DraggableSlot` + `SlotContainer`) - Manages positions and layout ## Core Components ### 1. DraggableObject (Abstract Base Class) Base class for any draggable UI element. **Key Features:** - Touch-compatible via Unity's pointer event system - Smooth movement toward pointer (configurable) - Automatic slot snapping on release - Selection support with visual offset - Comprehensive event system **Usage:** ```csharp public class MyDraggable : DraggableObject { protected override void OnDragStartedHook() { // Custom logic when drag starts } } ``` ### 2. DraggableVisual (Abstract Base Class) Visual representation that follows the DraggableObject. **Key Features:** - Lerps toward parent position (not instant) - Tilt based on movement velocity - Auto-tilt idle animation (sine/cosine wobble) - Manual tilt when hovering - Scale animations on hover/drag/select **Usage:** ```csharp public class MyVisual : DraggableVisual { protected override void UpdateVisualContent() { // Update your visual elements here } } ``` ### 3. DraggableSlot Represents a position where draggables can snap. **Key Features:** - Occupancy management (one object per slot) - Type filtering (restrict which types can occupy) - Lock/unlock functionality - Swap support ### 4. SlotContainer Manages a collection of slots. **Key Features:** - Multiple layout types (Horizontal, Vertical, Grid, Custom) - Curve-based positioning for horizontal layouts - Automatic slot registration - Find closest slot algorithm **Layout Types:** - **Horizontal** - Slots arranged in a horizontal line (with optional curve) - **Vertical** - Slots arranged in a vertical line - **Grid** - Slots arranged in a grid pattern - **Custom** - Manually position slots ## Card-Specific Implementations ### CardDraggable Card-specific draggable implementation. **Features:** - Holds `CardData` reference - Events for card data changes - Integrates with `CardSystemManager` **Example:** ```csharp CardDraggable card = GetComponent(); card.SetCardData(myCardData); ``` ### CardDraggableVisual Visual representation for cards. **Features:** - Uses existing `CardDisplay` component - Shadow effects on press - Automatic visual refresh when card data changes ### BoosterPackDraggable Booster pack implementation. **Features:** - Double-click to open support - Opening state management - Events for opening ### BoosterPackVisual Visual representation for booster packs. **Features:** - Glow particle effects - Opening animation (scale + rotation) - Sprite customization ## Setup Guide ### Basic Setup 1. **Create Slot Container:** ``` GameObject → UI → Panel (rename to "CardSlotContainer") Add Component → SlotContainer ``` 2. **Create Slots:** ``` Under CardSlotContainer: GameObject → UI → Empty (rename to "Slot_01") Add Component → DraggableSlot ``` Duplicate for as many slots as needed. 3. **Create Draggable Card:** ``` GameObject → UI → Image (rename to "CardDraggable") Add Component → CardDraggable ``` 4. **Create Visual Prefab:** ``` Create a prefab with: - Root: Empty GameObject with CardDraggableVisual component - Child: Canvas (for sorting control) - Child: TiltParent (Transform for tilt effects) - Child: ShakeParent (Transform for punch effects) - Child: CardDisplay (your existing card visual) ``` 5. **Link Visual to Draggable:** ``` On CardDraggable: - Assign your visual prefab to "Visual Prefab" - Set "Instantiate Visual" to true ``` ### Advanced: Curved Hand Layout For a Balatro-style curved card hand: 1. On SlotContainer: - Set Layout Type to "Horizontal" - Enable "Use Curve Layout" - Edit "Position Curve" (try: keys at 0,0.5,1 with values 0,1,0) - Set "Curve Height" (e.g., 50) - Enable "Center Slots" 2. Adjust spacing to fit your card size ## Event System ### DraggableObject Events: ```csharp draggable.OnDragStarted += (obj) => { }; draggable.OnDragEnded += (obj) => { }; draggable.OnPointerEntered += (obj) => { }; draggable.OnPointerExited += (obj) => { }; draggable.OnPointerDowned += (obj) => { }; draggable.OnPointerUpped += (obj, longPress) => { }; draggable.OnSelected += (obj, selected) => { }; draggable.OnSlotChanged += (obj, slot) => { }; ``` ### CardDraggable Events: ```csharp cardDraggable.OnCardDataChanged += (card, data) => { }; ``` ### BoosterPackDraggable Events: ```csharp boosterDraggable.OnBoosterOpened += (pack) => { }; ``` ## Touch Support The system is fully touch-compatible out of the box! Unity's Event System automatically routes touch events through the pointer interfaces. **Supported Gestures:** - Single touch drag - Tap to select - Double-tap (on booster packs) - Long press detection **Note:** For multi-touch support, the system uses PointerEventData which handles the first touch automatically. Additional touch support can be added by extending the pointer event handlers. ## Performance Tips 1. **Disable Idle Animations** if you have many cards: ``` On DraggableVisual: useIdleAnimation = false ``` 2. **Reduce Follow Speed** for smoother performance: ``` On DraggableVisual: followSpeed = 20 (default: 30) ``` 3. **Disable Scale Animations** if needed: ``` On DraggableVisual: useScaleAnimations = false ``` 4. **Use Object Pooling** for spawning many cards ## Extending the System ### Creating Custom Draggable Types 1. Inherit from `DraggableObject`: ```csharp public class MyCustomDraggable : DraggableObject { protected override void OnDragStartedHook() { // Your logic } } ``` 2. Inherit from `DraggableVisual`: ```csharp public class MyCustomVisual : DraggableVisual { protected override void UpdateVisualContent() { // Update your visuals } } ``` ### Custom Slot Filtering ```csharp // On DraggableSlot component: filterByType = true allowedTypeNames = { "CardDraggable", "BoosterPackDraggable" } ``` ## Troubleshooting **Cards don't snap to slots:** - Ensure SlotContainer has slots registered - Check that slots aren't locked - Verify type filtering isn't blocking the card **Visuals don't follow smoothly:** - Check followSpeed value (try 20-40) - Ensure TiltParent and ShakeParent are assigned - Verify the visual prefab has correct hierarchy **Touch not working:** - Ensure EventSystem exists in scene - Check Canvas Raycast Target is enabled - Verify GraphicRaycaster is on Canvas **Cards jitter or shake:** - Reduce followSpeed - Disable idle animation - Check for conflicting tweens ## Integration with Card System The drag-and-drop system integrates seamlessly with your existing `CardSystemManager`: ```csharp // Spawn a draggable card from CardData CardData cardData = CardSystemManager.Instance.GetAllCollectedCards()[0]; GameObject cardObj = Instantiate(cardDraggablePrefab, slotContainer.transform); CardDraggable card = cardObj.GetComponent(); card.SetCardData(cardData); // Assign to first available slot DraggableSlot slot = slotContainer.GetAvailableSlots().FirstOrDefault(); if (slot != null) { card.AssignToSlot(slot, false); } ``` ## Credits Inspired by the excellent feel of Balatro's card system (mixandjam/balatro-feel on GitHub). Adapted for AppleHills card collection game with full touch support and Unity UI integration.