9.6 KiB
🎨 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:
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:
-
Create SlotContainer:
BottomRightContainer └── [Add Component: SlotContainer] -
Set Layout Type:
- Layout Type:
Custom - Auto Register Children: ✓ (keep this checked)
- Layout Type:
-
Create Slot Children:
BottomRightContainer ├── Slot_0 (DraggableSlot) ├── Slot_1 (DraggableSlot) ├── Slot_2 (DraggableSlot) └── Slot_3 (DraggableSlot) -
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.
- Select
-
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:
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:
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
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:
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:
-
Create Container:
BottomRightContainer └── Layout Type: Custom └── Auto Register Children: ✓ -
Create 3 Slot Children:
Slot_0, Slot_1, Slot_2 └── Each has DraggableSlot component -
Position Each Slot:
- Use Scene View
- Click and drag to artistic positions
- Optional: Add slight rotation (Z-axis)
-
Test:
- Drag boosters to slots
- Verify they snap correctly
- Adjust positions as needed
-
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:
- Set Layout Type = Custom
- Create slot children
- Position visually in Scene View
- Test drag-and-drop
- Adjust as needed
- Save to prefab
🎨 Now go create some beautiful, artistic slot layouts! 🎨