400 lines
9.6 KiB
Markdown
400 lines
9.6 KiB
Markdown
# 🎨 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!** 🎨
|
|
|