This commit is contained in:
Michal Pikulski
2025-11-06 15:33:30 +01:00
parent 2d10d92bf5
commit 7862af7f8b
8 changed files with 0 additions and 4306 deletions

View File

@@ -1,147 +0,0 @@
# 🎯 Quick Setup Checklist - Copy This!
## ✅ What's Done (By AI)
- ✅ Created `BoosterVisual.prefab` in `Assets/Prefabs/UI/DragAndDrop/`
- ✅ Created `BoosterPackPrefab.prefab` in `Assets/Prefabs/UI/Cards/`
- ✅ Created `FlippableCardPrefab.prefab` in `Assets/Prefabs/UI/Cards/`
- ✅ BoosterVisual has TiltParent, ShakeParent, PackImage structure
- ✅ All components are added
---
## 📝 What YOU Need To Do (Quick Version)
### **1. BoosterVisual.prefab** (2 minutes)
```
Open prefab → Select root
├─ Assign: TiltParent → to "Tilt Parent" field
├─ Assign: ShakeParent → to "Shake Parent" field
├─ Assign: TiltParent/PackImage → to "Pack Image" field
└─ Assign: YOUR SPRITE → to "Pack Sprite" field
Select TiltParent/PackImage
└─ Uncheck "Raycast Target" on Image component
```
### **2. BoosterPackPrefab.prefab** (1 minute)
```
Open prefab → Select root
├─ Set size: 200x300
├─ Check "Raycast Target" on Image component
├─ Assign: BoosterVisual.prefab → to "Visual Prefab" field
├─ Check: "Can Tap To Open"
└─ Set: "Max Taps To Open" = 3
```
### **3. FlippableCardPrefab.prefab** (1 minute)
```
Open prefab → Select root
├─ Set size: 200x300
└─ Assign: YOUR CARD BACK SPRITE → to Image sprite
```
### **4. BoosterOpeningPage.prefab** (10 minutes)
```
Open prefab → Add children manually:
CREATE THESE CHILDREN:
├─ CloseButton (UI > Button)
├─ BottomRightContainer (Empty + SlotContainer component)
│ ├─ Slot_0 (Empty + DraggableSlot)
│ ├─ Slot_1 (Empty + DraggableSlot)
│ └─ Slot_2 (Empty + DraggableSlot)
├─ CenterSlot (Empty + DraggableSlot)
│ └─ IMPORTANT: Apply Scale = ✓, Scale = (2,2,1)
├─ CardDisplayContainer (Empty)
└─ BoosterInstances (Empty)
├─ Drag BoosterPackPrefab → name "BoosterPack_0" → Uncheck Active
├─ Drag BoosterPackPrefab → name "BoosterPack_1" → Uncheck Active
└─ Drag BoosterPackPrefab → name "BoosterPack_2" → Uncheck Active
WIRE UP ROOT COMPONENT:
Select BoosterOpeningPage root → Assign all references:
├─ Close Button → CloseButton
├─ Booster Pack Instances [0-2] → BoosterPack_0, 1, 2
├─ Bottom Right Slots → BottomRightContainer
├─ Center Opening Slot → CenterSlot
├─ Card Display Container → CardDisplayContainer
└─ Flippable Card Prefab → FlippableCardPrefab.prefab
```
---
## 🔍 Critical Settings
### **CenterSlot (Most Important!)**
```yaml
Apply Scale To Occupant: ✓ CHECK THIS!
Occupant Scale: (2, 2, 1) # Makes booster 2x bigger
Filter By Type:
Allowed Type Names: ["BoosterPackDraggable"]
```
### **BottomRightContainer SlotContainer**
```yaml
Layout Type: Vertical
Spacing: 120
Center Slots:
Auto Register Children:
```
### **Each Bottom Slot (Slot_0, 1, 2)**
```yaml
Filter By Type:
Allowed Type Names: ["BoosterPackDraggable"]
Apply Scale To Occupant: ☐ UNCHECKED (normal size)
```
---
## ⚡ Super Quick Start
**Fastest path to testing:**
1. Open `BoosterVisual.prefab` → Assign 4 references → Save
2. Open `BoosterPackPrefab.prefab` → Assign BoosterVisual, check tap settings → Save
3. Open `FlippableCardPrefab.prefab` → Assign sprite → Save
4. Open `BoosterOpeningPage.prefab` → Build child structure (see above) → Wire references → Save
5. Test!
---
## 🎨 Sprite Assignments Needed
You need to assign these sprites:
1. **Booster Pack Sprite** → BoosterVisual/PackImage
2. **Card Back Sprite** → FlippableCardPrefab
3. **Optional: Background** → BoosterOpeningPage/Background
---
## 🐛 If Something Doesn't Work
**Can't drag booster:**
→ Check BoosterPackPrefab base Image has raycastTarget = ✓
**Booster doesn't scale in center:**
→ Check CenterSlot "Apply Scale To Occupant" = ✓
**Taps don't work:**
→ Check "Can Tap To Open" = ✓ on booster
**No boosters show:**
→ Check they're assigned in BoosterOpeningPage component
→ Check CardSystemManager has boosters available
---
## 📄 Full Details
See `SETUP_COMPLETE_Manual_Steps.md` for detailed step-by-step instructions!
---
**Estimated Time:** 15-20 minutes total 🕐

View File

@@ -1,343 +0,0 @@
# ✅ Booster Opening System - Auto-Generated Prefabs & Manual Setup
## 🎉 What I Created For You
I've successfully created the following prefabs using Unity MCP:
### ✅ **Created Prefabs:**
1. **`Assets/Prefabs/UI/DragAndDrop/BoosterVisual.prefab`**
- Base visual prefab for booster packs
- Has: Canvas, CanvasGroup, BoosterPackVisual component
- Has children: TiltParent (with PackImage), ShakeParent
2. **`Assets/Prefabs/UI/Cards/BoosterPackPrefab.prefab`**
- Main draggable booster prefab
- Has: Image, BoosterPackDraggable component
- Ready for visual prefab assignment
3. **`Assets/Prefabs/UI/Cards/FlippableCardPrefab.prefab`**
- Card reveal prefab
- Has: Image, CardDisplay, Button components
- Ready for sprite assignment
### 📋 **Existing Prefab:**
4. **`Assets/Prefabs/UI/Cards/BoosterOpeningPage.prefab`**
- Already has: RectTransform, CanvasGroup, BoosterOpeningPage component
- Needs child structure added (see below)
---
## 🛠️ Manual Setup Required
Due to Unity MCP limitations with editing existing prefabs, you'll need to complete the setup manually. Here's EXACTLY what to do:
### **STEP 1: Configure BoosterVisual Prefab**
1. Open `Assets/Prefabs/UI/DragAndDrop/BoosterVisual.prefab`
2. Select the root `BoosterVisual` object
3. In **BoosterPackVisual** component, assign references:
- Tilt Parent: Drag `TiltParent` child
- Shake Parent: Drag `ShakeParent` child
- Pack Image: Drag `TiltParent/PackImage`
- Pack Sprite: **[YOU ASSIGN]** - Your booster pack sprite asset
4. Set these values:
```
Follow Speed: 30
Rotation Amount: 20
Rotation Speed: 20
Auto Tilt Amount: 30
Manual Tilt Amount: 20
Tilt Speed: 20
Use Scale Animations: ✓
Scale On Hover: 1.15
Scale On Drag: 1.25
Use Idle Animation: ✓
Idle Animation Speed: 1
Opening Scale Punch: 0.5
Opening Rotation Punch: 360
Opening Duration: 0.5
```
5. Select `TiltParent/PackImage`
6. Set **Image** component:
- Raycast Target: ☐ **UNCHECK THIS**
- Size: 180x280 (or adjust to fit)
7. Save prefab
---
### **STEP 2: Configure BoosterPackPrefab**
1. Open `Assets/Prefabs/UI/Cards/BoosterPackPrefab.prefab`
2. Select root `BoosterPackPrefab`
3. Set **RectTransform**:
- Width: 200
- Height: 300
4. Set **Image** component:
- Sprite: **[YOU ASSIGN]** - Placeholder or your booster sprite
- Raycast Target: ✓ **MUST BE CHECKED**
5. Set **BoosterPackDraggable** component:
```
Move Speed: 50
Smooth Movement: ✓
Snap Duration: 0.3
Visual Prefab: [Drag BoosterVisual.prefab]
Instantiate Visual: ✓
Is Selectable: ✓
Selection Offset: 50
Can Open On Drop: ☐
Can Open On Double Click: ☐
Can Tap To Open: ✓
Max Taps To Open: 3
```
6. Save prefab
---
### **STEP 3: Configure FlippableCardPrefab**
1. Open `Assets/Prefabs/UI/Cards/FlippableCardPrefab.prefab`
2. Select root `FlippableCardPrefab`
3. Set **RectTransform**:
- Width: 200
- Height: 300
4. Set **Image** component:
- Sprite: **[YOU ASSIGN]** - Card back sprite
- Raycast Target: ✓
5. **CardDisplay** component will be configured at runtime
6. **Button** component is ready (OnClick added at runtime)
7. Save prefab
---
### **STEP 4: Build BoosterOpeningPage Structure**
⚠️ **IMPORTANT:** You need to manually add children to the BoosterOpeningPage prefab.
1. Open `Assets/Prefabs/UI/Cards/BoosterOpeningPage.prefab` in Prefab mode
2. Add the following structure:
```
BoosterOpeningPage (root - already exists)
├── Background (optional)
│ └── [Create: UI > Image]
│ └── [Set: Full screen, dark semi-transparent]
├── CloseButton
│ └── [Create: UI > Button - TextMeshPro]
│ └── [Position: Top-right corner]
│ └── [Text: "X" or "Close"]
├── BottomRightContainer
│ └── [Create: Empty GameObject]
│ └── [Add Component: SlotContainer]
│ └── [Position: X=800, Y=-400 (adjust for your canvas)]
│ └── [Anchor: Bottom-Right]
│ └── [Children: 3 slots below]
│ │
│ ├── Slot_0
│ │ └── [Create: Empty GameObject]
│ │ └── [Add Component: DraggableSlot]
│ │ └── [Size: 150x200]
│ │
│ ├── Slot_1
│ │ └── [Same as Slot_0]
│ │
│ └── Slot_2
│ └── [Same as Slot_0]
├── CenterSlot
│ └── [Create: Empty GameObject]
│ └── [Add Component: DraggableSlot]
│ └── [Position: Center (0, 0)]
│ └── [Size: 300x400]
│ └── [Anchor: Middle Center]
├── CardDisplayContainer
│ └── [Create: Empty GameObject]
│ └── [Position: Center (0, -100)]
│ └── [Anchor: Middle Center]
└── BoosterInstances
└── [Create: Empty GameObject]
└── [Children: 3 booster instances below]
├── BoosterPack_0
│ └── [Drag in: BoosterPackPrefab]
│ └── [Set Active: ☐ UNCHECKED]
├── BoosterPack_1
│ └── [Drag in: BoosterPackPrefab]
│ └── [Set Active: ☐ UNCHECKED]
└── BoosterPack_2
└── [Drag in: BoosterPackPrefab]
└── [Set Active: ☐ UNCHECKED]
```
---
### **STEP 5: Configure BottomRightContainer (SlotContainer)**
1. Select `BottomRightContainer`
2. Set **RectTransform**:
- Anchor: Bottom-Right
- Pivot: (1, 0)
- Position: X=-100, Y=100 (offset from corner)
3. Set **SlotContainer** component:
```
Layout Type: Vertical
Spacing: 120
Center Slots: ✓
Auto Register Children: ✓
```
4. For each child slot (Slot_0, Slot_1, Slot_2):
- Width: 150, Height: 200
- **DraggableSlot** settings:
```
Slot Index: 0, 1, 2 (respectively)
Is Locked: ☐
Filter By Type: ✓
Allowed Type Names: ["BoosterPackDraggable"]
Apply Scale To Occupant: ☐
```
---
### **STEP 6: Configure CenterSlot (Opening Slot)**
1. Select `CenterSlot`
2. Set **RectTransform**:
- Anchor: Middle Center
- Position: (0, 0)
- Width: 300, Height: 400
3. Set **DraggableSlot** component:
```
Slot Index: 0
Is Locked: ☐
Filter By Type: ✓
Allowed Type Names: ["BoosterPackDraggable"]
Apply Scale To Occupant: ✓ ← IMPORTANT!
Occupant Scale: (2, 2, 1) ← IMPORTANT!
Scale Transition Duration: 0.3
```
---
### **STEP 7: Wire Up BoosterOpeningPage Component**
1. Select root `BoosterOpeningPage`
2. In **BoosterOpeningPage** component, assign:
```
[UI References]
Canvas Group: [Auto-assigned or drag it]
Close Button: [Drag CloseButton]
[Booster Management]
Booster Pack Instances (Array size: 3):
Element 0: [Drag BoosterPack_0]
Element 1: [Drag BoosterPack_1]
Element 2: [Drag BoosterPack_2]
Bottom Right Slots: [Drag BottomRightContainer]
Center Opening Slot: [Drag CenterSlot]
[Card Display]
Card Display Container: [Drag CardDisplayContainer]
Flippable Card Prefab: [Drag FlippableCardPrefab.prefab]
Card Spacing: 150
[Settings]
Card Reveal Delay: 0.5
Booster Disappear Duration: 0.5
Transition Duration: 0.3
```
3. Save prefab
---
## ✅ Final Checklist
Before testing, verify:
### **BoosterVisual.prefab:**
- [ ] TiltParent and ShakeParent are assigned in component
- [ ] Pack Image is assigned in component
- [ ] Pack Sprite is assigned (your asset)
- [ ] PackImage has raycastTarget = false
### **BoosterPackPrefab.prefab:**
- [ ] Size is 200x300
- [ ] Base Image has raycastTarget = TRUE
- [ ] Visual Prefab is assigned (BoosterVisual)
- [ ] Can Tap To Open = ✓
- [ ] Max Taps To Open = 3
### **FlippableCardPrefab.prefab:**
- [ ] Size is 200x300
- [ ] Has card back sprite assigned
- [ ] Has CardDisplay component
- [ ] Has Button component
### **BoosterOpeningPage.prefab:**
- [ ] Has all children created (see structure above)
- [ ] BottomRightContainer has 3 DraggableSlots
- [ ] CenterSlot has scale settings configured
- [ ] BoosterInstances has 3 inactive booster prefab instances
- [ ] All references assigned in BoosterOpeningPage component
---
## 🎮 Testing
1. **Test in Scene:**
- Add BoosterOpeningPage prefab to your scene
- Make sure it's a child of your Canvas
- Start inactive (UIPage system controls visibility)
2. **Test Boosters:**
- Set CardSystemManager booster count to 3
- Open album → Click booster button
- Should see 3 boosters in bottom-right
3. **Test Drag:**
- Drag a booster to center
- Should scale up to 2x
4. **Test Taps:**
- Tap booster 3 times
- Should shake progressively
- Should disappear and show cards
---
## 📝 What You Still Need To Do
1. **Assign Sprites:**
- Booster pack sprite to BoosterVisual/PackImage
- Card back sprite to FlippableCardPrefab
2. **Build Page Structure:**
- Add all children to BoosterOpeningPage as outlined
- Position elements appropriately for your canvas size
3. **Wire References:**
- Assign all the references in BoosterOpeningPage component
4. **Visual Polish:**
- Add optional particle effects to BoosterVisual
- Style the close button
- Add background image
---
## 💡 Tips
- **Canvas Size:** Adjust positions based on your canvas reference resolution
- **Testing:** Test one piece at a time (boosters, then slots, then page)
- **Sprites:** Use placeholder sprites initially, replace later
- **Debugging:** Enable Gizmos to see slot positions
---
🎉 **Three prefabs are ready! Just follow the manual steps above to complete the setup!** 🎉

View File

@@ -1,395 +0,0 @@
# Booster Opening System - Complete Setup Guide
## 📋 Overview
This guide walks you through setting up the complete booster opening flow, from the Album page to opening packs and revealing cards.
---
## 🎯 What Was Implemented
### **Core Components Enhanced:**
1. **DraggableSlot** - Added scale control for occupants
2. **BoosterPackDraggable** - Added tap-to-shake functionality
3. **BoosterPackVisual** - Added progressive shake animations
4. **BoosterOpeningPage** - Complete flow implementation
5. **AlbumViewPage** - Updated to pass booster count
### **Flow Summary:**
1. Album page shows booster buttons → Click → Opens BoosterOpeningPage
2. Opening page shows draggable boosters in bottom-right
3. Player taps or drags booster to center slot
4. Player taps booster X times (configurable, default 3)
5. Booster disappears, 3 card backs appear
6. Player clicks cards to reveal them one-by-one
7. After all revealed → Show next booster OR close page
---
## 🏗️ Scene Setup Instructions
### **Step 1: Create the BoosterOpeningPage GameObject**
1. **In your scene hierarchy:**
- Create an empty GameObject named `BoosterOpeningPage`
- Add component: `BoosterOpeningPage` script
- Add component: `Canvas Group` (for fade transitions)
- Ensure it's a child of your Canvas
2. **Create UI Structure:**
```
BoosterOpeningPage (BoosterOpeningPage component)
├── Background (Image - optional dark overlay)
├── CloseButton (Button)
├── BottomRightContainer (SlotContainer)
│ ├── Slot_0 (DraggableSlot)
│ │ └── BoosterPack_0 (BoosterPackDraggable prefab instance)
│ ├── Slot_1 (DraggableSlot)
│ │ └── BoosterPack_1 (BoosterPackDraggable prefab instance)
│ └── Slot_2 (DraggableSlot)
│ └── BoosterPack_2 (BoosterPackDraggable prefab instance)
├── CenterSlot (DraggableSlot)
└── CardDisplayContainer (Empty RectTransform)
```
> **💡 Tip:** Boosters are parented directly to their starting slots! This makes authoring easier and the hierarchy cleaner. No separate container needed.
---
## 📐 Detailed Component Setup
### **A. Bottom-Right Slot Container**
**GameObject Setup:**
- Name: `BottomRightContainer`
- Component: `SlotContainer`
- Position: Bottom-right of screen (e.g., anchored to bottom-right)
- Recommended position: X=800, Y=-400 (adjust for your canvas size)
**SlotContainer Settings:**
- Layout Type: `Vertical` (or `Custom` for hand-authored positions - see custom_layout_guide.md)
- Spacing: `120` (ignored if Layout Type = Custom)
- Center Slots: ✓ (ignored if Layout Type = Custom)
- Auto Register Children: ✓
> **💡 Tip:** Want artistic, scattered slot positions? Set Layout Type to `Custom` and manually position each slot in the Scene View. See `custom_layout_guide.md` for details!
**Child Slots (Slot_0, Slot_1, Slot_2):**
- Add component: `DraggableSlot` to each
- Size: 150x200 (adjust to your booster size)
- Settings:
- Filter By Type: ✓
- Allowed Type Names: `BoosterPackDraggable`
- Apply Scale To Occupant: ☐ (leave normal size)
---
### **B. Center Opening Slot**
**GameObject Setup:**
- Name: `CenterSlot`
- Component: `DraggableSlot`
- Position: Center of screen (X=0, Y=0)
- Size: 300x400 (larger than boosters)
**DraggableSlot Settings:**
- Filter By Type: ✓
- Allowed Type Names: `BoosterPackDraggable`
- **Apply Scale To Occupant: ✓** ← Important!
- **Occupant Scale: (2, 2, 1)** ← Makes booster 2x bigger
- Scale Transition Duration: `0.3`
---
### **C. Card Display Container**
**GameObject Setup:**
- Name: `CardDisplayContainer`
- Component: `RectTransform` only
- Position: Center of screen (X=0, Y=-100)
- This is where revealed cards will spawn
---
### **D. Booster Pack Instances**
**Setup as Slot Children (Recommended):**
**Create a single BoosterPackPrefab with visual as a child:**
**BoosterPackPrefab Structure:**
```
BoosterPackPrefab
├── BoosterPackDraggable (component)
├── CanvasGroup (component - for raycast control)
└── Visual (child GameObject)
├── Canvas (component - for z-order control)
├── CanvasGroup (component - for alpha fading)
├── BoosterPackVisual (component)
├── ShakeParent (RectTransform - position/rotation punch effects)
│ └── TiltParent (RectTransform - continuous smooth rotation)
│ └── PackImage (Image - your booster sprite, raycastTarget = false)
└── GlowEffect (ParticleSystem - optional)
```
**BoosterPackDraggable Settings:**
- **Visual Prefab:** Leave empty (using child visual instead)
- **Instantiate Visual:** ☐ unchecked (child visual is auto-detected)
- Can Open On Drop: ☐
- Can Open On Double Click: ☐
- **Can Tap To Open: ✓**
- **Max Taps To Open: 3**
**BoosterPackVisual Settings (on the Visual child):**
- Canvas: Auto-assigned or drag the Canvas component
- Canvas Group: Auto-assigned or drag the CanvasGroup component
- Tilt Parent: Drag `TiltParent`
- Shake Parent: Drag `ShakeParent`
- Pack Image: Drag `PackImage`
- Pack Sprite: Assign your booster sprite asset
- Glow Effect: Optional ParticleSystem
- Glow Transform: Optional (for rotation)
> **📝 Simplified Approach!** The visual is now a **direct child** of the BoosterPackPrefab, making authoring much easier. The system auto-detects child visuals using `GetComponentInChildren<DraggableVisual>()`. This gives you:
> - ✅ Full visual authoring control in the prefab
> - ✅ See exactly what your booster looks like
> - ✅ No separate prefab files to manage
> - ✅ Simpler hierarchy and setup
>
> The visual still follows with smooth lerp/delay and all animations work the same!
> **📝 Note on Hierarchy:** ShakeParent and TiltParent are **nested RectTransforms** (ShakeParent → TiltParent → PackImage) so PackImage gets BOTH effects combined: shake (discrete position/rotation tweens) AND tilt (continuous smooth rotation). This nesting allows transform inheritance through Unity's parent-child chain.
> **📝 Note on Raycast Control:** The root BoosterPackPrefab uses CanvasGroup's `blocksRaycasts` for raycast toggling during drag. Visual children (PackImage, etc.) should have `raycastTarget = false` so clicks reach the root.
> **💡 Advanced Option:** If you prefer the old pattern (separate prefab files), you can still use it by assigning a prefab to `Visual Prefab` and checking `Instantiate Visual`. The system supports both patterns - it checks for a child visual first, then falls back to instantiation if configured.
3. **Add to Slots:**
- Drag BoosterPackPrefab as child of `Slot_0` → Name it `BoosterPack_0`
- Drag BoosterPackPrefab as child of `Slot_1` → Name it `BoosterPack_1`
- Drag BoosterPackPrefab as child of `Slot_2` → Name it `BoosterPack_2`
- Set each booster's **Active state to ☐ UNCHECKED** (starts inactive)
- Position: (0, 0) local position (centered in slot)
> **💡 Why Parent to Slots?** The code calls `AssignToSlot()` which reparents anyway, so starting as a child of the slot makes authoring cleaner and more visual. You can see exactly where boosters will appear!
---
### **E. Flippable Card Prefab**
**For now (placeholder):**
1. Create a simple prefab named `FlippableCardPrefab`
2. Structure:
```
FlippableCardPrefab
├── RectTransform (200x300)
├── CardDisplay (component)
├── Image (card back sprite initially)
└── Button (will be added at runtime if not present)
```
3. **Important:**
- The card should show a "back" sprite initially
- When clicked, `CardDisplay.SetupCard()` will be called
- In the future, add flip animation here
---
### **F. BoosterOpeningPage Component Configuration**
**In the Inspector:**
**UI References:**
- Canvas Group: Auto-assigned or drag the CanvasGroup
- Close Button: Drag your close button
**Booster Management:**
- Booster Pack Instances: **Array size = 3**
- Element 0: `BoosterPack_0`
- Element 1: `BoosterPack_1`
- Element 2: `BoosterPack_2`
- Bottom Right Slots: Drag `BottomRightContainer`
- Center Opening Slot: Drag `CenterSlot`
**Card Display:**
- Card Display Container: Drag `CardDisplayContainer`
- Flippable Card Prefab: Drag your card prefab
- Card Spacing: `150` (distance between cards)
**Settings:**
- Card Reveal Delay: `0.5`
- Booster Disappear Duration: `0.5`
---
## 🎨 Creating the Booster Pack Visual Prefab
### **Booster Visual Structure:**
```
BoosterVisual (BoosterPackVisual component)
├── Canvas (component - sort order controlled at runtime)
├── CanvasGroup (component - for fading)
├── ShakeParent (RectTransform - outer animation layer)
│ └── TiltParent (RectTransform - inner animation layer)
│ ├── PackImage (Image - your booster pack sprite)
│ └── FrameImage (Image - optional frame/border)
└── GlowEffect (Particle System - optional sparkles)
```
> **💡 Animation Chain:** ShakeParent contains TiltParent, which contains the visuals. This nesting means PackImage gets BOTH shake (position/rotation punch) AND tilt (smooth continuous rotation) effects combined through Unity's transform hierarchy. Both are RectTransforms since this is UI.
### **Component Settings:**
**BoosterPackVisual:**
- Canvas: Auto-assigned
- Canvas Group: Auto-assigned
- Tilt Parent: Drag `TiltParent`
- Shake Parent: Drag `ShakeParent`
- Pack Image: Drag the `PackImage`
- Pack Sprite: Assign your booster sprite asset
**Visual Animation Settings:**
- Follow Speed: `30`
- Rotation Amount: `20`
- Tilt Speed: `20`
- Use Idle Animation: ✓
- Idle Animation Speed: `1`
- Opening Scale Punch: `0.5`
- Opening Rotation Punch: `360`
- Opening Duration: `0.5`
---
## 🔗 Connecting to AlbumViewPage
**In your AlbumViewPage Inspector:**
- Booster Opening Page: Drag your `BoosterOpeningPage` GameObject
- Booster Pack Buttons: Array of booster button GameObjects (already configured)
**That's it!** The AlbumViewPage script already has the updated code to pass booster count.
---
## 🎮 Testing the Flow
### **Test Checklist:**
1. **Setup Test:**
- ✓ BoosterOpeningPage exists in scene
- ✓ All slots configured
- ✓ Booster instances assigned
- ✓ AlbumViewPage has reference to opening page
2. **Manual Booster Count:**
- In CardSystemManager, set initial booster count to 3
- Or use the editor tool to add boosters
3. **Test Flow:**
1. Open album → Should see 3 booster buttons
2. Click a booster button → Opens BoosterOpeningPage
3. Should see 3 boosters in bottom-right
4. **Drag Test:** Drag a booster to center → Should scale up 2x
5. **Tap Test:** Tap the booster 3 times:
- Tap 1: Small shake
- Tap 2: Medium shake
- Tap 3: Big shake → Booster disappears
6. Should see 3 card backs appear
7. Click each card → Should reveal (show CardDisplay)
8. After all revealed → Should show next booster OR close page
---
## ⚙️ Configuration Options
### **Adjusting Tap Count:**
- Select any BoosterPackDraggable
- Change `Max Taps To Open` (3 is default)
### **Adjusting Booster Cap:**
- In BoosterOpeningPage, change `Booster Pack Instances` array size
- Add/remove booster instances
- Bottom-right slots should match (add more Slot children)
### **Adjusting Center Slot Scale:**
- Select `CenterSlot`
- Change `Occupant Scale` (2,2,1 = 2x size)
### **Card Spacing:**
- In BoosterOpeningPage, adjust `Card Spacing` (150 default)
---
## 🐛 Troubleshooting
### **Problem: Boosters don't appear**
- Check: `Booster Pack Instances` array is filled
- Check: CardSystemManager has boosters available
- Check: AlbumViewPage passes booster count correctly
### **Problem: Can't drag booster to center**
- Check: CenterSlot has `Filter By Type` with `BoosterPackDraggable`
- Check: CenterSlot is not locked initially
- Check: Booster has Image with `raycastTarget = true` on base object
### **Problem: Tapping doesn't work**
- Check: BoosterPackDraggable has `Can Tap To Open` enabled
- Check: Booster is in the center slot (tap only works when slotted)
- Check: Visual children have `raycastTarget = false` so taps reach the base
### **Problem: Booster doesn't scale up in center**
- Check: CenterSlot has `Apply Scale To Occupant` enabled
- Check: `Occupant Scale` is set (e.g., 2,2,1)
### **Problem: Cards don't reveal**
- Check: `Flippable Card Prefab` is assigned
- Check: Prefab has CardDisplay component
- Check: CardDisplay can receive card data
---
## 🎨 Visual Polish (Optional Next Steps)
### **Enhance Card Flip:**
- Add rotation animation when revealing
- Use DOTween or Tween for 3D flip effect
- Particle effects on reveal
### **Booster Opening Effects:**
- More dramatic particles when opening
- Sound effects on taps and opening
- Screen shake on final tap
### **Transition Polish:**
- Boosters fly in on page open
- Cards fly out after revealing
- Smooth transitions between boosters
---
## 📝 Summary
**You now have:**
✅ Complete booster opening flow
✅ Tap-to-shake interaction (3 taps default)
✅ Drag-and-drop alternative
✅ Card reveal system (click to flip)
✅ Auto-progression to next booster
✅ Auto-close when no boosters left
✅ Scalable system (adjust array size for more boosters)
**Next Steps:**
1. Create your visual assets (booster sprites, card backs)
2. Set up the scene structure as outlined
3. Configure the BoosterOpeningPage component
4. Test the flow
5. Polish with animations and effects
**Need Help?**
- Reference the existing drag-and-drop documentation
- Check CardSystem documentation for card data structure
- Test individual components in isolation first
---
🎉 **Happy Booster Opening!** 🎉

View File

@@ -1,342 +0,0 @@
# Booster Pack Prefab Structure Guide
## 🎨 Complete Booster Pack Prefab Setup
This guide shows you exactly how to structure your booster pack prefabs for the opening system.
---
## 📦 BoosterPackDraggable Prefab Structure
```
BoosterPackPrefab (RectTransform)
├── [Components on Root]
│ ├── RectTransform (200x300 size recommended)
│ ├── Image (IMPORTANT: raycastTarget = TRUE - for clicking/dragging)
│ ├── BoosterPackDraggable (script)
│ └── Canvas Group (optional - for fading)
└── Visual (Child GameObject)
└── [BoosterPackVisual Prefab Instance]
```
---
## 🎭 BoosterPackVisual Prefab Structure
```
BoosterVisual (RectTransform)
├── [Components on Root]
│ ├── RectTransform (same size as parent)
│ ├── Canvas (will be controlled at runtime)
│ ├── CanvasGroup (for alpha transitions)
│ └── BoosterPackVisual (script)
├── TiltParent (Empty Transform)
│ │ [This rotates for 3D tilt effect]
│ │
│ ├── PackImage (Image)
│ │ ├── Sprite: Your booster pack sprite
│ │ ├── raycastTarget: FALSE
│ │ └── Size: Slightly smaller than parent
│ │
│ ├── FrameImage (Image - optional)
│ │ ├── Sprite: Border/frame decoration
│ │ └── raycastTarget: FALSE
│ │
│ └── RarityIndicator (Image - optional)
│ ├── Sprite: Rarity gem/star
│ └── raycastTarget: FALSE
├── ShakeParent (Empty Transform)
│ │ [This is used for shake offset]
│ └── (Currently empty, reserved for effects)
└── GlowEffect (Particle System - optional)
├── Shape: Circle
├── Start Size: 5-10
├── Start Color: Golden/sparkly
├── Emission Rate: 10-20
└── Renderer: Sort Order = 1 (above images)
```
---
## ⚙️ Component Configuration
### **BoosterPackDraggable Settings:**
```yaml
[Draggable Settings]
Move Speed: 50
Smooth Movement:
Snap Duration: 0.3
[Visual]
Visual Prefab: (Assign BoosterVisual prefab)
Instantiate Visual:
Visual Parent: (Leave empty - uses canvas)
[Selection]
Is Selectable:
Selection Offset: 50
[Booster Pack Settings]
Can Open On Drop:
Can Open On Double Click:
[Tap to Open]
Can Tap To Open:
Max Taps To Open: 3
```
### **BoosterPackVisual Settings:**
```yaml
[References]
Canvas: (Auto-assigned)
Canvas Group: (Auto-assigned)
Tilt Parent: (Drag TiltParent object)
Shake Parent: (Drag ShakeParent object)
[Follow Parameters]
Follow Speed: 30
Use Follow Delay:
[Rotation/Tilt Parameters]
Rotation Amount: 20
Rotation Speed: 20
Auto Tilt Amount: 30
Manual Tilt Amount: 20
Tilt Speed: 20
[Scale Parameters]
Use Scale Animations:
Scale On Hover: 1.15
Scale On Drag: 1.25
Scale Transition Duration: 0.15
[Idle Animation]
Use Idle Animation:
Idle Animation Speed: 1
[Booster Pack Visual]
Pack Image: (Drag PackImage)
Pack Sprite: (Assign your sprite asset)
Glow Effect: (Drag particle system if using)
Glow Transform: (Drag particle transform if using)
[Opening Animation]
Opening Scale Punch: 0.5
Opening Rotation Punch: 360
Opening Duration: 0.5
```
---
## 🎨 Creating in Unity (Step-by-Step)
### **Step 1: Create Root GameObject**
1. Right-click in Hierarchy → UI → Image
2. Name it: `BoosterPackPrefab`
3. Set Size: 200x300 (Width x Height)
4. Add sprite: Temporary placeholder or your booster sprite
5. **IMPORTANT:** Ensure `raycastTarget` is ✓ checked
### **Step 2: Add BoosterPackDraggable**
1. Add Component → `BoosterPackDraggable`
2. Configure settings (see above)
3. Leave Visual Prefab empty for now
### **Step 3: Create Visual Child**
1. Right-click BoosterPackPrefab → Create Empty
2. Name it: `BoosterVisual`
3. Add Component → `Canvas`
4. Add Component → `Canvas Group`
5. Add Component → `BoosterPackVisual`
### **Step 4: Create TiltParent**
1. Right-click BoosterVisual → Create Empty
2. Name it: `TiltParent`
3. Position: (0, 0, 0)
### **Step 5: Add Images under TiltParent**
1. Right-click TiltParent → UI → Image
2. Name it: `PackImage`
3. Assign your booster sprite
4. **Set raycastTarget to ☐ UNCHECKED**
5. Size: 180x280 (slightly smaller)
### **Step 6: Create ShakeParent**
1. Right-click BoosterVisual → Create Empty
2. Name it: `ShakeParent`
3. Position: (0, 0, 0)
### **Step 7: Add Glow Effect (Optional)**
1. Right-click BoosterVisual → Effects → Particle System
2. Name it: `GlowEffect`
3. Configure:
- Duration: 1
- Looping: ✓
- Start Lifetime: 1-2
- Start Speed: 0
- Start Size: 5-10
- Start Color: Gold/Yellow with alpha
- Shape: Circle, Radius: 1
- Emission: Rate over Time = 15
- Renderer: Material = Default Particle, Sort Order = 1
### **Step 8: Wire Up References**
1. Select `BoosterVisual`
2. In BoosterPackVisual component:
- Tilt Parent: Drag `TiltParent`
- Shake Parent: Drag `ShakeParent`
- Pack Image: Drag `PackImage`
- Pack Sprite: Assign sprite asset
- Glow Effect: Drag `GlowEffect` (if using)
### **Step 9: Make Prefab**
1. Drag `BoosterPackPrefab` to Project folder
2. Delete from scene
3. You now have a reusable prefab!
---
## 🎯 Instantiation in Scene
### **Option A: Prefab Instances (Recommended)**
In your BoosterOpeningPage scene:
1. Create empty GameObject: `BoosterInstances`
2. Drag 3 instances of your prefab into it
3. Name them: `BoosterPack_0`, `BoosterPack_1`, `BoosterPack_2`
4. Set all to Active = ☐ (unchecked)
5. Position doesn't matter - they'll be assigned to slots
### **Option B: Runtime Instantiation**
- Assign the prefab to BoosterOpeningPage
- Script will instantiate as needed
- (Not implemented yet, but easy to add)
---
## 🧪 Testing Your Prefab
### **Test 1: Visual Check**
1. Place one instance in scene (active)
2. Enter Play Mode
3. Should see: Sprite, idle wobble animation
4. Hover over it: Should scale up slightly
### **Test 2: Drag Test**
1. Create a SlotContainer with a slot
2. Try dragging the booster to the slot
3. Should snap smoothly
4. Visual should lerp-follow with delay
### **Test 3: Tap Test**
1. Place booster in a slot
2. Set Can Tap To Open = ✓, Max Taps = 3
3. Click 3 times
4. Should see increasing shakes
---
## 📐 Size Recommendations
### **For Mobile:**
- Booster: 200x300
- Center Slot Scale: 2x → 400x600
- Card: 180x250
### **For Desktop:**
- Booster: 250x350
- Center Slot Scale: 1.5x → 375x525
- Card: 200x280
### **Spacing:**
- Bottom slots: 120-150 units apart
- Cards: 150-200 units apart
---
## 🎨 Sprite Requirements
### **Booster Pack Sprite:**
- Recommended: 512x768 or 1024x1536
- Format: PNG with transparency
- Style: Vertical rectangle (2:3 ratio)
- Should have clear visual identity
### **Card Back Sprite:**
- Same aspect ratio as cards
- Clearly distinct from front
- Can match booster theme
---
## 🔧 Troubleshooting Prefabs
**Problem: Can't click/drag booster**
→ Check: Root Image has raycastTarget = ✓
→ Check: Visual children have raycastTarget = ☐
**Problem: Visual doesn't follow smoothly**
→ Check: BoosterPackVisual is initialized
→ Check: Follow Speed > 0, Use Follow Delay = ✓
**Problem: No shake animation**
→ Check: BoosterPackVisual subscribes to OnTapped
→ Check: TiltParent and ShakeParent are assigned
**Problem: Booster looks weird when dragging**
→ Check: TiltParent contains the images
→ Check: Rotation parameters are reasonable (10-30)
---
## 💡 Advanced Customization
### **Rarity-Based Visuals:**
Add different sprites or effects based on rarity:
```csharp
// In BoosterPackDraggable or Visual:
public enum BoosterRarity { Common, Rare, Legendary }
public BoosterRarity rarity;
// Change sprite/effects based on rarity
```
### **Opening Sequence:**
Customize the opening animation:
- Adjust Opening Scale Punch (0.5 default)
- Adjust Opening Rotation Punch (360 default)
- Add sound effects in `PlayOpeningAnimation()`
### **Hover Effects:**
Make boosters more responsive:
- Increase Scale On Hover (1.15 → 1.3)
- Add glow intensity on hover
- Tilt toward mouse more (Manual Tilt Amount)
---
## ✅ Final Checklist
Before using your prefab:
- [ ] Root has BoosterPackDraggable
- [ ] Root Image has raycastTarget = TRUE
- [ ] Visual child has BoosterPackVisual
- [ ] TiltParent exists and contains sprites
- [ ] ShakeParent exists
- [ ] All image children have raycastTarget = FALSE
- [ ] References are wired up in Visual
- [ ] Prefab is saved in Project
- [ ] Can Tap To Open = ✓
- [ ] Max Taps To Open = 3 (or your choice)
---
🎉 **You're ready to create beautiful, interactive booster packs!** 🎉

View File

@@ -1,340 +0,0 @@
# Apple Hills Card System Designer Playbook
This playbook is for designers working with the raritybased Card System in Apple Hills. It provides a highlevel architecture overview, fast TL;DR playbooks for common tasks (with code-first snippets), and deeper guidance on how the system fits together.
## Table of Contents
- [Architecture at a Glance](#architecture-at-a-glance)
- [TL;DR Playbooks (Code-First)](#tldr-playbooks-code-first)
- [Open Booster Packs](#open-booster-packs)
- [Grant a Specific Card](#grant-a-specific-card)
- [Query the Collection](#query-the-collection)
- [Configure Visuals by Code (optional)](#configure-visuals-by-code-optional)
- [Clear the Collection](#clear-the-collection)
- [Authoring with the Card Editor (No Manual Setup)](#authoring-with-the-card-editor-no-manual-setup)
- [What happens automatically (under the hood)](#what-happens-automatically-under-the-hood)
- [In-Depth Overview](#in-depth-overview)
- [Designer HowTos (Quick Reference)](#designer-how-tos-quick-reference)
- [Best Practices & Tips](#best-practices--tips)
- [Known Gaps](#known-gaps)
- [FAQ](#faq)
- [Technical Reference (Quick)](#technical-reference-quick)
- [Change Log](#change-log)
## Architecture at a Glance
The system is split into two main layers with clear responsibilities and a lightweight event bridge.
- Data Layer (`Assets/Scripts/Data/CardSystem`)
- `CardSystemManager` (singleton)
- Source of truth for all card definitions, booster logic, and player `CardInventory`.
- Emits events on collection changes and booster activity.
- `CardInventory`
- Players owned cards and booster pack counters.
- Utility APIs for filtering cards (by rarity, zone, etc.).
- `CardDefinition` (`ScriptableObject`)
- Authoring asset that defines a card (name, visuals, zone, rarity tiering).
- `CardData`
- Runtime instance of a card in the player collection (ID, rarity, copies owned, links back to `CardDefinition`).
- `CardVisualConfig` (`ScriptableObject`)
- Central mapping for rarity/zone → visual treatment (colors, frames, shapes).
- UI Layer (`Assets/Scripts/UI/CardSystem`)
- `CardAlbumUI`
- Entry point for the card UI (menu, album browser, booster opening).
- `UIPageController` + `UIPage`
- Stack-based navigation (push/pop) and shared transitions.
- Page Components
- `CardMenuPage` Card hub/menu
- `AlbumViewPage` Grid album view with filter/sort options
- `BoosterOpeningPage` Interactive three-card reveal
- `CardUIElement`
- Renders a single card using `CardData` and `CardVisualConfig`
- `BoosterNotificationDot`
- Shows available booster pack count
- Events (Data ↔ UI)
- `OnBoosterCountChanged`
- `OnBoosterOpened`
- `OnCardCollected`
- `OnCardRarityUpgraded`
- Lifecycle
- Initialize during boot: `BootCompletionService.RegisterInitAction(...)`
- `CardSystemManager.Instance` is the access point from UI and tools (e.g., `CardSystemTester`)
## TL;DR Playbooks (Code-First)
These are the most common tasks youll perform in code. Paste the snippets into a temporary test `MonoBehaviour`, a unit test, or hook them into your gameplay systems.
### Open Booster Packs
```csharp
using AppleHills.Data.CardSystem;
using UnityEngine;
public class BoosterOpenerSample : MonoBehaviour
{
private void Start()
{
// Subscribe to events (optional, for feedback/UI updates)
CardSystemManager.Instance.OnBoosterOpened += cards =>
{
Debug.Log($"Opened booster with {cards.Count} cards");
};
CardSystemManager.Instance.OnCardCollected += card =>
{
Debug.Log($"Collected: {card.Definition.Name} (rarity: {card.Rarity})");
};
CardSystemManager.Instance.OnCardRarityUpgraded += card =>
{
Debug.Log($"Upgraded rarity: {card.Definition.Name} -> {card.Rarity}");
};
// Give the player some boosters and open one
CardSystemManager.Instance.AddBoosterPack(1);
var revealed = CardSystemManager.Instance.OpenBoosterPack();
foreach (var cd in revealed)
{
Debug.Log($"Revealed: {cd.Definition.Name}");
}
}
}
```
### Grant a Specific Card
```csharp
using System.Linq;
using AppleHills.Data.CardSystem;
using UnityEngine;
public class GrantCardSample : MonoBehaviour
{
[SerializeField] private string cardName = "Apple Picker"; // example
private void Start()
{
var def = CardSystemManager.Instance
.GetAllCardDefinitions()
.FirstOrDefault(d => d.Name == cardName);
if (def == null)
{
Debug.LogWarning($"CardDefinition not found: {cardName}");
return;
}
var cardData = CardSystemManager.Instance.AddCardToInventory(def);
Debug.Log($"Granted: {cardData.Definition.Name} (copies: {cardData.CopiesOwned})");
}
}
```
### Query the Collection
```csharp
using AppleHills.Data.CardSystem;
using UnityEngine;
public class QueryCollectionSample : MonoBehaviour
{
private void Start()
{
var inv = CardSystemManager.Instance.GetCardInventory();
// All cards
var all = inv.GetAllCards();
Debug.Log($"Total cards owned: {all.Count}");
// By rarity (example)
var rares = inv.GetByRarity(CardRarity.Rare);
Debug.Log($"Rares: {rares.Count}");
// By zone (example)
var forest = inv.GetByZone(CardZone.Forest);
Debug.Log($"Forest: {forest.Count}");
// Counts (progression checks)
var byRarity = inv.CountByRarity();
var byZone = inv.CountByZone();
// Iterate dictionaries as needed
}
}
```
### Clear the Collection
```csharp
using AppleHills.Data.CardSystem;
using UnityEngine;
public class ClearCollectionSample : MonoBehaviour
{
private void Start()
{
CardSystemManager.Instance.ClearAllCards();
Debug.Log("Card collection cleared (dev/test only).");
}
}
```
## Authoring with the Card Editor (No Manual Setup)
Designers should use the dedicated editor window: `AppleHills/Card Editor`.
- Open via Unity menu: `AppleHills/Card Editor` (menu path defined in `CardEditorWindow`)
- The window provides a card list, detail editor, and a live preview of the UI card.
- You dont need to create folders or assets manually—the tool handles setup and discovery.
### What happens automatically (under the hood)
The `Assets/Editor/CardSystem/CardEditorWindow.cs` implements the following behaviors:
- Ensures the data directory exists: `Assets/Data/Cards` (creates it on first run).
- Discovers all `CardDefinition` assets under `Assets/Data/Cards` automatically via `AssetDatabase.FindAssets("t:CardDefinition")` and keeps the list sorted by `Name`.
- Creates new card assets as `Card_<Name>.asset` inside `Assets/Data/Cards`.
- Loads a UI preview prefab from `Assets/Prefabs/UI/Cards/SIngleCardDisplayUI.prefab` to render a live card preview inside the editor using `PreviewRenderUtility`.
- Tries to load `CardVisualConfig` at `Assets/Data/Cards/CardVisualConfig.asset` for consistent rarity/zone styling in the preview.
- Responds to Undo/Redo and Editor recompiles to stay in sync.
- When you duplicate or delete cards from the window, the corresponding assets are created/removed accordingly.
Result: Designers focus on content (name, description, zone, art, etc.) in the Card Editor. The system takes care of folders, discovery, previewing, and syncing definitions with runtime.
## In-Depth Overview
Data Layer Details
- `CardDefinition` (authoring)
- What it is: a `ScriptableObject` that defines a cards identity and static presentation (name, description, zone, default visuals).
- Typical fields: `Name`, `Description`, `Zone`, rarity metadata, sprites/textures, optional tags.
- Helpers: `CreateCardData()` to generate a runtime `CardData` instance for inventory.
- `CardData` (runtime instance)
- Purpose: Represents what the player owns unique ID, rarity state, copies owned.
- Behavior: When duplicates are collected, `CardData` may upgrade rarity (e.g., duplicate thresholds).
- Links: Holds reference to its `CardDefinition` for display and classification.
- `CardInventory` (player collection)
- Responsibilities: Store cards, track counts, support filters/lookups (by rarity/zone), manage booster counters.
- Common operations: `AddCard(def)`, `GetAllCards()`, `GetByRarity()`, `GetByZone()`, `CountByRarity()`, `CountByZone()`.
- Notes: Designed for efficiency and clarity; returns collections for the UI to display.
- `CardSystemManager` (singleton coordinator)
- Responsibilities: Owns `CardInventory`, holds the card definition catalog, controls booster generation/opening, exposes public APIs.
- Events: `OnBoosterCountChanged`, `OnBoosterOpened`, `OnCardCollected`, `OnCardRarityUpgraded`.
- Typical APIs:
- `GetAllCardDefinitions()``IReadOnlyList<CardDefinition>`
- `GetCardInventory()``CardInventory`
- `AddBoosterPack(int count)`
- `OpenBoosterPack()``List<CardData>` (or similar)
- `AddCardToInventory(CardDefinition def)`
- `ClearAllCards()`
- `CardVisualConfig` (visual mappings)
- Purpose: Central place to establish the brand/look per rarity and zone.
- Implementation: `ScriptableObject` with dictionaries for quick lookups by rarity/zone.
UI Layer Details
- `UIPageController` + `UIPage`
- Navigation: `PushPage(page)`, `PopPage()`, `Clear()`; optional transition animations.
- Pattern: Each page is a `UIPage` subclass registered under `CardAlbumUI`.
- `CardAlbumUI` (system entry)
- Role: Wires pages together, responds to `CardSystemManager` events, routes to `AlbumViewPage` or `BoosterOpeningPage`.
- `AlbumViewPage`
- Role: Displays a grid of `CardUIElement` items from `CardInventory` with filtering/sorting UI.
- Notes: Uses TextMeshPro components and simplified presentation (no stack/slot system).
- `BoosterOpeningPage`
- Role: Handles the interactive reveal flow for 3 cards per booster; flip animations, particle FX, rarity feedback.
- Notes: Timings and art are still under refinement; supports individual reveal clicks.
- `CardUIElement`
- Role: Displays a single `CardData` name, art, frame, rarity chips, zone styling.
- Data Source: Uses `CardVisualConfig` for color/frame mapping.
- `BoosterNotificationDot`
- Role: Indicates current booster count; listens to `OnBoosterCountChanged`.
Event Flow Examples
- Opening a booster
- Player code calls `CardSystemManager.OpenBoosterPack()` → generates/awards 3 cards → raises `OnBoosterOpened`.
- Inventory updated; for each card, `OnCardCollected` (and maybe `OnCardRarityUpgraded`) fires.
- `BoosterOpeningPage` animates reveals; `AlbumViewPage` updates when returning.
- Adding a card via quest reward
- Gameplay script calls `AddCardToInventory(def)`.
- Inventory updates; events fire to update UI.
Rarity & Duplicates
- The system tracks copies owned per card.
- Duplicates can trigger rarity upgrades based on thresholds (implemented in `CardData`).
- UI can react with special effects via `OnCardRarityUpgraded` (e.g., distinct VFX/SFX per rarity).
## Designer HowTos (Quick Reference)
Author a New Card (use the editor tool)
1) Open `AppleHills/Card Editor`.
2) Click New/Duplicate, edit `Name`, `Description`, `Zone`, and assign art.
3) Save—asset is created as `Assets/Data/Cards/Card_<Name>.asset`. Its auto-discovered by runtime.
Grant a Card by Code (quest reward)
```csharp
CardSystemManager.Instance.AddCardToInventory(rewardDefinition);
```
Open a Booster by Code
```csharp
CardSystemManager.Instance.AddBoosterPack(1);
CardSystemManager.Instance.OpenBoosterPack();
```
Filter Cards for a Page
```csharp
var inv = CardSystemManager.Instance.GetCardInventory();
var list = inv.GetByZone(CardZone.Quarry);
```
## Best Practices & Tips
- Always access data via `CardSystemManager.Instance` to keep a single source of truth.
- Keep `CardDefinition` assets lightweight and consistent; use tags/zones for filtering.
- When adding new rarities/zones, update `CardVisualConfig` and verify UI styling.
- For smoother reveals, coordinate with VFX/SFX to differentiate rarities.
- Large collections: consider paging or virtualized grids if performance becomes a concern.
## Known Gaps
- Save/Load not implemented test sessions wont persist player collection.
- Booster Opening polish (timings/art/audio) is ongoing.
- Collection statistics/achievements UI pending.
## FAQ
- Q: My new `CardDefinition` doesnt show up in the album.
- A: Ensure the asset is in `Assets/Data/Cards` (the editor tool places it there) and that definitions load before UI opens. Check the `CardSystemManager` discovery and your zones/rarities.
- Q: How do I trigger the card UI from another part of the game?
- A: Use `CardAlbumUI`s public entry points or route through your UI flow to push the relevant page via `UIPageController`.
- Q: Can I guarantee a specific rarity in a booster for a tutorial?
- A: Add a temporary tutorial hook in `CardSystemManager` to override booster generation rules for guided moments, or directly grant specific `CardDefinition`s for the tutorial step.
- Q: How do duplicates upgrade rarity?
- A: The logic lives in `CardData`; when copies cross thresholds, it fires `OnCardRarityUpgraded`. Coordinate with design to set thresholds and with UI to show feedback.
## Technical Reference (Quick)
- Initialize after boot: `BootCompletionService.RegisterInitAction(InitializePostBoot)`
- Data access: `CardSystemManager.Instance`
- Navigation: `UIPageController.Instance.PushPage()` / `PopPage()`
- Show a card in UI: `CardUIElement.SetupCard(cardData)`
- Test harness: `CardSystemTester` (add to scene, link `CardAlbumUI`)
## Change Log
- v1.1: Added Table of Contents, inline code formatting, code-first TL;DR playbooks, and an authoring section based on `CardEditorWindow`.
- v1.0: Initial designer playbook, aligned with Implementation Plan and Integration & Testing Guide.

View File

@@ -1,399 +0,0 @@
# 🎨 SlotContainer Custom Layout Guide
## 📋 Overview
The `SlotContainer` has a **Custom** layout mode that's perfect for artistic, hand-authored slot positions! When you select `Custom`, the SlotContainer **doesn't automatically position your slots** - it lets you manually position them however you want.
---
## 🎯 How Custom Layout Works
### **What Happens When Layout = Custom:**
Looking at the code:
```csharp
public void UpdateLayout()
{
if (layoutType == LayoutType.Custom)
{
OnLayoutChanged?.Invoke(); // Just fires the event
return; // Doesn't reposition anything!
}
// Other layouts (Horizontal, Vertical, Grid) reposition slots here...
}
```
**Key Point:** Custom layout means **"Don't touch my slot positions!"**
The SlotContainer still:
- ✅ Registers your slots
- ✅ Finds closest slot when dragging
- ✅ Manages slot occupancy
- ✅ Fires events
But it **does NOT:**
- ❌ Automatically position slots
- ❌ Apply spacing
- ❌ Center slots
- ❌ Use curves or grids
---
## 🛠️ Setting Up Custom Layout (Step-by-Step)
### **Method 1: Hand-Author in Scene (Recommended)**
This is the easiest and most visual approach:
1. **Create SlotContainer:**
```
BottomRightContainer
└── [Add Component: SlotContainer]
```
2. **Set Layout Type:**
- Layout Type: `Custom`
- Auto Register Children: ✓ (keep this checked)
3. **Create Slot Children:**
```
BottomRightContainer
├── Slot_0 (DraggableSlot)
├── Slot_1 (DraggableSlot)
├── Slot_2 (DraggableSlot)
└── Slot_3 (DraggableSlot)
```
4. **Position Slots Manually in Scene View:**
- Select `Slot_0`
- Drag it to artistic position (e.g., X=-150, Y=200)
- Select `Slot_1`
- Drag it to artistic position (e.g., X=50, Y=150)
- Select `Slot_2`
- Drag it to artistic position (e.g., X=-200, Y=50)
- Etc.
5. **Done!** Your slots stay exactly where you put them.
---
### **Method 2: Script Positions (For Procedural Scatter)**
If you want some randomness or code-controlled positions:
```csharp
using UnityEngine;
using UI.DragAndDrop.Core;
public class CustomSlotPositioner : MonoBehaviour
{
[SerializeField] private SlotContainer slotContainer;
[SerializeField] private Vector2[] handAuthoredPositions;
private void Start()
{
PositionSlots();
}
private void PositionSlots()
{
var slots = slotContainer.Slots;
for (int i = 0; i < slots.Count && i < handAuthoredPositions.Length; i++)
{
if (slots[i].RectTransform != null)
{
slots[i].RectTransform.anchoredPosition = handAuthoredPositions[i];
}
}
}
}
```
Then in Inspector:
- Hand Authored Positions (Array):
- Element 0: (-150, 200)
- Element 1: (50, 150)
- Element 2: (-200, 50)
- Etc.
---
### **Method 3: Subscribe to OnLayoutChanged Event**
For advanced custom positioning logic:
```csharp
using UnityEngine;
using UI.DragAndDrop.Core;
public class ArtisticSlotLayout : MonoBehaviour
{
[SerializeField] private SlotContainer slotContainer;
[SerializeField] private float scatterRadius = 200f;
[SerializeField] private bool useRandomSeed = true;
[SerializeField] private int seed = 42;
private void Awake()
{
if (slotContainer != null)
{
slotContainer.OnLayoutChanged += ApplyCustomLayout;
}
}
private void OnDestroy()
{
if (slotContainer != null)
{
slotContainer.OnLayoutChanged -= ApplyCustomLayout;
}
}
private void ApplyCustomLayout()
{
if (useRandomSeed)
Random.InitState(seed);
var slots = slotContainer.Slots;
for (int i = 0; i < slots.Count; i++)
{
if (slots[i].RectTransform != null)
{
// Scattered circular layout
float angle = Random.Range(0f, 360f);
float distance = Random.Range(0f, scatterRadius);
float x = Mathf.Cos(angle * Mathf.Deg2Rad) * distance;
float y = Mathf.Sin(angle * Mathf.Deg2Rad) * distance;
slots[i].RectTransform.anchoredPosition = new Vector2(x, y);
// Optional: Random rotation for extra artistic flair
slots[i].RectTransform.rotation = Quaternion.Euler(0, 0, Random.Range(-15f, 15f));
}
}
}
}
```
---
## 🎨 Example: Artistic Scattered Boosters
### **Scenario:** 3 booster slots scattered artistically in bottom-right
**Setup:**
```
BottomRightContainer (SlotContainer - Custom Layout)
├── Slot_0 (DraggableSlot)
│ └── Position: (-180, 220)
│ └── Rotation: (0, 0, -5) ← Slight tilt left
├── Slot_1 (DraggableSlot)
│ └── Position: (40, 180)
│ └── Rotation: (0, 0, 8) ← Slight tilt right
└── Slot_2 (DraggableSlot)
└── Position: (-220, 80)
└── Rotation: (0, 0, -3) ← Slight tilt left
```
**Visual Result:**
```
[Slot_0] ← Tilted left, higher
[Slot_1] ← Tilted right, middle
[Slot_2] ← Tilted left, lower
```
---
## ⚙️ Custom Layout Inspector Settings
When you select **Custom** layout type, these settings are **ignored**:
- ❌ Spacing (not used)
- ❌ Center Slots (not used)
- ❌ Use Curve Layout (not used)
- ❌ Position Curve (not used)
- ❌ Curve Height (not used)
These settings **still work**:
- ✅ Auto Register Children (still registers your slots)
- ✅ OnSlotAdded/OnSlotRemoved events (still fire)
- ✅ OnLayoutChanged event (fires when UpdateLayout is called)
---
## 🎯 Best Practices for Custom Layout
### **1. Use Scene View for Positioning**
- Easiest and most visual
- See results immediately
- Adjust in real-time
### **2. Add Visual Guides**
- Create a background image showing your intended layout
- Position slots over the image
- Delete/hide the guide after
### **3. Use Gizmos for Visualization**
```csharp
private void OnDrawGizmos()
{
if (slotContainer == null || slotContainer.Slots == null) return;
Gizmos.color = Color.yellow;
foreach (var slot in slotContainer.Slots)
{
if (slot != null)
{
Gizmos.DrawWireSphere(slot.transform.position, 50f);
}
}
}
```
### **4. Consider Spacing**
- Even with artistic scatter, avoid overlapping slots
- Leave enough space for hover/selection effects
- Test with different screen sizes
### **5. Save Positions in Prefab**
- Once you're happy with positions, save to prefab
- Ensures consistency across scenes
---
## 🔄 Combining Custom with Other Layouts
You can switch layout types at runtime:
```csharp
public class DynamicLayoutSwitcher : MonoBehaviour
{
[SerializeField] private SlotContainer slotContainer;
public void SwitchToScattered()
{
// Save current positions
Vector2[] savedPositions = new Vector2[slotContainer.SlotCount];
for (int i = 0; i < slotContainer.SlotCount; i++)
{
savedPositions[i] = slotContainer.GetSlotAtIndex(i).RectTransform.anchoredPosition;
}
// Switch to custom
// Note: You'd need to expose layoutType or use reflection
// For now, this is just conceptual
}
}
```
---
## 📋 Quick Setup Checklist
For your bottom-right booster slots:
1. **Create Container:**
```
BottomRightContainer
└── Layout Type: Custom
└── Auto Register Children: ✓
```
2. **Create 3 Slot Children:**
```
Slot_0, Slot_1, Slot_2
└── Each has DraggableSlot component
```
3. **Position Each Slot:**
- Use Scene View
- Click and drag to artistic positions
- Optional: Add slight rotation (Z-axis)
4. **Test:**
- Drag boosters to slots
- Verify they snap correctly
- Adjust positions as needed
5. **Save to Prefab:**
- Once happy, save BoosterOpeningPage prefab
- Positions are preserved
---
## 💡 Creative Ideas
### **Scattered Stack:**
```
Slot_0: (-200, 250) Rotation: -8°
Slot_1: (-180, 220) Rotation: 3°
Slot_2: (-210, 195) Rotation: -5°
```
→ Looks like a messy pile of cards!
### **Arc Formation:**
```
Slot_0: (-180, 200) Rotation: -15°
Slot_1: (0, 220) Rotation: 0°
Slot_2: (180, 200) Rotation: 15°
```
→ Gentle arc, like cards in hand
### **Diagonal Cascade:**
```
Slot_0: (-150, 250) Rotation: -10°
Slot_1: (-50, 180) Rotation: -5°
Slot_2: (50, 110) Rotation: 0°
```
→ Diagonal waterfall effect
---
## 🐛 Troubleshooting Custom Layout
**Problem: Slots keep resetting position**
→ Make sure Layout Type = `Custom`
→ Check if something is calling `UpdateLayout()` with a different type
**Problem: Slots don't register**
→ Ensure Auto Register Children = ✓
→ Verify slots are direct children of SlotContainer
**Problem: Can't drag boosters to slots**
→ Check DraggableSlot configuration (Filter By Type, etc.)
→ Verify slots aren't locked
**Problem: Positions lost on scene reload**
→ Save to prefab!
→ Check if positions are being set in Awake/Start
---
## 📝 Summary
**Custom Layout = Full Manual Control!**
- ✅ Position slots anywhere you want
- ✅ Add rotation for artistic flair
- ✅ No automatic repositioning
- ✅ Perfect for hand-authored layouts
- ✅ Still gets all SlotContainer benefits (registration, finding, events)
**Recommended Workflow:**
1. Set Layout Type = Custom
2. Create slot children
3. Position visually in Scene View
4. Test drag-and-drop
5. Adjust as needed
6. Save to prefab
---
🎨 **Now go create some beautiful, artistic slot layouts!** 🎨

View File

@@ -1,309 +0,0 @@
# 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<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:
```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<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.

File diff suppressed because it is too large Load Diff