Files
AppleHillsProduction/docs/drag_drop_system_readme.md
2025-11-06 15:27:08 +01:00

7.7 KiB

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:

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:

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:

CardDraggable card = GetComponent<CardDraggable>();
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:

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:

cardDraggable.OnCardDataChanged += (card, data) => { };

BoosterPackDraggable Events:

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:
public class MyCustomDraggable : DraggableObject
{
    protected override void OnDragStartedHook()
    {
        // Your logic
    }
}
  1. Inherit from DraggableVisual:
public class MyCustomVisual : DraggableVisual
{
    protected override void UpdateVisualContent()
    {
        // Update your visuals
    }
}

Custom Slot Filtering

// 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:

// Spawn a draggable card from CardData
CardData cardData = CardSystemManager.Instance.GetAllCollectedCards()[0];
GameObject cardObj = Instantiate(cardDraggablePrefab, slotContainer.transform);
CardDraggable card = cardObj.GetComponent<CardDraggable>();
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.