412 lines
13 KiB
Markdown
412 lines
13 KiB
Markdown
|
|
# Card Prefab Assembly Guide
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
This guide walks you through creating the new Card prefab with state machine architecture while integrating with existing CardDisplay components.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Step 1: Create CardAnimationConfig Asset
|
||
|
|
|
||
|
|
1. In Unity Project window, right-click in `Assets/Data/CardSystem/`
|
||
|
|
2. Select **Create → AppleHills → Card Animation Config**
|
||
|
|
3. Name it `CardAnimationConfig`
|
||
|
|
4. Configure settings (these match your current FlippableCard settings):
|
||
|
|
- **Flip Animation**
|
||
|
|
- Flip Duration: `0.6`
|
||
|
|
- Flip Scale Punch: `1.1`
|
||
|
|
- **Enlarge Animation**
|
||
|
|
- Enlarged Scale: `2.5`
|
||
|
|
- Scale Duration: `0.3`
|
||
|
|
- **Hover Animation**
|
||
|
|
- Hover Height: `10`
|
||
|
|
- Hover Duration: `1.5`
|
||
|
|
- Hover Scale Multiplier: `1.05`
|
||
|
|
- **Drag Animation**
|
||
|
|
- Drag Scale: `1.1`
|
||
|
|
- Snap Duration: `0.4`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Step 2: Create Base Card Prefab
|
||
|
|
|
||
|
|
### Create Root GameObject
|
||
|
|
1. In Hierarchy, right-click → **Create Empty**
|
||
|
|
2. Name it `Card`
|
||
|
|
3. Add Component → **Rect Transform** (converts to UI element)
|
||
|
|
4. Set **Anchors** to center-middle
|
||
|
|
5. Set **Size** to `200 x 280` (standard card size)
|
||
|
|
|
||
|
|
### Add Core Components to Root
|
||
|
|
1. **Add CardContext component:**
|
||
|
|
- Click **Add Component** → search `CardContext`
|
||
|
|
- Leave references empty for now (will auto-find)
|
||
|
|
|
||
|
|
2. **Add CardAnimator component:**
|
||
|
|
- Click **Add Component** → search `CardAnimator`
|
||
|
|
- Drag `CardAnimationConfig` asset to **Config** field
|
||
|
|
|
||
|
|
3. **Add Card component:**
|
||
|
|
- Click **Add Component** → search `Card`
|
||
|
|
- Set **Initial State** to `IdleState`
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Step 3: Add CardDisplay (From Existing Prefab)
|
||
|
|
|
||
|
|
### Option A: If you have existing CardDisplay prefab
|
||
|
|
1. Drag `CardDisplay` prefab into hierarchy as child of `Card`
|
||
|
|
2. Position at `(0, 0, 0)` local position
|
||
|
|
3. Ensure it fills the card area
|
||
|
|
|
||
|
|
### Option B: Create CardDisplay from scratch
|
||
|
|
1. Right-click `Card` in hierarchy → **Create Empty**
|
||
|
|
2. Name it `CardDisplay`
|
||
|
|
3. Add Component → **Card Display** (your existing script)
|
||
|
|
4. Setup UI elements as children:
|
||
|
|
- Add **Image** for card image
|
||
|
|
- Add **TextMeshProUGUI** for card name
|
||
|
|
- Add **Image** for frame
|
||
|
|
- Add **Image** for overlay
|
||
|
|
- Add **Image** for background
|
||
|
|
- Add **Image** for zone shape
|
||
|
|
5. Assign these to CardDisplay component fields
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Step 4: Create State Machine Hierarchy
|
||
|
|
|
||
|
|
### Create StateMachine GameObject
|
||
|
|
1. Right-click `Card` → **Create Empty**
|
||
|
|
2. Name it `CardStateMachine`
|
||
|
|
3. Add Component → **Apple Machine** (from Pixelplacement)
|
||
|
|
4. Configure AppleMachine:
|
||
|
|
- **Verbose**: unchecked (unless debugging)
|
||
|
|
- **Allow Reentry**: unchecked
|
||
|
|
- **Return To Default On Disable**: checked
|
||
|
|
|
||
|
|
### Create State GameObjects (as children of CardStateMachine)
|
||
|
|
|
||
|
|
For each state, follow this pattern:
|
||
|
|
|
||
|
|
#### 1. IdleState
|
||
|
|
1. Right-click `CardStateMachine` → **Create Empty**
|
||
|
|
2. Name it `IdleState`
|
||
|
|
3. Add Component → **Card Idle State**
|
||
|
|
4. **No child visuals needed** for this state
|
||
|
|
|
||
|
|
#### 2. FlippingState
|
||
|
|
1. Right-click `CardStateMachine` → **Create Empty**
|
||
|
|
2. Name it `FlippingState`
|
||
|
|
3. Add Component → **Card Flipping State**
|
||
|
|
4. **Create CardBackVisual child:**
|
||
|
|
- Right-click `FlippingState` → **UI → Image**
|
||
|
|
- Name it `CardBackVisual`
|
||
|
|
- Set **Anchors** to stretch-stretch
|
||
|
|
- Set **Left/Right/Top/Bottom** to `0`
|
||
|
|
- Assign your card back sprite to **Source Image**
|
||
|
|
- Drag this to **Card Back Visual** field on CardFlippingState component
|
||
|
|
|
||
|
|
#### 3. RevealedState
|
||
|
|
1. Right-click `CardStateMachine` → **Create Empty**
|
||
|
|
2. Name it `RevealedState`
|
||
|
|
3. Add Component → **Card Revealed State**
|
||
|
|
4. **No child visuals needed** for this state
|
||
|
|
|
||
|
|
#### 4. EnlargedNewState
|
||
|
|
1. Right-click `CardStateMachine` → **Create Empty**
|
||
|
|
2. Name it `EnlargedNewState`
|
||
|
|
3. Add Component → **Card Enlarged New State**
|
||
|
|
4. **Create NewCardBadge child:**
|
||
|
|
- Right-click `EnlargedNewState` → **Create Empty**
|
||
|
|
- Name it `NewCardBadge`
|
||
|
|
- Add child **UI → Image** for badge background
|
||
|
|
- Add child **UI → TextMeshProUGUI** for "NEW CARD" text
|
||
|
|
- Position badge at top of card (e.g., Y offset +100)
|
||
|
|
- Drag parent `NewCardBadge` to **New Card Badge** field on CardEnlargedNewState
|
||
|
|
|
||
|
|
#### 5. EnlargedRepeatState
|
||
|
|
1. Right-click `CardStateMachine` → **Create Empty**
|
||
|
|
2. Name it `EnlargedRepeatState`
|
||
|
|
3. Add Component → **Card Enlarged Repeat State**
|
||
|
|
4. **Create ProgressBarUI child:**
|
||
|
|
- Right-click `EnlargedRepeatState` → **Create Empty**
|
||
|
|
- Name it `ProgressBarContainer`
|
||
|
|
- Add child **UI → Image** for background bar
|
||
|
|
- Add child **UI → Image** for fill bar (set Image Type to **Filled**)
|
||
|
|
- Add child **UI → TextMeshProUGUI** for "X/5" text
|
||
|
|
- Position at bottom of card (e.g., Y offset -100)
|
||
|
|
- Drag `ProgressBarContainer` to **Progress Bar Container** field
|
||
|
|
- Drag fill bar to **Progress Bar Fill** field
|
||
|
|
- Drag text to **Progress Text** field
|
||
|
|
- Set **Cards To Upgrade** to `5`
|
||
|
|
|
||
|
|
#### 6. DraggingState
|
||
|
|
1. Right-click `CardStateMachine` → **Create Empty**
|
||
|
|
2. Name it `DraggingState`
|
||
|
|
3. Add Component → **Card Dragging State**
|
||
|
|
4. Set **Drag Scale** to `1.1`
|
||
|
|
5. **No child visuals needed** for this state
|
||
|
|
|
||
|
|
#### 7. PlacedInSlotState
|
||
|
|
1. Right-click `CardStateMachine` → **Create Empty**
|
||
|
|
2. Name it `PlacedInSlotState`
|
||
|
|
3. Add Component → **Card Placed In Slot State**
|
||
|
|
4. **No child visuals needed** for this state
|
||
|
|
|
||
|
|
#### 8. AlbumEnlargedState
|
||
|
|
1. Right-click `CardStateMachine` → **Create Empty**
|
||
|
|
2. Name it `AlbumEnlargedState`
|
||
|
|
3. Add Component → **Card Album Enlarged State**
|
||
|
|
4. **No child visuals needed** for this state
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Step 5: Wire Up References
|
||
|
|
|
||
|
|
### On CardStateMachine (AppleMachine component)
|
||
|
|
1. Select `CardStateMachine` GameObject
|
||
|
|
2. Set **Default State** to `IdleState` GameObject (drag from hierarchy)
|
||
|
|
|
||
|
|
### On Card Root (Card component)
|
||
|
|
1. Select root `Card` GameObject
|
||
|
|
2. **Context** should auto-find CardContext component
|
||
|
|
3. **Animator** should auto-find CardAnimator component
|
||
|
|
4. **State Machine** should auto-find CardStateMachine/AppleMachine
|
||
|
|
5. If not auto-found, drag components manually
|
||
|
|
|
||
|
|
### On CardContext Component
|
||
|
|
1. Select root `Card` GameObject
|
||
|
|
2. In CardContext component:
|
||
|
|
- **Card Display**: Drag `CardDisplay` child GameObject
|
||
|
|
- **Card Animator**: Should auto-find on same GameObject
|
||
|
|
- **State Machine**: Should auto-find CardStateMachine child
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Step 6: Test the State Machine
|
||
|
|
|
||
|
|
### Test in Editor (Play Mode)
|
||
|
|
1. Select root `Card` in hierarchy
|
||
|
|
2. In Card component, call `SetupCard()` from inspector (you'll need test data)
|
||
|
|
3. Watch the state machine transition through states
|
||
|
|
4. Check **CardStateMachine → Current State** field to see active state
|
||
|
|
5. Click the card to trigger state transitions
|
||
|
|
|
||
|
|
### Debug Tips
|
||
|
|
- Enable **Verbose** on AppleMachine to see state change logs
|
||
|
|
- Watch hierarchy - active state GameObject will be enabled (blue icon)
|
||
|
|
- Inactive states will be disabled (gray icon)
|
||
|
|
- State-owned visuals (CardBackVisual, NewCardBadge, etc.) should activate/deactivate with their parent state
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Step 7: Save as Prefab
|
||
|
|
|
||
|
|
1. Drag the root `Card` GameObject from hierarchy to `Assets/Prefabs/UI/CardSystem/`
|
||
|
|
2. Name it `Card.prefab`
|
||
|
|
3. Delete the instance from hierarchy (prefab is saved)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Step 8: Integration with Existing Code
|
||
|
|
|
||
|
|
### Spawning New Cards (BoosterOpeningPage example)
|
||
|
|
|
||
|
|
**Old code:**
|
||
|
|
```csharp
|
||
|
|
GameObject cardObj = Instantiate(flippableCardPrefab, cardDisplayContainer);
|
||
|
|
FlippableCard card = cardObj.GetComponent<FlippableCard>();
|
||
|
|
card.SetupCard(cardData);
|
||
|
|
```
|
||
|
|
|
||
|
|
**New code:**
|
||
|
|
```csharp
|
||
|
|
GameObject cardObj = Instantiate(cardPrefab, cardDisplayContainer);
|
||
|
|
Card card = cardObj.GetComponent<Card>();
|
||
|
|
card.SetupForBoosterReveal(cardData, isNew: true);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Placing Cards in Album (AlbumViewPage example)
|
||
|
|
|
||
|
|
**Old code:**
|
||
|
|
```csharp
|
||
|
|
AlbumCard albumCard = Instantiate(albumCardPrefab, slot.transform);
|
||
|
|
albumCard.SetupCard(cardData);
|
||
|
|
albumCard.SetParentSlot(slot);
|
||
|
|
```
|
||
|
|
|
||
|
|
**New code:**
|
||
|
|
```csharp
|
||
|
|
Card card = Instantiate(cardPrefab, slot.transform);
|
||
|
|
card.SetupForAlbumSlot(cardData, slot);
|
||
|
|
```
|
||
|
|
|
||
|
|
### Listening to State Events
|
||
|
|
|
||
|
|
**Example - Listening for card reveal:**
|
||
|
|
```csharp
|
||
|
|
Card card = GetComponent<Card>();
|
||
|
|
var flippingState = card.GetStateComponent<CardFlippingState>("FlippingState");
|
||
|
|
|
||
|
|
// Subscribe to flip complete (you may need to add custom events)
|
||
|
|
// Or check current state:
|
||
|
|
if (card.GetCurrentStateName() == "RevealedState")
|
||
|
|
{
|
||
|
|
// Card was revealed
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Step 9: Create Prefab Variants (Optional)
|
||
|
|
|
||
|
|
You can create variants for different card contexts:
|
||
|
|
|
||
|
|
### BoosterCard Variant
|
||
|
|
1. Right-click `Card.prefab` → **Create → Prefab Variant**
|
||
|
|
2. Name it `BoosterCard.prefab`
|
||
|
|
3. Adjust initial state to `IdleState`
|
||
|
|
4. Customize appearance if needed
|
||
|
|
|
||
|
|
### AlbumCard Variant
|
||
|
|
1. Right-click `Card.prefab` → **Create → Prefab Variant**
|
||
|
|
2. Name it `AlbumCard.prefab`
|
||
|
|
3. Adjust initial state to `PlacedInSlotState`
|
||
|
|
4. Remove states not needed (like IdleState, FlippingState if cards are pre-placed)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Hierarchy Reference (Final Structure)
|
||
|
|
|
||
|
|
```
|
||
|
|
Card (RectTransform)
|
||
|
|
├─ [CardContext component]
|
||
|
|
├─ [CardAnimator component]
|
||
|
|
├─ [Card component]
|
||
|
|
├─ CardDisplay
|
||
|
|
│ ├─ CardImage (Image)
|
||
|
|
│ ├─ CardNameText (TextMeshProUGUI)
|
||
|
|
│ ├─ FrameImage (Image)
|
||
|
|
│ ├─ OverlayImage (Image)
|
||
|
|
│ ├─ BackgroundImage (Image)
|
||
|
|
│ └─ ZoneShapeImage (Image)
|
||
|
|
└─ CardStateMachine
|
||
|
|
├─ [AppleMachine component]
|
||
|
|
├─ IdleState
|
||
|
|
│ └─ [CardIdleState component]
|
||
|
|
├─ FlippingState
|
||
|
|
│ ├─ [CardFlippingState component]
|
||
|
|
│ └─ CardBackVisual (Image)
|
||
|
|
├─ RevealedState
|
||
|
|
│ └─ [CardRevealedState component]
|
||
|
|
├─ EnlargedNewState
|
||
|
|
│ ├─ [CardEnlargedNewState component]
|
||
|
|
│ └─ NewCardBadge
|
||
|
|
│ ├─ BadgeBackground (Image)
|
||
|
|
│ └─ BadgeText (TextMeshProUGUI)
|
||
|
|
├─ EnlargedRepeatState
|
||
|
|
│ ├─ [CardEnlargedRepeatState component]
|
||
|
|
│ └─ ProgressBarContainer
|
||
|
|
│ ├─ BarBackground (Image)
|
||
|
|
│ ├─ BarFill (Image - Filled type)
|
||
|
|
│ └─ CountText (TextMeshProUGUI)
|
||
|
|
├─ DraggingState
|
||
|
|
│ └─ [CardDraggingState component]
|
||
|
|
├─ PlacedInSlotState
|
||
|
|
│ └─ [CardPlacedInSlotState component]
|
||
|
|
└─ AlbumEnlargedState
|
||
|
|
└─ [CardAlbumEnlargedState component]
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Issue: States not transitioning
|
||
|
|
- **Check:** AppleMachine **Default State** is assigned
|
||
|
|
- **Check:** State names match exactly (case-sensitive: "IdleState" not "idlestate")
|
||
|
|
- **Check:** Enable **Verbose** on AppleMachine to see logs
|
||
|
|
|
||
|
|
### Issue: Visuals not showing
|
||
|
|
- **Check:** State-owned visuals (CardBackVisual, etc.) are assigned in state components
|
||
|
|
- **Check:** Images have sprites assigned
|
||
|
|
- **Check:** RectTransforms are sized properly (not 0x0)
|
||
|
|
|
||
|
|
### Issue: CardContext references null
|
||
|
|
- **Check:** CardDisplay is assigned on CardContext component
|
||
|
|
- **Check:** CardAnimator has CardAnimationConfig asset assigned
|
||
|
|
- **Check:** All components are on correct GameObjects
|
||
|
|
|
||
|
|
### Issue: Animations not playing
|
||
|
|
- **Check:** CardAnimationConfig asset is assigned to CardAnimator
|
||
|
|
- **Check:** Tween system (Pixelplacement) is in project
|
||
|
|
- **Check:** Config values are not zero
|
||
|
|
|
||
|
|
### Issue: Click not working
|
||
|
|
- **Check:** Canvas has **Graphic Raycaster** component
|
||
|
|
- **Check:** EventSystem exists in scene
|
||
|
|
- **Check:** Card has **CanvasGroup** with **Blocks Raycasts** enabled (or Image component)
|
||
|
|
- **Check:** State components implement IPointerClickHandler (IdleState, RevealedState, etc.)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Migration Checklist
|
||
|
|
|
||
|
|
- [ ] Created CardAnimationConfig asset
|
||
|
|
- [ ] Created base Card prefab with all components
|
||
|
|
- [ ] Created all 8 state GameObjects under CardStateMachine
|
||
|
|
- [ ] Assigned state-owned visuals (CardBackVisual, NewCardBadge, ProgressBarUI)
|
||
|
|
- [ ] Wired up all component references
|
||
|
|
- [ ] Tested state transitions in Play mode
|
||
|
|
- [ ] Saved as prefab
|
||
|
|
- [ ] Updated BoosterOpeningPage to use new Card prefab
|
||
|
|
- [ ] Updated AlbumViewPage to use new Card prefab
|
||
|
|
- [ ] Tested booster opening flow
|
||
|
|
- [ ] Tested album placement flow
|
||
|
|
- [ ] Tested enlarge/shrink interactions
|
||
|
|
- [ ] (Optional) Deprecated old prefabs (FlippableCard, AlbumCard, etc.)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Next Steps After Prefab Creation
|
||
|
|
|
||
|
|
1. **Test Booster Opening Flow:**
|
||
|
|
- Open BoosterOpeningPage scene
|
||
|
|
- Replace FlippableCard prefab references with Card prefab
|
||
|
|
- Test pack opening, card reveal, new/repeat states
|
||
|
|
|
||
|
|
2. **Test Album Flow:**
|
||
|
|
- Open AlbumViewPage scene
|
||
|
|
- Replace AlbumCard prefab references with Card prefab
|
||
|
|
- Test placing cards in slots, enlarging from album
|
||
|
|
|
||
|
|
3. **Performance Testing:**
|
||
|
|
- Spawn 10+ cards at once
|
||
|
|
- Check frame rate, tween performance
|
||
|
|
- Verify no memory leaks from state transitions
|
||
|
|
|
||
|
|
4. **Clean Up Old Code:**
|
||
|
|
- Once new system is stable, deprecate:
|
||
|
|
- `FlippableCard.cs`
|
||
|
|
- `AlbumCard.cs`
|
||
|
|
- `AlbumCardPlacementDraggable.cs` (if fully replaced)
|
||
|
|
- Keep `CardDisplay.cs` (still used!)
|
||
|
|
- Archive old prefabs for reference
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Success Criteria
|
||
|
|
|
||
|
|
✅ Card prefab created with 8 functional states
|
||
|
|
✅ State transitions work (Idle → Flipping → Revealed, etc.)
|
||
|
|
✅ State-owned visuals activate/deactivate automatically
|
||
|
|
✅ Animations play correctly (flip, enlarge, hover)
|
||
|
|
✅ Click interactions work in all states
|
||
|
|
✅ Integration with BoosterOpeningPage works
|
||
|
|
✅ Integration with AlbumViewPage works
|
||
|
|
✅ No console errors during state transitions
|
||
|
|
✅ Performance is acceptable (60fps with multiple cards)
|
||
|
|
|
||
|
|
Once all criteria met, you have successfully migrated to the new card system! 🎉
|
||
|
|
|