Compare commits
7 Commits
d4f6505c85
...
0eb1e0efd5
| Author | SHA1 | Date | |
|---|---|---|---|
| 0eb1e0efd5 | |||
|
|
7862af7f8b | ||
|
|
2d10d92bf5 | ||
|
|
4e0c9cb4c4 | ||
|
|
95daea8d34 | ||
|
|
b6d8586eab | ||
| 5792c01908 |
@@ -1,369 +1,267 @@
|
||||
// using UnityEditor;
|
||||
// using UnityEngine;
|
||||
// using AppleHills.Data.CardSystem;
|
||||
// using Data.CardSystem;
|
||||
// using Core;
|
||||
// using UI.CardSystem;
|
||||
// using UnityEngine.UI;
|
||||
// using System.Collections.Generic;
|
||||
//
|
||||
// namespace Editor.CardSystem
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// Editor window for testing the Card System in play mode.
|
||||
// /// Provides buttons to test core functionalities like adding booster packs, opening packs, and generating cards.
|
||||
// /// </summary>
|
||||
// public class CardSystemTesterWindow : EditorWindow
|
||||
// {
|
||||
// // Test Settings
|
||||
// private int boosterPacksToAdd = 3;
|
||||
// private int cardsToGenerate = 10;
|
||||
// private bool autoOpenPacksWhenAdded = false;
|
||||
//
|
||||
// // Debug Info
|
||||
// private int currentBoosterCount;
|
||||
// private int totalCardsInCollection;
|
||||
// private string lastActionMessage = "";
|
||||
//
|
||||
// // UI State
|
||||
// private Vector2 scrollPosition;
|
||||
// private CardAlbumUI cachedCardAlbumUI;
|
||||
//
|
||||
// [MenuItem("AppleHills/Card System Tester")]
|
||||
// public static void ShowWindow()
|
||||
// {
|
||||
// var window = GetWindow<CardSystemTesterWindow>(false, "Card System Tester", true);
|
||||
// window.minSize = new Vector2(400, 500);
|
||||
// }
|
||||
//
|
||||
// private void OnEnable()
|
||||
// {
|
||||
// EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
// }
|
||||
//
|
||||
// private void OnDisable()
|
||||
// {
|
||||
// EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
// }
|
||||
//
|
||||
// private void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
// {
|
||||
// if (state == PlayModeStateChange.EnteredPlayMode)
|
||||
// {
|
||||
// cachedCardAlbumUI = null;
|
||||
// RefreshDebugInfo();
|
||||
// }
|
||||
// else if (state == PlayModeStateChange.ExitingPlayMode)
|
||||
// {
|
||||
// cachedCardAlbumUI = null;
|
||||
// lastActionMessage = "";
|
||||
// }
|
||||
//
|
||||
// Repaint();
|
||||
// }
|
||||
//
|
||||
// private void OnGUI()
|
||||
// {
|
||||
// scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
//
|
||||
// // Header
|
||||
// EditorGUILayout.Space(10);
|
||||
// EditorGUILayout.LabelField("Card System Tester", EditorStyles.boldLabel);
|
||||
// EditorGUILayout.HelpBox("This tool allows you to test the card system in play mode. " +
|
||||
// "Enter play mode to enable the testing functions.", MessageType.Info);
|
||||
//
|
||||
// EditorGUILayout.Space(10);
|
||||
//
|
||||
// // Test Settings Section
|
||||
// DrawTestSettings();
|
||||
//
|
||||
// EditorGUILayout.Space(10);
|
||||
//
|
||||
// // Debug Info Section
|
||||
// DrawDebugInfo();
|
||||
//
|
||||
// EditorGUILayout.Space(10);
|
||||
//
|
||||
// // Test Actions Section
|
||||
// DrawTestActions();
|
||||
//
|
||||
// EditorGUILayout.EndScrollView();
|
||||
// }
|
||||
//
|
||||
// private void DrawTestSettings()
|
||||
// {
|
||||
// EditorGUILayout.LabelField("Test Settings", EditorStyles.boldLabel);
|
||||
//
|
||||
// EditorGUI.BeginDisabledGroup(!Application.isPlaying);
|
||||
//
|
||||
// boosterPacksToAdd = EditorGUILayout.IntSlider("Booster Packs to Add", boosterPacksToAdd, 1, 10);
|
||||
// cardsToGenerate = EditorGUILayout.IntSlider("Cards to Generate", cardsToGenerate, 1, 100);
|
||||
// autoOpenPacksWhenAdded = EditorGUILayout.Toggle("Auto-Open Packs When Added", autoOpenPacksWhenAdded);
|
||||
//
|
||||
// EditorGUI.EndDisabledGroup();
|
||||
// }
|
||||
//
|
||||
// private void DrawDebugInfo()
|
||||
// {
|
||||
// EditorGUILayout.LabelField("Debug Info", EditorStyles.boldLabel);
|
||||
//
|
||||
// if (Application.isPlaying)
|
||||
// {
|
||||
// EditorGUI.BeginDisabledGroup(true);
|
||||
// EditorGUILayout.IntField("Current Booster Count", currentBoosterCount);
|
||||
// EditorGUILayout.IntField("Total Cards in Collection", totalCardsInCollection);
|
||||
// EditorGUI.EndDisabledGroup();
|
||||
//
|
||||
// if (!string.IsNullOrEmpty(lastActionMessage))
|
||||
// {
|
||||
// EditorGUILayout.Space(5);
|
||||
// EditorGUILayout.HelpBox(lastActionMessage, MessageType.None);
|
||||
// }
|
||||
//
|
||||
// EditorGUILayout.Space(5);
|
||||
// if (GUILayout.Button("Refresh Debug Info"))
|
||||
// {
|
||||
// RefreshDebugInfo();
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// EditorGUILayout.HelpBox("Debug info available in play mode.", MessageType.Warning);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void DrawTestActions()
|
||||
// {
|
||||
// EditorGUILayout.LabelField("Test Actions", EditorStyles.boldLabel);
|
||||
//
|
||||
// if (!Application.isPlaying)
|
||||
// {
|
||||
// EditorGUILayout.HelpBox("Enter Play Mode to use these testing functions.", MessageType.Warning);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Booster Pack Actions
|
||||
// EditorGUILayout.Space(5);
|
||||
// EditorGUILayout.LabelField("Booster Pack Actions", EditorStyles.miniBoldLabel);
|
||||
//
|
||||
// if (GUILayout.Button("Add Booster Packs", GUILayout.Height(30)))
|
||||
// {
|
||||
// AddBoosterPacks();
|
||||
// }
|
||||
//
|
||||
// if (GUILayout.Button("Open Card Menu", GUILayout.Height(30)))
|
||||
// {
|
||||
// SimulateBackpackClick();
|
||||
// }
|
||||
//
|
||||
// if (GUILayout.Button("Open Booster Pack", GUILayout.Height(30)))
|
||||
// {
|
||||
// OpenBoosterPack();
|
||||
// }
|
||||
//
|
||||
// if (GUILayout.Button("Open Album View", GUILayout.Height(30)))
|
||||
// {
|
||||
// OpenAlbumView();
|
||||
// }
|
||||
//
|
||||
// // Card Generation Actions
|
||||
// EditorGUILayout.Space(10);
|
||||
// EditorGUILayout.LabelField("Card Generation Actions", EditorStyles.miniBoldLabel);
|
||||
//
|
||||
// if (GUILayout.Button("Generate Random Cards", GUILayout.Height(30)))
|
||||
// {
|
||||
// GenerateRandomCards();
|
||||
// }
|
||||
//
|
||||
// EditorGUILayout.Space(5);
|
||||
//
|
||||
// // Danger Zone
|
||||
// EditorGUILayout.Space(10);
|
||||
// EditorGUILayout.LabelField("Danger Zone", EditorStyles.miniBoldLabel);
|
||||
//
|
||||
// GUI.backgroundColor = new Color(1f, 0.6f, 0.6f);
|
||||
// if (GUILayout.Button("Clear All Cards", GUILayout.Height(30)))
|
||||
// {
|
||||
// if (EditorUtility.DisplayDialog("Clear All Cards",
|
||||
// "Are you sure you want to clear all cards from the inventory? This cannot be undone in this play session.",
|
||||
// "Clear All", "Cancel"))
|
||||
// {
|
||||
// ClearAllCards();
|
||||
// }
|
||||
// }
|
||||
// GUI.backgroundColor = Color.white;
|
||||
// }
|
||||
//
|
||||
// // Refresh the debug information
|
||||
// private void RefreshDebugInfo()
|
||||
// {
|
||||
// if (!Application.isPlaying)
|
||||
// return;
|
||||
//
|
||||
// if (CardSystemManager.Instance != null)
|
||||
// {
|
||||
// currentBoosterCount = CardSystemManager.Instance.GetBoosterPackCount();
|
||||
// totalCardsInCollection = CardSystemManager.Instance.GetCardInventory().GetAllCards().Count;
|
||||
// Repaint();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private CardAlbumUI GetCardAlbumUI()
|
||||
// {
|
||||
// if (cachedCardAlbumUI == null)
|
||||
// {
|
||||
// cachedCardAlbumUI = Object.FindAnyObjectByType<CardAlbumUI>();
|
||||
//
|
||||
// if (cachedCardAlbumUI == null)
|
||||
// {
|
||||
// lastActionMessage = "Error: No CardAlbumUI found in the scene!";
|
||||
// Debug.LogError("[CardSystemTesterWindow] " + lastActionMessage);
|
||||
// Repaint();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return cachedCardAlbumUI;
|
||||
// }
|
||||
//
|
||||
// // Test Action Methods
|
||||
//
|
||||
// private void AddBoosterPacks()
|
||||
// {
|
||||
// if (CardSystemManager.Instance != null)
|
||||
// {
|
||||
// CardSystemManager.Instance.AddBoosterPack(boosterPacksToAdd);
|
||||
// lastActionMessage = $"Added {boosterPacksToAdd} booster pack(s)";
|
||||
// Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
// RefreshDebugInfo();
|
||||
//
|
||||
// if (autoOpenPacksWhenAdded && GetCardAlbumUI() != null)
|
||||
// {
|
||||
// SimulateBackpackClick();
|
||||
// cachedCardAlbumUI.OpenBoosterPack();
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// lastActionMessage = "Error: CardSystemManager instance not found!";
|
||||
// Debug.LogError("[CardSystemTesterWindow] " + lastActionMessage);
|
||||
// Repaint();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void SimulateBackpackClick()
|
||||
// {
|
||||
// CardAlbumUI cardAlbumUI = GetCardAlbumUI();
|
||||
//
|
||||
// if (cardAlbumUI != null)
|
||||
// {
|
||||
// if (cardAlbumUI.BackpackIcon != null)
|
||||
// {
|
||||
// Button backpackButton = cardAlbumUI.BackpackIcon.GetComponent<Button>();
|
||||
// if (backpackButton != null)
|
||||
// {
|
||||
// backpackButton.onClick.Invoke();
|
||||
// lastActionMessage = "Opened card menu via backpack click";
|
||||
// Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// lastActionMessage = "Failed to find Button component on backpack icon";
|
||||
// Logging.Warning($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// lastActionMessage = "BackpackIcon reference is null";
|
||||
// Logging.Warning($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
// }
|
||||
//
|
||||
// Repaint();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void OpenBoosterPack()
|
||||
// {
|
||||
// CardAlbumUI cardAlbumUI = GetCardAlbumUI();
|
||||
//
|
||||
// if (cardAlbumUI != null)
|
||||
// {
|
||||
// SimulateBackpackClick();
|
||||
// cardAlbumUI.OpenBoosterPack();
|
||||
// lastActionMessage = "Opening booster pack";
|
||||
// Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
// RefreshDebugInfo();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void OpenAlbumView()
|
||||
// {
|
||||
// CardAlbumUI cardAlbumUI = GetCardAlbumUI();
|
||||
//
|
||||
// if (cardAlbumUI != null)
|
||||
// {
|
||||
// SimulateBackpackClick();
|
||||
// cardAlbumUI.OpenAlbumView();
|
||||
// lastActionMessage = "Opening album view";
|
||||
// Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
// Repaint();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void GenerateRandomCards()
|
||||
// {
|
||||
// if (CardSystemManager.Instance != null)
|
||||
// {
|
||||
// int cardsAdded = 0;
|
||||
// List<CardDefinition> allDefinitions = CardSystemManager.Instance.GetAllCardDefinitions();
|
||||
//
|
||||
// if (allDefinitions.Count == 0)
|
||||
// {
|
||||
// lastActionMessage = "Error: No card definitions available";
|
||||
// Logging.Warning($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
// Repaint();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// for (int i = 0; i < cardsToGenerate; i++)
|
||||
// {
|
||||
// // Get a random card definition
|
||||
// CardDefinition randomDef = allDefinitions[Random.Range(0, allDefinitions.Count)];
|
||||
//
|
||||
// // Create a card data instance and add it to inventory
|
||||
// CardData newCard = randomDef.CreateCardData();
|
||||
// CardSystemManager.Instance.GetCardInventory().AddCard(newCard);
|
||||
// cardsAdded++;
|
||||
// }
|
||||
//
|
||||
// lastActionMessage = $"Generated {cardsAdded} random cards";
|
||||
// Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
// RefreshDebugInfo();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// lastActionMessage = "Error: CardSystemManager instance not found!";
|
||||
// Debug.LogError("[CardSystemTesterWindow] " + lastActionMessage);
|
||||
// Repaint();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void ClearAllCards()
|
||||
// {
|
||||
// if (CardSystemManager.Instance != null)
|
||||
// {
|
||||
// int count = CardSystemManager.Instance.GetCardInventory().GetAllCards().Count;
|
||||
// CardSystemManager.Instance.GetCardInventory().ClearAllCards();
|
||||
// lastActionMessage = $"Cleared {count} cards from inventory";
|
||||
// Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
// RefreshDebugInfo();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// lastActionMessage = "Error: CardSystemManager instance not found!";
|
||||
// Debug.LogError("[CardSystemTesterWindow] " + lastActionMessage);
|
||||
// Repaint();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using AppleHills.Data.CardSystem;
|
||||
using Data.CardSystem;
|
||||
using Core;
|
||||
using UI.CardSystem;
|
||||
using UnityEngine.UI;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Editor.CardSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Editor window for testing the Card System in play mode.
|
||||
/// Provides buttons to test core functionalities like adding booster packs, opening packs, and generating cards.
|
||||
/// </summary>
|
||||
public class CardSystemTesterWindow : EditorWindow
|
||||
{
|
||||
// Test Settings
|
||||
private int boosterPacksToAdd = 3;
|
||||
private int cardsToGenerate = 10;
|
||||
|
||||
// Debug Info
|
||||
private int currentBoosterCount;
|
||||
private int totalCardsInCollection;
|
||||
private string lastActionMessage = "";
|
||||
|
||||
// UI State
|
||||
private Vector2 scrollPosition;
|
||||
|
||||
[MenuItem("AppleHills/Card System Tester")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
var window = GetWindow<CardSystemTesterWindow>(false, "Card System Tester", true);
|
||||
window.minSize = new Vector2(400, 500);
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
|
||||
}
|
||||
|
||||
private void OnPlayModeStateChanged(PlayModeStateChange state)
|
||||
{
|
||||
if (state == PlayModeStateChange.EnteredPlayMode)
|
||||
{
|
||||
RefreshDebugInfo();
|
||||
}
|
||||
else if (state == PlayModeStateChange.ExitingPlayMode)
|
||||
{
|
||||
lastActionMessage = "";
|
||||
}
|
||||
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
|
||||
|
||||
// Header
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField("Card System Tester", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox("This tool allows you to test the card system in play mode. " +
|
||||
"Enter play mode to enable the testing functions.", MessageType.Info);
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Test Settings Section
|
||||
DrawTestSettings();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Debug Info Section
|
||||
DrawDebugInfo();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Test Actions Section
|
||||
DrawTestActions();
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void DrawTestSettings()
|
||||
{
|
||||
EditorGUILayout.LabelField("Test Settings", EditorStyles.boldLabel);
|
||||
|
||||
EditorGUI.BeginDisabledGroup(!Application.isPlaying);
|
||||
|
||||
boosterPacksToAdd = EditorGUILayout.IntSlider("Booster Packs to Add", boosterPacksToAdd, 1, 10);
|
||||
cardsToGenerate = EditorGUILayout.IntSlider("Cards to Generate", cardsToGenerate, 1, 100);
|
||||
|
||||
EditorGUI.EndDisabledGroup();
|
||||
}
|
||||
|
||||
private void DrawDebugInfo()
|
||||
{
|
||||
EditorGUILayout.LabelField("Debug Info", EditorStyles.boldLabel);
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
EditorGUI.BeginDisabledGroup(true);
|
||||
EditorGUILayout.IntField("Current Booster Count", currentBoosterCount);
|
||||
EditorGUILayout.IntField("Total Cards in Collection", totalCardsInCollection);
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
if (!string.IsNullOrEmpty(lastActionMessage))
|
||||
{
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.HelpBox(lastActionMessage, MessageType.None);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
if (GUILayout.Button("Refresh Debug Info"))
|
||||
{
|
||||
RefreshDebugInfo();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Debug info available in play mode.", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawTestActions()
|
||||
{
|
||||
EditorGUILayout.LabelField("Test Actions", EditorStyles.boldLabel);
|
||||
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Enter Play Mode to use these testing functions.", MessageType.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// Booster Pack Actions
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.LabelField("Booster Pack Actions", EditorStyles.miniBoldLabel);
|
||||
|
||||
if (GUILayout.Button("Add Booster Packs", GUILayout.Height(30)))
|
||||
{
|
||||
AddBoosterPacks();
|
||||
}
|
||||
|
||||
// Card Generation Actions
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField("Card Generation Actions", EditorStyles.miniBoldLabel);
|
||||
|
||||
if (GUILayout.Button("Generate Random Cards", GUILayout.Height(30)))
|
||||
{
|
||||
GenerateRandomCards();
|
||||
}
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Danger Zone
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.LabelField("Danger Zone", EditorStyles.miniBoldLabel);
|
||||
|
||||
GUI.backgroundColor = new Color(1f, 0.6f, 0.6f);
|
||||
if (GUILayout.Button("Clear All Cards", GUILayout.Height(30)))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Clear All Cards",
|
||||
"Are you sure you want to clear all cards from the inventory? This cannot be undone in this play session.",
|
||||
"Clear All", "Cancel"))
|
||||
{
|
||||
ClearAllCards();
|
||||
}
|
||||
}
|
||||
GUI.backgroundColor = Color.white;
|
||||
}
|
||||
|
||||
// Refresh the debug information
|
||||
private void RefreshDebugInfo()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
return;
|
||||
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
currentBoosterCount = CardSystemManager.Instance.GetBoosterPackCount();
|
||||
totalCardsInCollection = CardSystemManager.Instance.GetCardInventory().GetAllCards().Count;
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
// Test Action Methods
|
||||
|
||||
private void AddBoosterPacks()
|
||||
{
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.AddBoosterPack(boosterPacksToAdd);
|
||||
lastActionMessage = $"Added {boosterPacksToAdd} booster pack(s)";
|
||||
Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
RefreshDebugInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
lastActionMessage = "Error: CardSystemManager instance not found!";
|
||||
Debug.LogError("[CardSystemTesterWindow] " + lastActionMessage);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateRandomCards()
|
||||
{
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
int cardsAdded = 0;
|
||||
List<CardDefinition> allDefinitions = CardSystemManager.Instance.GetAllCardDefinitions();
|
||||
|
||||
if (allDefinitions.Count == 0)
|
||||
{
|
||||
lastActionMessage = "Error: No card definitions available";
|
||||
Logging.Warning($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
Repaint();
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < cardsToGenerate; i++)
|
||||
{
|
||||
// Get a random card definition
|
||||
CardDefinition randomDef = allDefinitions[Random.Range(0, allDefinitions.Count)];
|
||||
|
||||
// Create a card data instance and add it to inventory
|
||||
CardData newCard = randomDef.CreateCardData();
|
||||
CardSystemManager.Instance.GetCardInventory().AddCard(newCard);
|
||||
cardsAdded++;
|
||||
}
|
||||
|
||||
lastActionMessage = $"Generated {cardsAdded} random cards";
|
||||
Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
RefreshDebugInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
lastActionMessage = "Error: CardSystemManager instance not found!";
|
||||
Debug.LogError("[CardSystemTesterWindow] " + lastActionMessage);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearAllCards()
|
||||
{
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
int count = CardSystemManager.Instance.GetCardInventory().GetAllCards().Count;
|
||||
CardSystemManager.Instance.GetCardInventory().ClearAllCards();
|
||||
lastActionMessage = $"Cleared {count} cards from inventory";
|
||||
Logging.Debug($"[CardSystemTesterWindow] {lastActionMessage}");
|
||||
RefreshDebugInfo();
|
||||
}
|
||||
else
|
||||
{
|
||||
lastActionMessage = "Error: CardSystemManager instance not found!";
|
||||
Debug.LogError("[CardSystemTesterWindow] " + lastActionMessage);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
97
Assets/Prefabs/UI/CardsSystem/BaseSlot.prefab
Normal file
97
Assets/Prefabs/UI/CardsSystem/BaseSlot.prefab
Normal file
@@ -0,0 +1,97 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &7367593073050060448
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1169576498417741412}
|
||||
- component: {fileID: 8622784967378501758}
|
||||
- component: {fileID: 1820057541044822447}
|
||||
- component: {fileID: 6049376501508697626}
|
||||
m_Layer: 0
|
||||
m_Name: BaseSlot
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1169576498417741412
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7367593073050060448}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 300, y: 400}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &8622784967378501758
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7367593073050060448}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: ee43b700f9dd44dba39deb8c5bcd688c, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::UI.DragAndDrop.Core.DraggableSlot
|
||||
slotIndex: 0
|
||||
isLocked: 0
|
||||
filterByType: 0
|
||||
allowedTypeNames: []
|
||||
applyScaleToOccupant: 1
|
||||
occupantScale: {x: 1, y: 1, z: 1}
|
||||
scaleTransitionDuration: 0.3
|
||||
--- !u!222 &1820057541044822447
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7367593073050060448}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &6049376501508697626
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7367593073050060448}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
7
Assets/Prefabs/UI/CardsSystem/BaseSlot.prefab.meta
Normal file
7
Assets/Prefabs/UI/CardsSystem/BaseSlot.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c924a527b14d18b4085c11610d640896
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Prefabs/UI/CardsSystem/Boosters.meta
Normal file
8
Assets/Prefabs/UI/CardsSystem/Boosters.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db0521a12a8dc4b43b8286c8dc6dc98d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,334 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &3555924788298046233
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2510839793683184596}
|
||||
- component: {fileID: 7931090856607470176}
|
||||
- component: {fileID: 3867938905670777662}
|
||||
m_Layer: 5
|
||||
m_Name: NotificationDot
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2510839793683184596
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3555924788298046233}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 8243838592031101330}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &7931090856607470176
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3555924788298046233}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &3867938905670777662
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3555924788298046233}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 1454986794490068504, guid: dad47d8bcf29e4742803088801ae4b04, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &3813704348964656314
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 8243838592031101330}
|
||||
m_Layer: 5
|
||||
m_Name: Container
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &8243838592031101330
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3813704348964656314}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 2510839793683184596}
|
||||
- {fileID: 912809337432025300}
|
||||
m_Father: {fileID: 4399514539281523652}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!1 &4367103374191057909
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 912809337432025300}
|
||||
- component: {fileID: 2674835159736628855}
|
||||
- component: {fileID: 7140808185011848511}
|
||||
m_Layer: 5
|
||||
m_Name: BoosterCount
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &912809337432025300
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4367103374191057909}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 8243838592031101330}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &2674835159736628855
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4367103374191057909}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &7140808185011848511
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 4367103374191057909}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: Unity.TextMeshPro::TMPro.TextMeshProUGUI
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_text: 1
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_fontSharedMaterials: []
|
||||
m_fontMaterial: {fileID: 0}
|
||||
m_fontMaterials: []
|
||||
m_fontColor32:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_enableVertexGradient: 0
|
||||
m_colorMode: 3
|
||||
m_fontColorGradient:
|
||||
topLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
topRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
|
||||
bottomRight: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_fontColorGradientPreset: {fileID: 0}
|
||||
m_spriteAsset: {fileID: 0}
|
||||
m_tintAllSprites: 0
|
||||
m_StyleSheet: {fileID: 0}
|
||||
m_TextStyleHashCode: -1183493901
|
||||
m_overrideHtmlColors: 0
|
||||
m_faceColor:
|
||||
serializedVersion: 2
|
||||
rgba: 4294967295
|
||||
m_fontSize: 40
|
||||
m_fontSizeBase: 40
|
||||
m_fontWeight: 400
|
||||
m_enableAutoSizing: 0
|
||||
m_fontSizeMin: 18
|
||||
m_fontSizeMax: 72
|
||||
m_fontStyle: 1
|
||||
m_HorizontalAlignment: 2
|
||||
m_VerticalAlignment: 512
|
||||
m_textAlignment: 65535
|
||||
m_characterSpacing: 0
|
||||
m_wordSpacing: 0
|
||||
m_lineSpacing: 0
|
||||
m_lineSpacingMax: 0
|
||||
m_paragraphSpacing: 0
|
||||
m_charWidthMaxAdj: 0
|
||||
m_TextWrappingMode: 1
|
||||
m_wordWrappingRatios: 0.4
|
||||
m_overflowMode: 0
|
||||
m_linkedTextComponent: {fileID: 0}
|
||||
parentLinkedComponent: {fileID: 0}
|
||||
m_enableKerning: 0
|
||||
m_ActiveFontFeatures: 6e72656b
|
||||
m_enableExtraPadding: 0
|
||||
checkPaddingRequired: 0
|
||||
m_isRichText: 1
|
||||
m_EmojiFallbackSupport: 1
|
||||
m_parseCtrlCharacters: 1
|
||||
m_isOrthographic: 1
|
||||
m_isCullingEnabled: 0
|
||||
m_horizontalMapping: 0
|
||||
m_verticalMapping: 0
|
||||
m_uvLineOffset: 0
|
||||
m_geometrySortingOrder: 0
|
||||
m_IsTextObjectScaleStatic: 0
|
||||
m_VertexBufferAutoSizeReduction: 0
|
||||
m_useMaxVisibleDescender: 1
|
||||
m_pageToDisplay: 1
|
||||
m_margin: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_isUsingLegacyAnimationComponent: 0
|
||||
m_isVolumetricText: 0
|
||||
m_hasFontAssetChanged: 0
|
||||
m_baseMaterial: {fileID: 0}
|
||||
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
|
||||
--- !u!1 &6686144552275602124
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 4399514539281523652}
|
||||
- component: {fileID: 7701049314552813552}
|
||||
- component: {fileID: 6728421751561708195}
|
||||
m_Layer: 5
|
||||
m_Name: BoosterNotifications
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &4399514539281523652
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6686144552275602124}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 8243838592031101330}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 1, y: 1}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 37.5, y: 37.5}
|
||||
m_SizeDelta: {x: 75, y: 75}
|
||||
m_Pivot: {x: 1, y: 1}
|
||||
--- !u!114 &7701049314552813552
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6686144552275602124}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 5845ed3764635fe429b6f1063effdd8a, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::AppleHills.UI.CardSystem.BoosterNotificationDot
|
||||
dotBackground: {fileID: 3555924788298046233}
|
||||
countText: {fileID: 7140808185011848511}
|
||||
hideWhenZero: 1
|
||||
useAnimation: 1
|
||||
textPrefix:
|
||||
textSuffix:
|
||||
textColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
useTween: 1
|
||||
pulseDuration: 0.3
|
||||
pulseScale: 1.2
|
||||
animator: {fileID: 0}
|
||||
animationTrigger: Update
|
||||
--- !u!95 &6728421751561708195
|
||||
Animator:
|
||||
serializedVersion: 7
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6686144552275602124}
|
||||
m_Enabled: 1
|
||||
m_Avatar: {fileID: 0}
|
||||
m_Controller: {fileID: 0}
|
||||
m_CullingMode: 0
|
||||
m_UpdateMode: 0
|
||||
m_ApplyRootMotion: 0
|
||||
m_LinearVelocityBlending: 0
|
||||
m_StabilizeFeet: 0
|
||||
m_AnimatePhysics: 0
|
||||
m_WarningMessage:
|
||||
m_HasTransformHierarchy: 1
|
||||
m_AllowConstantClipSamplingOptimization: 1
|
||||
m_KeepAnimatorStateOnDisable: 0
|
||||
m_WriteDefaultValuesOnDisable: 0
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd3f9527253841847a5a204072bec7bb
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1418
Assets/Prefabs/UI/CardsSystem/Boosters/BoosterOpeningPage.prefab
Normal file
1418
Assets/Prefabs/UI/CardsSystem/Boosters/BoosterOpeningPage.prefab
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ebde0b07986e69419b050433d30ccd2
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
345
Assets/Prefabs/UI/CardsSystem/Boosters/BoosterPackPrefab.prefab
Normal file
345
Assets/Prefabs/UI/CardsSystem/Boosters/BoosterPackPrefab.prefab
Normal file
@@ -0,0 +1,345 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &1439929750438628637
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1966378914653314124}
|
||||
- component: {fileID: 1426611910087532856}
|
||||
- component: {fileID: 2074101745098457442}
|
||||
- component: {fileID: 8806344280287390479}
|
||||
- component: {fileID: 1085122085982152990}
|
||||
m_Layer: 0
|
||||
m_Name: BoosterPackPrefab
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1966378914653314124
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1439929750438628637}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 3011670408481256992}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 400}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &1426611910087532856
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1439929750438628637}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!225 &2074101745098457442
|
||||
CanvasGroup:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1439929750438628637}
|
||||
m_Enabled: 1
|
||||
m_Alpha: 1
|
||||
m_Interactable: 1
|
||||
m_BlocksRaycasts: 1
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!114 &8806344280287390479
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1439929750438628637}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 86710e43de46f6f4bac7c8e50813a599, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.AspectRatioFitter
|
||||
m_AspectMode: 2
|
||||
m_AspectRatio: 0.56378603
|
||||
--- !u!114 &1085122085982152990
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1439929750438628637}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: f95c1542aaa549d1867b43f6dc21e90f, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::UI.CardSystem.DragDrop.BoosterPackDraggable
|
||||
moveSpeed: 1500
|
||||
smoothMovement: 0
|
||||
snapDuration: 0.3
|
||||
visual: {fileID: 7621119673479996768}
|
||||
isSelectable: 1
|
||||
selectionOffset: 10
|
||||
canOpenOnDrop: 0
|
||||
canOpenOnDoubleClick: 0
|
||||
canTapToOpen: 0
|
||||
maxTapsToOpen: 3
|
||||
tapPulseScale: 1.15
|
||||
tapPulseDuration: 0.2
|
||||
openingParticleSystem: {fileID: 0}
|
||||
--- !u!1 &7174819457781356441
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5816619084030964884}
|
||||
m_Layer: 0
|
||||
m_Name: ShakeParent
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &5816619084030964884
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7174819457781356441}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5686487750795060323}
|
||||
m_Father: {fileID: 3011670408481256992}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!1 &8049593174403022815
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3011670408481256992}
|
||||
- component: {fileID: 7621119673479996768}
|
||||
- component: {fileID: 7579137834975743343}
|
||||
m_Layer: 0
|
||||
m_Name: Visual
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &3011670408481256992
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8049593174403022815}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5816619084030964884}
|
||||
m_Father: {fileID: 1966378914653314124}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!114 &7621119673479996768
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8049593174403022815}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a7d9474ece3b4d2ebad19ae178b22f4d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::UI.CardSystem.DragDrop.BoosterPackVisual
|
||||
canvas: {fileID: 7579137834975743343}
|
||||
canvasGroup: {fileID: 0}
|
||||
tiltParent: {fileID: 5686487750795060323}
|
||||
shakeParent: {fileID: 5816619084030964884}
|
||||
followSpeed: 3
|
||||
useFollowDelay: 1
|
||||
rotationAmount: 10
|
||||
rotationSpeed: 10
|
||||
autoTiltAmount: 15
|
||||
manualTiltAmount: 10
|
||||
tiltSpeed: 5
|
||||
useScaleAnimations: 1
|
||||
scaleOnHover: 1.15
|
||||
scaleOnDrag: 1.25
|
||||
scaleTransitionDuration: 0.15
|
||||
useIdleAnimation: 1
|
||||
idleAnimationSpeed: 1
|
||||
packImage: {fileID: 536676675070797395}
|
||||
packSprite: {fileID: 4365544765984126881, guid: 9dac643e78ad86e4988c11a92f9c7a6d, type: 3}
|
||||
glowEffect: {fileID: 0}
|
||||
glowTransform: {fileID: 0}
|
||||
openingScalePunch: 0.5
|
||||
openingRotationPunch: 360
|
||||
openingDuration: 0.5
|
||||
--- !u!223 &7579137834975743343
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8049593174403022815}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 2
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!1 &8644026751513010577
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3852979982219423268}
|
||||
- component: {fileID: 7478606448171944241}
|
||||
- component: {fileID: 536676675070797395}
|
||||
m_Layer: 0
|
||||
m_Name: PackImage
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &3852979982219423268
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8644026751513010577}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 5686487750795060323}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &7478606448171944241
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8644026751513010577}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &536676675070797395
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8644026751513010577}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 4365544765984126881, guid: 9dac643e78ad86e4988c11a92f9c7a6d, type: 3}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!1 &8857005617017844649
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5686487750795060323}
|
||||
m_Layer: 0
|
||||
m_Name: TiltParent
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &5686487750795060323
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 8857005617017844649}
|
||||
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 3852979982219423268}
|
||||
m_Father: {fileID: 5816619084030964884}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cff5eaa9e8cc26a439e7b36345916468
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
129
Assets/Prefabs/UI/CardsSystem/Boosters/BoosterSlot.prefab
Normal file
129
Assets/Prefabs/UI/CardsSystem/Boosters/BoosterSlot.prefab
Normal file
@@ -0,0 +1,129 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1001 &3163881818805475495
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 0}
|
||||
m_Modifications:
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 800
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1169576498417741412, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6049376501508697626, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_Sprite
|
||||
value:
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7367593073050060448, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: BoosterSlot
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 8622784967378501758, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
propertyPath: hideImageOnPlay
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents:
|
||||
- targetCorrespondingSourceObject: {fileID: 7367593073050060448, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
insertIndex: -1
|
||||
addedObject: {fileID: 2951238399271190329}
|
||||
m_SourcePrefab: {fileID: 100100000, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
--- !u!1 &5608862925863296519 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 7367593073050060448, guid: c924a527b14d18b4085c11610d640896, type: 3}
|
||||
m_PrefabInstance: {fileID: 3163881818805475495}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &2951238399271190329
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 5608862925863296519}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 86710e43de46f6f4bac7c8e50813a599, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.AspectRatioFitter
|
||||
m_AspectMode: 2
|
||||
m_AspectRatio: 0.56378603
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 561f7c561a416e54e9bf1c2af2f3f4ef
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Prefabs/UI/CardsSystem/Cards.meta
Normal file
8
Assets/Prefabs/UI/CardsSystem/Cards.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 560e2d789fbb45242b47a65e8dfbfcf2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
238
Assets/Prefabs/UI/CardsSystem/Cards/FlippableCardPrefab.prefab
Normal file
238
Assets/Prefabs/UI/CardsSystem/Cards/FlippableCardPrefab.prefab
Normal file
@@ -0,0 +1,238 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &2592418251725585151
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 2124645323449894959}
|
||||
- component: {fileID: 4796702999672489241}
|
||||
- component: {fileID: 3137319209721935388}
|
||||
- component: {fileID: 4204736264338791493}
|
||||
m_Layer: 0
|
||||
m_Name: Image
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &2124645323449894959
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2592418251725585151}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1716378143019989539}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
m_AnchorMax: {x: 1, y: 1}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &4796702999672489241
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2592418251725585151}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &3137319209721935388
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2592418251725585151}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Image
|
||||
m_Material: {fileID: 0}
|
||||
m_Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_RaycastTarget: 1
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 1
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
m_Type: 0
|
||||
m_PreserveAspect: 0
|
||||
m_FillCenter: 1
|
||||
m_FillMethod: 4
|
||||
m_FillAmount: 1
|
||||
m_FillClockwise: 1
|
||||
m_FillOrigin: 0
|
||||
m_UseSpriteMesh: 0
|
||||
m_PixelsPerUnitMultiplier: 1
|
||||
--- !u!114 &4204736264338791493
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2592418251725585151}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 3137319209721935388}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1 &9060030918047515996
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1716378143019989539}
|
||||
- component: {fileID: 7707921903331303303}
|
||||
- component: {fileID: 2188119248360986999}
|
||||
- component: {fileID: 8105728209131118840}
|
||||
m_Layer: 0
|
||||
m_Name: FlippableCardPrefab
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1716378143019989539
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9060030918047515996}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 2124645323449894959}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 200, y: 300}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &7707921903331303303
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9060030918047515996}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &2188119248360986999
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9060030918047515996}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 72cb26621865420aa763a66c06eb7f6d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::UI.CardSystem.CardDisplay
|
||||
cardNameText: {fileID: 0}
|
||||
cardImage: {fileID: 0}
|
||||
frameImage: {fileID: 0}
|
||||
overlayImage: {fileID: 0}
|
||||
backgroundImage: {fileID: 0}
|
||||
zoneShapeImage: {fileID: 0}
|
||||
cardData:
|
||||
Id: 32182d0e-634e-462c-9964-c57f5cf8e562
|
||||
DefinitionId:
|
||||
Rarity: 0
|
||||
CopiesOwned: 0
|
||||
visualConfig: {fileID: 0}
|
||||
editorCardDefinition: {fileID: 0}
|
||||
--- !u!114 &8105728209131118840
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 9060030918047515996}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.Button
|
||||
m_Navigation:
|
||||
m_Mode: 3
|
||||
m_WrapAround: 0
|
||||
m_SelectOnUp: {fileID: 0}
|
||||
m_SelectOnDown: {fileID: 0}
|
||||
m_SelectOnLeft: {fileID: 0}
|
||||
m_SelectOnRight: {fileID: 0}
|
||||
m_Transition: 1
|
||||
m_Colors:
|
||||
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
|
||||
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
|
||||
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
|
||||
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
|
||||
m_ColorMultiplier: 1
|
||||
m_FadeDuration: 0.1
|
||||
m_SpriteState:
|
||||
m_HighlightedSprite: {fileID: 0}
|
||||
m_PressedSprite: {fileID: 0}
|
||||
m_SelectedSprite: {fileID: 0}
|
||||
m_DisabledSprite: {fileID: 0}
|
||||
m_AnimationTriggers:
|
||||
m_NormalTrigger: Normal
|
||||
m_HighlightedTrigger: Highlighted
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 0}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e16716863eca4704fbfabef5a699b5aa
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -606,7 +606,7 @@ GameObject:
|
||||
- component: {fileID: 4655070334633964078}
|
||||
- component: {fileID: 982405227736437572}
|
||||
m_Layer: 5
|
||||
m_Name: Button (2)
|
||||
m_Name: Booster2
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
@@ -962,7 +962,8 @@ RectTransform:
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Children:
|
||||
- {fileID: 8268404978292125473}
|
||||
m_Father: {fileID: 8113036759791843642}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0, y: 0}
|
||||
@@ -1118,7 +1119,7 @@ GameObject:
|
||||
- component: {fileID: 3176006474186944314}
|
||||
- component: {fileID: 2938846613341841058}
|
||||
m_Layer: 5
|
||||
m_Name: Button
|
||||
m_Name: Booster3
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
@@ -1272,6 +1273,7 @@ RectTransform:
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 5906828909529466605}
|
||||
- {fileID: 691082512065824864}
|
||||
- {fileID: 4472180225153869618}
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
@@ -1371,7 +1373,7 @@ GameObject:
|
||||
- component: {fileID: 2682813042770983550}
|
||||
- component: {fileID: 7409731688398821684}
|
||||
m_Layer: 5
|
||||
m_Name: Button (1)
|
||||
m_Name: Booster1
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
@@ -1492,6 +1494,168 @@ MonoBehaviour:
|
||||
m_EditorClassIdentifier: UnityEngine.UI::UnityEngine.UI.AspectRatioFitter
|
||||
m_AspectMode: 1
|
||||
m_AspectRatio: 0.56378603
|
||||
--- !u!1001 &4691000134302573506
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 8113036759791843642}
|
||||
m_Modifications:
|
||||
- target: {fileID: 106385575138511979, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 245933177249891519, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: openingParticleSystem
|
||||
value:
|
||||
objectReference: {fileID: 6207392896650453636}
|
||||
- target: {fileID: 498445838423597154, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: BoosterOpeningPage
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1313588858854015996, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1530993938104875321, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: openingParticleSystem
|
||||
value:
|
||||
objectReference: {fileID: 6207392896650453636}
|
||||
- target: {fileID: 1670660734182863341, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2466164304475032254, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3371630871680769077, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: hideImageOnPlay
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 3723208489285683019, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 0.5
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: -0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5285839430634762799, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 5548954158011133206, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6433114534737680452, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
propertyPath: openingParticleSystem
|
||||
value:
|
||||
objectReference: {fileID: 6207392896650453636}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
--- !u!114 &304904072851265091 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 4981820558408988033, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
m_PrefabInstance: {fileID: 4691000134302573506}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 91691a5efb1346b5b34482dd8200c868, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::UI.CardSystem.BoosterOpeningPage
|
||||
--- !u!224 &691082512065824864 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 5228380266581535650, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
m_PrefabInstance: {fileID: 4691000134302573506}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!198 &6207392896650453636 stripped
|
||||
ParticleSystem:
|
||||
m_CorrespondingSourceObject: {fileID: 1674443196371304774, guid: 8ebde0b07986e69419b050433d30ccd2, type: 3}
|
||||
m_PrefabInstance: {fileID: 4691000134302573506}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!1001 &5549612182461073321
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -1600,6 +1764,26 @@ PrefabInstance:
|
||||
propertyPath: exitButton
|
||||
value:
|
||||
objectReference: {fileID: 2193243227233893301}
|
||||
- target: {fileID: 2162084082982493373, guid: 88a05fdd940194543ade1cc2bcdada5f, type: 3}
|
||||
propertyPath: boosterOpeningPage
|
||||
value:
|
||||
objectReference: {fileID: 304904072851265091}
|
||||
- target: {fileID: 2162084082982493373, guid: 88a05fdd940194543ade1cc2bcdada5f, type: 3}
|
||||
propertyPath: boosterPackButtons.Array.size
|
||||
value: 3
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 2162084082982493373, guid: 88a05fdd940194543ade1cc2bcdada5f, type: 3}
|
||||
propertyPath: 'boosterPackButtons.Array.data[0]'
|
||||
value:
|
||||
objectReference: {fileID: 9208173931017297427}
|
||||
- target: {fileID: 2162084082982493373, guid: 88a05fdd940194543ade1cc2bcdada5f, type: 3}
|
||||
propertyPath: 'boosterPackButtons.Array.data[1]'
|
||||
value:
|
||||
objectReference: {fileID: 4385084760331272994}
|
||||
- target: {fileID: 2162084082982493373, guid: 88a05fdd940194543ade1cc2bcdada5f, type: 3}
|
||||
propertyPath: 'boosterPackButtons.Array.data[2]'
|
||||
value:
|
||||
objectReference: {fileID: 8678042374005579956}
|
||||
- target: {fileID: 2432304785419660060, guid: 88a05fdd940194543ade1cc2bcdada5f, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 0
|
||||
@@ -2003,3 +2187,105 @@ MonoBehaviour:
|
||||
selectedScale: 1.5
|
||||
normalScale: 1
|
||||
scaleTransitionDuration: 0.2
|
||||
--- !u!1001 &5742495987250154725
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
m_Modification:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 4472180225153869618}
|
||||
m_Modifications:
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_Pivot.y
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_AnchorMax.x
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_AnchorMax.y
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_AnchorMin.x
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_AnchorMin.y
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 75
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 75
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalPosition.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalPosition.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalRotation.w
|
||||
value: 1
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalRotation.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalRotation.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalRotation.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: -164
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.x
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.y
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 6686144552275602124, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
propertyPath: m_Name
|
||||
value: BoosterNotifications
|
||||
objectReference: {fileID: 0}
|
||||
m_RemovedComponents: []
|
||||
m_RemovedGameObjects: []
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
--- !u!224 &8268404978292125473 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 4399514539281523652, guid: fd3f9527253841847a5a204072bec7bb, type: 3}
|
||||
m_PrefabInstance: {fileID: 5742495987250154725}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
4900
Assets/Prefabs/UI/CardsSystem/VFX/VFX_ConfettiBurst_WIP.prefab
Normal file
4900
Assets/Prefabs/UI/CardsSystem/VFX/VFX_ConfettiBurst_WIP.prefab
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a04075d06f231594292595e90a69cbb1
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Prefabs/UI/DragAndDrop.meta
Normal file
8
Assets/Prefabs/UI/DragAndDrop.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c71be4bd960f6d4da1f5dd99ebdf6c0
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
111
Assets/Prefabs/UI/DragAndDrop/BoosterVisual.prefab
Normal file
111
Assets/Prefabs/UI/DragAndDrop/BoosterVisual.prefab
Normal file
@@ -0,0 +1,111 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &2827681756075696773
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 6992943185340855393}
|
||||
- component: {fileID: 2901215963033372043}
|
||||
- component: {fileID: 3683862272405493446}
|
||||
- component: {fileID: 3839250086744484578}
|
||||
m_Layer: 0
|
||||
m_Name: BoosterVisual
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &6992943185340855393
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2827681756075696773}
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
m_AnchorMax: {x: 0.5, y: 0.5}
|
||||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 100, y: 100}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!223 &2901215963033372043
|
||||
Canvas:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2827681756075696773}
|
||||
m_Enabled: 1
|
||||
serializedVersion: 3
|
||||
m_RenderMode: 2
|
||||
m_Camera: {fileID: 0}
|
||||
m_PlaneDistance: 100
|
||||
m_PixelPerfect: 0
|
||||
m_ReceivesEvents: 1
|
||||
m_OverrideSorting: 0
|
||||
m_OverridePixelPerfect: 0
|
||||
m_SortingBucketNormalizedSize: 0
|
||||
m_VertexColorAlwaysGammaSpace: 0
|
||||
m_AdditionalShaderChannelsFlag: 0
|
||||
m_UpdateRectTransformForStandalone: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingOrder: 0
|
||||
m_TargetDisplay: 0
|
||||
--- !u!225 &3683862272405493446
|
||||
CanvasGroup:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2827681756075696773}
|
||||
m_Enabled: 1
|
||||
m_Alpha: 1
|
||||
m_Interactable: 1
|
||||
m_BlocksRaycasts: 1
|
||||
m_IgnoreParentGroups: 0
|
||||
--- !u!114 &3839250086744484578
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 2827681756075696773}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: a7d9474ece3b4d2ebad19ae178b22f4d, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier: AppleHillsScripts::UI.CardSystem.DragDrop.BoosterPackVisual
|
||||
canvas: {fileID: 0}
|
||||
canvasGroup: {fileID: 0}
|
||||
tiltParent: {fileID: 0}
|
||||
shakeParent: {fileID: 0}
|
||||
followSpeed: 30
|
||||
useFollowDelay: 1
|
||||
rotationAmount: 20
|
||||
rotationSpeed: 20
|
||||
autoTiltAmount: 30
|
||||
manualTiltAmount: 20
|
||||
tiltSpeed: 20
|
||||
useScaleAnimations: 1
|
||||
scaleOnHover: 1.15
|
||||
scaleOnDrag: 1.25
|
||||
scaleTransitionDuration: 0.15
|
||||
useIdleAnimation: 1
|
||||
idleAnimationSpeed: 1
|
||||
packImage: {fileID: 0}
|
||||
packSprite: {fileID: 0}
|
||||
glowEffect: {fileID: 0}
|
||||
glowTransform: {fileID: 0}
|
||||
openingScalePunch: 0.5
|
||||
openingRotationPunch: 360
|
||||
openingDuration: 0.5
|
||||
7
Assets/Prefabs/UI/DragAndDrop/BoosterVisual.prefab.meta
Normal file
7
Assets/Prefabs/UI/DragAndDrop/BoosterVisual.prefab.meta
Normal file
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71e052626ab48b442b89a312a9f51eda
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,4 +1,6 @@
|
||||
using Pixelplacement;
|
||||
using Bootstrap;
|
||||
using Data.CardSystem;
|
||||
using Pixelplacement;
|
||||
using UI.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
@@ -7,12 +9,20 @@ namespace UI.CardSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// UI page for viewing the player's card collection in an album.
|
||||
/// Manages booster pack button visibility and opening flow.
|
||||
/// </summary>
|
||||
public class AlbumViewPage : UIPage
|
||||
{
|
||||
[Header("UI References")]
|
||||
[SerializeField] private CanvasGroup canvasGroup;
|
||||
[SerializeField] private Button exitButton;
|
||||
[SerializeField] private BookCurlPro.BookPro book;
|
||||
|
||||
[Header("Booster Pack UI")]
|
||||
[SerializeField] private GameObject[] boosterPackButtons;
|
||||
[SerializeField] private BoosterOpeningPage boosterOpeningPage;
|
||||
|
||||
private Input.InputMode _previousInputMode;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
@@ -28,16 +38,73 @@ namespace UI.CardSystem
|
||||
exitButton.onClick.AddListener(OnExitButtonClicked);
|
||||
}
|
||||
|
||||
// Set up booster pack button listeners
|
||||
SetupBoosterButtonListeners();
|
||||
|
||||
// Register for post-boot initialization
|
||||
BootCompletionService.RegisterInitAction(InitializePostBoot);
|
||||
|
||||
// UI pages should start disabled
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void InitializePostBoot()
|
||||
{
|
||||
// Subscribe to CardSystemManager events
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
|
||||
|
||||
// Update initial button visibility
|
||||
int initialCount = CardSystemManager.Instance.GetBoosterPackCount();
|
||||
UpdateBoosterButtons(initialCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetupBoosterButtonListeners()
|
||||
{
|
||||
if (boosterPackButtons == null) return;
|
||||
|
||||
for (int i = 0; i < boosterPackButtons.Length; i++)
|
||||
{
|
||||
if (boosterPackButtons[i] == null) continue;
|
||||
|
||||
Button button = boosterPackButtons[i].GetComponent<Button>();
|
||||
if (button != null)
|
||||
{
|
||||
button.onClick.AddListener(OnBoosterButtonClicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Unsubscribe from CardSystemManager
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
||||
}
|
||||
|
||||
// Clean up exit button
|
||||
if (exitButton != null)
|
||||
{
|
||||
exitButton.onClick.RemoveListener(OnExitButtonClicked);
|
||||
}
|
||||
|
||||
// Clean up booster button listeners
|
||||
if (boosterPackButtons != null)
|
||||
{
|
||||
foreach (var buttonObj in boosterPackButtons)
|
||||
{
|
||||
if (buttonObj == null) continue;
|
||||
|
||||
Button button = buttonObj.GetComponent<Button>();
|
||||
if (button != null)
|
||||
{
|
||||
button.onClick.RemoveListener(OnBoosterButtonClicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExitButtonClicked()
|
||||
@@ -57,12 +124,72 @@ namespace UI.CardSystem
|
||||
else
|
||||
{
|
||||
// Already on page 0 or no book reference, exit
|
||||
// Restore input mode before popping
|
||||
if (Input.InputManager.Instance != null)
|
||||
{
|
||||
Input.InputManager.Instance.SetInputMode(_previousInputMode);
|
||||
Debug.Log($"[AlbumViewPage] Restored input mode to {_previousInputMode} on exit");
|
||||
}
|
||||
|
||||
if (UIPageController.Instance != null)
|
||||
{
|
||||
UIPageController.Instance.PopPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBoosterCountChanged(int newCount)
|
||||
{
|
||||
UpdateBoosterButtons(newCount);
|
||||
}
|
||||
|
||||
private void UpdateBoosterButtons(int boosterCount)
|
||||
{
|
||||
if (boosterPackButtons == null || boosterPackButtons.Length == 0) return;
|
||||
|
||||
int visibleCount = Mathf.Min(boosterCount, boosterPackButtons.Length);
|
||||
|
||||
for (int i = 0; i < boosterPackButtons.Length; i++)
|
||||
{
|
||||
if (boosterPackButtons[i] != null)
|
||||
{
|
||||
boosterPackButtons[i].SetActive(i < visibleCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBoosterButtonClicked()
|
||||
{
|
||||
if (boosterOpeningPage != null && UIPageController.Instance != null)
|
||||
{
|
||||
// Pass current booster count to the opening page
|
||||
int boosterCount = CardSystemManager.Instance?.GetBoosterPackCount() ?? 0;
|
||||
boosterOpeningPage.SetAvailableBoosterCount(boosterCount);
|
||||
|
||||
UIPageController.Instance.PushPage(boosterOpeningPage);
|
||||
}
|
||||
}
|
||||
|
||||
public override void TransitionIn()
|
||||
{
|
||||
// Only store and switch input mode if this is the first time entering
|
||||
// (when _previousInputMode hasn't been set yet)
|
||||
if (Input.InputManager.Instance != null)
|
||||
{
|
||||
// Store the current input mode before switching
|
||||
_previousInputMode = Input.InputMode.GameAndUI;
|
||||
Input.InputManager.Instance.SetInputMode(Input.InputMode.UI);
|
||||
Debug.Log("[AlbumViewPage] Switched to UI-only input mode on first entry");
|
||||
}
|
||||
|
||||
base.TransitionIn();
|
||||
}
|
||||
|
||||
public override void TransitionOut()
|
||||
{
|
||||
// Don't restore input mode here - only restore when actually exiting (in OnExitButtonClicked)
|
||||
base.TransitionOut();
|
||||
}
|
||||
|
||||
protected override void DoTransitionIn(System.Action onComplete)
|
||||
{
|
||||
|
||||
225
Assets/Scripts/UI/CardSystem/BoosterNotificationDot.cs
Normal file
225
Assets/Scripts/UI/CardSystem/BoosterNotificationDot.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
using Bootstrap;
|
||||
using Data.CardSystem;
|
||||
using Pixelplacement;
|
||||
using Pixelplacement.TweenSystem;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.CardSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages a notification dot that displays a count (e.g., booster packs)
|
||||
/// Can be reused across different UI elements that need to show numeric notifications
|
||||
/// Automatically syncs with CardSystemManager to display booster pack count
|
||||
/// </summary>
|
||||
public class BoosterNotificationDot : MonoBehaviour
|
||||
{
|
||||
[Header("UI References")]
|
||||
[SerializeField] private GameObject dotBackground;
|
||||
[SerializeField] private TextMeshProUGUI countText;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private bool hideWhenZero = true;
|
||||
[SerializeField] private bool useAnimation = false;
|
||||
[SerializeField] private string textPrefix = "";
|
||||
[SerializeField] private string textSuffix = "";
|
||||
[SerializeField] private Color textColor = Color.white;
|
||||
|
||||
[Header("Animation")]
|
||||
[SerializeField] private bool useTween = true;
|
||||
[SerializeField] private float pulseDuration = 0.3f;
|
||||
[SerializeField] private float pulseScale = 1.2f;
|
||||
|
||||
// Optional animator reference
|
||||
[SerializeField] private Animator animator;
|
||||
[SerializeField] private string animationTrigger = "Update";
|
||||
|
||||
// Current count value
|
||||
private int _currentCount;
|
||||
private Vector3 _originalScale;
|
||||
|
||||
private TweenBase _activeTween;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Store original scale for pulse animation
|
||||
if (dotBackground != null)
|
||||
{
|
||||
_originalScale = dotBackground.transform.localScale;
|
||||
}
|
||||
|
||||
// Apply text color
|
||||
if (countText != null)
|
||||
{
|
||||
countText.color = textColor;
|
||||
}
|
||||
|
||||
// Register for post-boot initialization
|
||||
BootCompletionService.RegisterInitAction(InitializePostBoot);
|
||||
}
|
||||
|
||||
private void InitializePostBoot()
|
||||
{
|
||||
// Subscribe to CardSystemManager events
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.OnBoosterCountChanged += OnBoosterCountChanged;
|
||||
|
||||
// Poll initial count and display it
|
||||
int initialCount = CardSystemManager.Instance.GetBoosterPackCount();
|
||||
SetCount(initialCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If CardSystemManager isn't available yet, set to default count
|
||||
SetCount(_currentCount);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Unsubscribe from CardSystemManager events to prevent memory leaks
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
CardSystemManager.Instance.OnBoosterCountChanged -= OnBoosterCountChanged;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when booster count changes in CardSystemManager
|
||||
/// </summary>
|
||||
private void OnBoosterCountChanged(int newCount)
|
||||
{
|
||||
SetCount(newCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the count displayed on the notification dot
|
||||
/// Also handles visibility based on settings
|
||||
/// </summary>
|
||||
public void SetCount(int count)
|
||||
{
|
||||
bool countChanged = count != _currentCount;
|
||||
_currentCount = count;
|
||||
|
||||
// Update text
|
||||
if (countText != null)
|
||||
{
|
||||
countText.text = textPrefix + count.ToString() + textSuffix;
|
||||
}
|
||||
|
||||
// Handle visibility
|
||||
if (hideWhenZero)
|
||||
{
|
||||
SetVisibility(count > 0);
|
||||
}
|
||||
|
||||
// Play animation if value changed and animation is enabled
|
||||
if (countChanged && count > 0)
|
||||
{
|
||||
if (useAnimation)
|
||||
{
|
||||
Animate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current count value
|
||||
/// </summary>
|
||||
public int GetCount()
|
||||
{
|
||||
return _currentCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set text formatting options
|
||||
/// </summary>
|
||||
public void SetFormatting(string prefix, string suffix, Color color)
|
||||
{
|
||||
textPrefix = prefix;
|
||||
textSuffix = suffix;
|
||||
textColor = color;
|
||||
|
||||
if (countText != null)
|
||||
{
|
||||
countText.color = color;
|
||||
// Update text with new formatting
|
||||
countText.text = textPrefix + _currentCount.ToString() + textSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitly control the notification dot visibility
|
||||
/// </summary>
|
||||
public void SetVisibility(bool isVisible)
|
||||
{
|
||||
if (dotBackground != null)
|
||||
{
|
||||
dotBackground.SetActive(isVisible);
|
||||
}
|
||||
|
||||
if (countText != null)
|
||||
{
|
||||
countText.gameObject.SetActive(isVisible);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the notification dot
|
||||
/// </summary>
|
||||
public void Show()
|
||||
{
|
||||
SetVisibility(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide the notification dot
|
||||
/// </summary>
|
||||
public void Hide()
|
||||
{
|
||||
SetVisibility(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play animation manually - either using Animator or Tween
|
||||
/// </summary>
|
||||
public void Animate()
|
||||
{
|
||||
if (useAnimation)
|
||||
{
|
||||
if (animator != null)
|
||||
{
|
||||
animator.SetTrigger(animationTrigger);
|
||||
}
|
||||
else if (useTween && dotBackground != null)
|
||||
{
|
||||
// Cancel any existing tweens on this transform
|
||||
if(_activeTween != null)
|
||||
_activeTween.Cancel();
|
||||
|
||||
// Reset to original scale
|
||||
dotBackground.transform.localScale = _originalScale;
|
||||
|
||||
// Pulse animation using Tween
|
||||
_activeTween = Tween.LocalScale(dotBackground.transform,
|
||||
_originalScale * pulseScale,
|
||||
pulseDuration/2,
|
||||
0,
|
||||
Tween.EaseOut,
|
||||
Tween.LoopType.None,
|
||||
null,
|
||||
() => {
|
||||
// Scale back to original size
|
||||
Tween.LocalScale(dotBackground.transform,
|
||||
_originalScale,
|
||||
pulseDuration/2,
|
||||
0,
|
||||
Tween.EaseIn);
|
||||
},
|
||||
obeyTimescale: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5845ed3764635fe429b6f1063effdd8a
|
||||
529
Assets/Scripts/UI/CardSystem/BoosterOpeningPage.cs
Normal file
529
Assets/Scripts/UI/CardSystem/BoosterOpeningPage.cs
Normal file
@@ -0,0 +1,529 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AppleHills.Data.CardSystem;
|
||||
using Data.CardSystem;
|
||||
using Pixelplacement;
|
||||
using UI.Core;
|
||||
using UI.CardSystem.DragDrop;
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI.CardSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// UI page for opening booster packs and displaying the cards received.
|
||||
/// Manages the entire booster opening flow with drag-and-drop interaction.
|
||||
/// </summary>
|
||||
public class BoosterOpeningPage : UIPage
|
||||
{
|
||||
[Header("UI References")]
|
||||
[SerializeField] private CanvasGroup canvasGroup;
|
||||
[SerializeField] private Button closeButton;
|
||||
|
||||
[Header("Booster Management")]
|
||||
[SerializeField] private GameObject[] boosterPackInstances; // Booster prefabs/instances
|
||||
[SerializeField] private SlotContainer bottomRightSlots; // Holds waiting boosters
|
||||
[SerializeField] private DraggableSlot centerOpeningSlot; // Where booster goes to open
|
||||
|
||||
[Header("Card Display")]
|
||||
[SerializeField] private Transform cardDisplayContainer;
|
||||
[SerializeField] private GameObject flippableCardPrefab; // Placeholder for card backs
|
||||
[SerializeField] private float cardSpacing = 150f;
|
||||
|
||||
[Header("Settings")]
|
||||
[SerializeField] private float cardRevealDelay = 0.5f;
|
||||
[SerializeField] private float boosterDisappearDuration = 0.5f;
|
||||
|
||||
private int _availableBoosterCount;
|
||||
private BoosterPackDraggable _currentBoosterInCenter;
|
||||
private List<GameObject> _currentRevealedCards = new List<GameObject>();
|
||||
private CardData[] _currentCardData;
|
||||
private int _revealedCardCount;
|
||||
private bool _isProcessingOpening;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Make sure we have a CanvasGroup for transitions
|
||||
if (canvasGroup == null)
|
||||
canvasGroup = GetComponent<CanvasGroup>();
|
||||
if (canvasGroup == null)
|
||||
canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
||||
|
||||
// Set up close button
|
||||
if (closeButton != null)
|
||||
{
|
||||
closeButton.onClick.AddListener(OnCloseButtonClicked);
|
||||
}
|
||||
|
||||
// UI pages should start disabled
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (closeButton != null)
|
||||
{
|
||||
closeButton.onClick.RemoveListener(OnCloseButtonClicked);
|
||||
}
|
||||
|
||||
// Unsubscribe from slot events
|
||||
if (centerOpeningSlot != null)
|
||||
{
|
||||
centerOpeningSlot.OnOccupied -= OnBoosterPlacedInCenter;
|
||||
}
|
||||
|
||||
// Unsubscribe from booster events
|
||||
UnsubscribeFromAllBoosters();
|
||||
}
|
||||
|
||||
private void OnCloseButtonClicked()
|
||||
{
|
||||
if (UIPageController.Instance != null)
|
||||
{
|
||||
UIPageController.Instance.PopPage();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the number of available booster packs before showing the page
|
||||
/// </summary>
|
||||
public void SetAvailableBoosterCount(int count)
|
||||
{
|
||||
_availableBoosterCount = count;
|
||||
}
|
||||
|
||||
public override void TransitionIn()
|
||||
{
|
||||
base.TransitionIn();
|
||||
InitializeBoosterDisplay();
|
||||
}
|
||||
|
||||
public override void TransitionOut()
|
||||
{
|
||||
CleanupPage();
|
||||
base.TransitionOut();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the booster pack display based on available count
|
||||
/// </summary>
|
||||
private void InitializeBoosterDisplay()
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] InitializeBoosterDisplay called with {_availableBoosterCount} boosters available");
|
||||
|
||||
if (boosterPackInstances == null || boosterPackInstances.Length == 0)
|
||||
{
|
||||
Debug.LogWarning("BoosterOpeningPage: No booster pack instances assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate how many boosters to show (capped by array size)
|
||||
int visibleCount = Mathf.Min(_availableBoosterCount, boosterPackInstances.Length);
|
||||
|
||||
Debug.Log($"[BoosterOpeningPage] Will show {visibleCount} boosters out of {boosterPackInstances.Length} instances");
|
||||
|
||||
// Show/hide boosters and assign to slots
|
||||
for (int i = 0; i < boosterPackInstances.Length; i++)
|
||||
{
|
||||
if (boosterPackInstances[i] == null) continue;
|
||||
|
||||
bool shouldShow = i < visibleCount;
|
||||
Debug.Log($"[BoosterOpeningPage] Booster {i} ({boosterPackInstances[i].name}): shouldShow={shouldShow}, position={boosterPackInstances[i].transform.position}");
|
||||
|
||||
boosterPackInstances[i].SetActive(shouldShow);
|
||||
|
||||
if (shouldShow)
|
||||
{
|
||||
// Get the booster draggable component
|
||||
BoosterPackDraggable booster = boosterPackInstances[i].GetComponent<BoosterPackDraggable>();
|
||||
if (booster != null)
|
||||
{
|
||||
// Reset state
|
||||
booster.ResetTapCount();
|
||||
booster.SetTapToOpenEnabled(false); // Disable tap-to-open until in center
|
||||
|
||||
// Subscribe to events
|
||||
booster.OnReadyToOpen += OnBoosterReadyToOpen;
|
||||
|
||||
// Assign to bottom-right slot if slots available
|
||||
if (bottomRightSlots != null && i < bottomRightSlots.SlotCount)
|
||||
{
|
||||
DraggableSlot slot = bottomRightSlots.GetSlotAtIndex(i);
|
||||
if (slot != null)
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] Assigning booster {i} to slot {slot.name} at {slot.transform.position}");
|
||||
booster.AssignToSlot(slot, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[BoosterOpeningPage] Slot {i} is null in bottomRightSlots!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[BoosterOpeningPage] No slot available for booster {i}. bottomRightSlots={bottomRightSlots}, SlotCount={bottomRightSlots?.SlotCount}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[BoosterOpeningPage] Booster {i} has no BoosterPackDraggable component!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe to center slot events
|
||||
if (centerOpeningSlot != null)
|
||||
{
|
||||
centerOpeningSlot.OnOccupied += OnBoosterPlacedInCenter;
|
||||
Debug.Log($"[BoosterOpeningPage] Subscribed to center slot {centerOpeningSlot.name} at {centerOpeningSlot.transform.position}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[BoosterOpeningPage] centerOpeningSlot is null!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when a booster is placed in the center opening slot
|
||||
/// </summary>
|
||||
private void OnBoosterPlacedInCenter(DraggableObject draggable)
|
||||
{
|
||||
BoosterPackDraggable booster = draggable as BoosterPackDraggable;
|
||||
if (booster == null) return;
|
||||
|
||||
_currentBoosterInCenter = booster;
|
||||
|
||||
// Lock the slot so it can't be dragged out
|
||||
centerOpeningSlot.SetLocked(true);
|
||||
|
||||
// Configure booster for opening (disables drag, enables tapping, resets tap count)
|
||||
booster.SetInOpeningSlot(true);
|
||||
|
||||
// Subscribe to tap events for visual feedback
|
||||
booster.OnTapped += OnBoosterTapped;
|
||||
booster.OnReadyToOpen += OnBoosterReadyToOpen;
|
||||
|
||||
Debug.Log($"[BoosterOpeningPage] Booster placed in center, ready for {booster.CurrentTapCount} taps");
|
||||
}
|
||||
|
||||
private void OnBoosterTapped(BoosterPackDraggable booster, int currentTaps, int maxTaps)
|
||||
{
|
||||
Debug.Log($"[BoosterOpeningPage] Booster tapped: {currentTaps}/{maxTaps}");
|
||||
|
||||
// Calculate shake intensity (increases with each tap)
|
||||
float shakeIntensity = currentTaps / (float)maxTaps;
|
||||
float shakeAmount = 10f + (shakeIntensity * 30f); // 10 to 40 units
|
||||
|
||||
// TODO: Shake visual feedback
|
||||
// This would be handled by BoosterPackVisual if we add a shake method
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle tap-to-place: When player taps a booster in bottom slots, move it to center
|
||||
/// </summary>
|
||||
public void OnBoosterTappedInBottomSlot(BoosterPackDraggable booster)
|
||||
{
|
||||
if (_currentBoosterInCenter != null || centerOpeningSlot == null)
|
||||
return; // Center slot already occupied
|
||||
|
||||
// Move booster to center slot
|
||||
booster.AssignToSlot(centerOpeningSlot, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle when booster is ready to open (after max taps)
|
||||
/// </summary>
|
||||
private void OnBoosterReadyToOpen(BoosterPackDraggable booster)
|
||||
{
|
||||
if (_isProcessingOpening) return;
|
||||
|
||||
Debug.Log($"[BoosterOpeningPage] Booster ready to open!");
|
||||
|
||||
// Trigger the actual opening sequence
|
||||
booster.TriggerOpen();
|
||||
StartCoroutine(ProcessBoosterOpening(booster));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the booster opening sequence
|
||||
/// </summary>
|
||||
private IEnumerator ProcessBoosterOpening(BoosterPackDraggable booster)
|
||||
{
|
||||
_isProcessingOpening = true;
|
||||
|
||||
// Call CardSystemManager to open the pack
|
||||
if (CardSystemManager.Instance != null)
|
||||
{
|
||||
List<CardData> revealedCardsList = CardSystemManager.Instance.OpenBoosterPack();
|
||||
_currentCardData = revealedCardsList.ToArray();
|
||||
|
||||
// Animate booster disappearing
|
||||
yield return StartCoroutine(AnimateBoosterDisappear(booster));
|
||||
|
||||
// Show card backs
|
||||
SpawnCardBacks(_currentCardData.Length);
|
||||
|
||||
// Wait for player to reveal all cards
|
||||
yield return StartCoroutine(WaitForCardReveals());
|
||||
|
||||
// Check if more boosters available
|
||||
_availableBoosterCount--;
|
||||
|
||||
if (_availableBoosterCount > 0)
|
||||
{
|
||||
// Show next booster
|
||||
yield return StartCoroutine(ShowNextBooster());
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more boosters, auto-close page
|
||||
yield return new WaitForSeconds(1f);
|
||||
if (UIPageController.Instance != null)
|
||||
{
|
||||
UIPageController.Instance.PopPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_isProcessingOpening = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Animate the booster pack disappearing
|
||||
/// </summary>
|
||||
private IEnumerator AnimateBoosterDisappear(BoosterPackDraggable booster)
|
||||
{
|
||||
if (booster == null) yield break;
|
||||
|
||||
// Scale down and fade out
|
||||
Transform boosterTransform = booster.transform;
|
||||
|
||||
Tween.LocalScale(boosterTransform, Vector3.zero, boosterDisappearDuration, 0f, Tween.EaseInBack);
|
||||
|
||||
// Also fade the visual if it has a CanvasGroup
|
||||
CanvasGroup boosterCg = booster.GetComponentInChildren<CanvasGroup>();
|
||||
if (boosterCg != null)
|
||||
{
|
||||
Tween.Value(1f, 0f, (val) => boosterCg.alpha = val, boosterDisappearDuration, 0f);
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(boosterDisappearDuration);
|
||||
|
||||
// Destroy the booster
|
||||
Destroy(booster.gameObject);
|
||||
_currentBoosterInCenter = null;
|
||||
|
||||
// Unlock center slot
|
||||
centerOpeningSlot.SetLocked(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Spawn card back placeholders for revealing
|
||||
/// </summary>
|
||||
private void SpawnCardBacks(int count)
|
||||
{
|
||||
if (flippableCardPrefab == null || cardDisplayContainer == null)
|
||||
{
|
||||
Debug.LogWarning("BoosterOpeningPage: Missing card prefab or container!");
|
||||
return;
|
||||
}
|
||||
|
||||
_currentRevealedCards.Clear();
|
||||
_revealedCardCount = 0;
|
||||
|
||||
// Calculate positions
|
||||
float totalWidth = (count - 1) * cardSpacing;
|
||||
float startX = -totalWidth / 2f;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
GameObject cardObj = Instantiate(flippableCardPrefab, cardDisplayContainer);
|
||||
RectTransform cardRect = cardObj.GetComponent<RectTransform>();
|
||||
|
||||
if (cardRect != null)
|
||||
{
|
||||
cardRect.anchoredPosition = new Vector2(startX + (i * cardSpacing), 0);
|
||||
}
|
||||
|
||||
// Add button to handle reveal on click
|
||||
Button cardButton = cardObj.GetComponent<Button>();
|
||||
if (cardButton == null)
|
||||
{
|
||||
cardButton = cardObj.AddComponent<Button>();
|
||||
}
|
||||
|
||||
int cardIndex = i; // Capture for closure
|
||||
cardButton.onClick.AddListener(() => OnCardClicked(cardIndex, cardObj));
|
||||
|
||||
_currentRevealedCards.Add(cardObj);
|
||||
|
||||
// Animate cards flying in
|
||||
cardRect.localScale = Vector3.zero;
|
||||
Tween.LocalScale(cardRect, Vector3.one, 0.3f, i * 0.1f, Tween.EaseOutBack);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle card click to reveal
|
||||
/// </summary>
|
||||
private void OnCardClicked(int cardIndex, GameObject cardObj)
|
||||
{
|
||||
if (cardIndex >= _currentCardData.Length) return;
|
||||
|
||||
// Flip/reveal animation (placeholder - just show card data for now)
|
||||
CardDisplay cardDisplay = cardObj.GetComponent<CardDisplay>();
|
||||
if (cardDisplay != null)
|
||||
{
|
||||
cardDisplay.SetupCard(_currentCardData[cardIndex]);
|
||||
}
|
||||
|
||||
// Disable button so it can't be clicked again
|
||||
Button cardButton = cardObj.GetComponent<Button>();
|
||||
if (cardButton != null)
|
||||
{
|
||||
cardButton.interactable = false;
|
||||
}
|
||||
|
||||
// Scale punch animation
|
||||
Tween.LocalScale(cardObj.transform, Vector3.one * 1.2f, 0.15f, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () => {
|
||||
Tween.LocalScale(cardObj.transform, Vector3.one, 0.15f, 0f, Tween.EaseInBack);
|
||||
});
|
||||
|
||||
_revealedCardCount++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wait until all cards are revealed
|
||||
/// </summary>
|
||||
private IEnumerator WaitForCardReveals()
|
||||
{
|
||||
while (_revealedCardCount < _currentCardData.Length)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// All cards revealed, wait a moment
|
||||
yield return new WaitForSeconds(1f);
|
||||
|
||||
// Clear cards
|
||||
foreach (GameObject card in _currentRevealedCards)
|
||||
{
|
||||
if (card != null)
|
||||
{
|
||||
// Animate out
|
||||
Tween.LocalScale(card.transform, Vector3.zero, 0.3f, 0f, Tween.EaseInBack,
|
||||
completeCallback: () => Destroy(card));
|
||||
}
|
||||
}
|
||||
|
||||
_currentRevealedCards.Clear();
|
||||
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the next booster pack
|
||||
/// </summary>
|
||||
private IEnumerator ShowNextBooster()
|
||||
{
|
||||
// Find the next inactive booster and activate it
|
||||
for (int i = 0; i < boosterPackInstances.Length; i++)
|
||||
{
|
||||
if (boosterPackInstances[i] != null && !boosterPackInstances[i].activeSelf)
|
||||
{
|
||||
boosterPackInstances[i].SetActive(true);
|
||||
|
||||
BoosterPackDraggable booster = boosterPackInstances[i].GetComponent<BoosterPackDraggable>();
|
||||
if (booster != null)
|
||||
{
|
||||
booster.ResetTapCount();
|
||||
booster.SetTapToOpenEnabled(false);
|
||||
booster.OnReadyToOpen += OnBoosterReadyToOpen;
|
||||
|
||||
// Assign to first available slot
|
||||
DraggableSlot slot = bottomRightSlots?.GetAvailableSlots().FirstOrDefault();
|
||||
if (slot != null)
|
||||
{
|
||||
booster.AssignToSlot(slot, true);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up the page when hidden
|
||||
/// </summary>
|
||||
private void CleanupPage()
|
||||
{
|
||||
UnsubscribeFromAllBoosters();
|
||||
|
||||
// Clear any remaining cards
|
||||
foreach (GameObject card in _currentRevealedCards)
|
||||
{
|
||||
if (card != null)
|
||||
Destroy(card);
|
||||
}
|
||||
_currentRevealedCards.Clear();
|
||||
|
||||
_currentBoosterInCenter = null;
|
||||
_isProcessingOpening = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from all booster events
|
||||
/// </summary>
|
||||
private void UnsubscribeFromAllBoosters()
|
||||
{
|
||||
if (boosterPackInstances == null) return;
|
||||
|
||||
foreach (GameObject boosterObj in boosterPackInstances)
|
||||
{
|
||||
if (boosterObj == null) continue;
|
||||
|
||||
BoosterPackDraggable booster = boosterObj.GetComponent<BoosterPackDraggable>();
|
||||
if (booster != null)
|
||||
{
|
||||
booster.OnReadyToOpen -= OnBoosterReadyToOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DoTransitionIn(System.Action onComplete)
|
||||
{
|
||||
// Simple fade in animation
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
canvasGroup.alpha = 0f;
|
||||
Tween.Value(0f, 1f, (value) => canvasGroup.alpha = value, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, onComplete, obeyTimescale: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback if no CanvasGroup
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void DoTransitionOut(System.Action onComplete)
|
||||
{
|
||||
// Simple fade out animation
|
||||
if (canvasGroup != null)
|
||||
{
|
||||
Tween.Value(canvasGroup.alpha, 0f, (value) => canvasGroup.alpha = value, transitionDuration, 0f, Tween.EaseInOut, Tween.LoopType.None, null, onComplete, obeyTimescale: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback if no CanvasGroup
|
||||
onComplete?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Scripts/UI/CardSystem/BoosterOpeningPage.cs.meta
Normal file
3
Assets/Scripts/UI/CardSystem/BoosterOpeningPage.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91691a5efb1346b5b34482dd8200c868
|
||||
timeCreated: 1762418615
|
||||
3
Assets/Scripts/UI/CardSystem/DragDrop.meta
Normal file
3
Assets/Scripts/UI/CardSystem/DragDrop.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 053a2ff2538541699b134b07a07edecb
|
||||
timeCreated: 1762420654
|
||||
167
Assets/Scripts/UI/CardSystem/DragDrop/BoosterPackDraggable.cs
Normal file
167
Assets/Scripts/UI/CardSystem/DragDrop/BoosterPackDraggable.cs
Normal file
@@ -0,0 +1,167 @@
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.CardSystem.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Booster pack specific implementation of DraggableObject.
|
||||
/// Manages booster pack behavior and opening logic.
|
||||
/// </summary>
|
||||
public class BoosterPackDraggable : DraggableObject
|
||||
{
|
||||
[Header("Booster Pack Settings")]
|
||||
[SerializeField] private bool canOpenOnDrop = true;
|
||||
[SerializeField] private bool canOpenOnDoubleClick = true;
|
||||
|
||||
[Header("Tap to Open")]
|
||||
[SerializeField] private bool canTapToOpen = true;
|
||||
[SerializeField] private int maxTapsToOpen = 3;
|
||||
[SerializeField] private float tapPulseScale = 1.15f;
|
||||
[SerializeField] private float tapPulseDuration = 0.2f;
|
||||
[SerializeField] private ParticleSystem openingParticleSystem;
|
||||
|
||||
// ...existing code...
|
||||
public event System.Action<BoosterPackDraggable> OnBoosterOpened;
|
||||
public event System.Action<BoosterPackDraggable, int, int> OnTapped; // (booster, currentTap, maxTaps)
|
||||
public event System.Action<BoosterPackDraggable> OnReadyToOpen; // Final tap reached
|
||||
|
||||
private bool _isOpening;
|
||||
private float _lastClickTime;
|
||||
private int _currentTapCount;
|
||||
|
||||
public bool IsOpening => _isOpening;
|
||||
public int CurrentTapCount => _currentTapCount;
|
||||
|
||||
protected override void OnPointerUpHook(bool longPress)
|
||||
{
|
||||
base.OnPointerUpHook(longPress);
|
||||
|
||||
// Handle tap-to-open logic (only when in slot and not a long press)
|
||||
if (canTapToOpen && !longPress && CurrentSlot != null)
|
||||
{
|
||||
_currentTapCount++;
|
||||
|
||||
// Pulse effect on tap (scales visual up and back down)
|
||||
if (Visual != null)
|
||||
{
|
||||
// Calculate pulse intensity based on tap progress
|
||||
float tapProgress = _currentTapCount / (float)maxTapsToOpen;
|
||||
float currentPulseScale = 1f + (tapPulseScale - 1f) * (0.5f + tapProgress * 0.5f); // Increases from 1.075 to 1.15
|
||||
|
||||
// Save the current scale before pulsing
|
||||
Vector3 baseScale = Visual.transform.localScale;
|
||||
|
||||
Pixelplacement.Tween.Cancel(Visual.transform.GetInstanceID());
|
||||
Pixelplacement.Tween.LocalScale(Visual.transform, baseScale * currentPulseScale, tapPulseDuration * 0.5f, 0f,
|
||||
Pixelplacement.Tween.EaseOutBack, completeCallback: () =>
|
||||
{
|
||||
// Return to the base scale we had before pulsing
|
||||
Pixelplacement.Tween.LocalScale(Visual.transform, baseScale, tapPulseDuration * 0.5f, 0f, Pixelplacement.Tween.EaseInBack);
|
||||
});
|
||||
}
|
||||
|
||||
OnTapped?.Invoke(this, _currentTapCount, maxTapsToOpen);
|
||||
|
||||
if (_currentTapCount >= maxTapsToOpen)
|
||||
{
|
||||
OnReadyToOpen?.Invoke(this);
|
||||
}
|
||||
|
||||
return; // Don't process double-click if tap-to-open is active
|
||||
}
|
||||
|
||||
// ...existing code...
|
||||
if (canOpenOnDoubleClick && !longPress)
|
||||
{
|
||||
float timeSinceLastClick = Time.time - _lastClickTime;
|
||||
|
||||
if (timeSinceLastClick < 0.3f) // Double click threshold
|
||||
{
|
||||
TriggerOpen();
|
||||
}
|
||||
|
||||
_lastClickTime = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDragEndedHook()
|
||||
{
|
||||
base.OnDragEndedHook();
|
||||
|
||||
// Optionally trigger open when dropped in specific zones
|
||||
if (canOpenOnDrop)
|
||||
{
|
||||
// Could check if dropped in an "opening zone"
|
||||
// For now, just a placeholder
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trigger the booster pack opening animation and logic
|
||||
/// </summary>
|
||||
public void TriggerOpen()
|
||||
{
|
||||
if (_isOpening)
|
||||
return;
|
||||
|
||||
_isOpening = true;
|
||||
|
||||
// Play particle effect
|
||||
if (openingParticleSystem != null)
|
||||
{
|
||||
openingParticleSystem.Play();
|
||||
}
|
||||
|
||||
OnBoosterOpened?.Invoke(this);
|
||||
|
||||
// The actual opening logic (calling CardSystemManager) should be handled
|
||||
// by the UI page or controller that manages this booster pack
|
||||
|
||||
// Visual feedback would be handled by the BoosterPackVisual
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the opening state
|
||||
/// </summary>
|
||||
public void ResetOpeningState()
|
||||
{
|
||||
_isOpening = false;
|
||||
_currentTapCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set whether this booster is in the opening slot (disables dragging, enables tapping)
|
||||
/// </summary>
|
||||
public void SetInOpeningSlot(bool inSlot)
|
||||
{
|
||||
SetDraggingEnabled(!inSlot); // Disable dragging when in opening slot
|
||||
canTapToOpen = inSlot; // Enable tap-to-open when in opening slot
|
||||
|
||||
if (inSlot)
|
||||
{
|
||||
_currentTapCount = 0; // Reset tap counter when placed
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetOpeningState(); // Reset completely when removed
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset tap count (useful when starting a new opening sequence)
|
||||
/// </summary>
|
||||
public void ResetTapCount()
|
||||
{
|
||||
_currentTapCount = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable tap-to-open functionality at runtime
|
||||
/// </summary>
|
||||
public void SetTapToOpenEnabled(bool enabled)
|
||||
{
|
||||
canTapToOpen = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f95c1542aaa549d1867b43f6dc21e90f
|
||||
timeCreated: 1762420681
|
||||
189
Assets/Scripts/UI/CardSystem/DragDrop/BoosterPackVisual.cs
Normal file
189
Assets/Scripts/UI/CardSystem/DragDrop/BoosterPackVisual.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using Pixelplacement;
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI.CardSystem.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Visual representation for BoosterPackDraggable.
|
||||
/// Displays the booster pack sprite and handles opening animations.
|
||||
/// </summary>
|
||||
public class BoosterPackVisual : DraggableVisual
|
||||
{
|
||||
[Header("Booster Pack Visual")]
|
||||
[SerializeField] private Image packImage;
|
||||
[SerializeField] private Sprite packSprite;
|
||||
[SerializeField] private ParticleSystem glowEffect;
|
||||
[SerializeField] private Transform glowTransform;
|
||||
|
||||
[Header("Opening Animation")]
|
||||
[SerializeField] private float openingScalePunch = 0.5f;
|
||||
[SerializeField] private float openingRotationPunch = 360f;
|
||||
[SerializeField] private float openingDuration = 0.5f;
|
||||
|
||||
private BoosterPackDraggable _boosterDraggable;
|
||||
|
||||
public override void Initialize(DraggableObject parent)
|
||||
{
|
||||
base.Initialize(parent);
|
||||
|
||||
_boosterDraggable = parent as BoosterPackDraggable;
|
||||
|
||||
// Get pack image if not assigned
|
||||
if (packImage == null)
|
||||
{
|
||||
packImage = GetComponentInChildren<Image>();
|
||||
}
|
||||
|
||||
// Set initial sprite
|
||||
if (packImage != null && packSprite != null)
|
||||
{
|
||||
packImage.sprite = packSprite;
|
||||
}
|
||||
|
||||
// Subscribe to booster events
|
||||
if (_boosterDraggable != null)
|
||||
{
|
||||
_boosterDraggable.OnBoosterOpened += HandleBoosterOpened;
|
||||
_boosterDraggable.OnTapped += HandleTapped;
|
||||
}
|
||||
|
||||
// Start glow effect if available
|
||||
if (glowEffect != null && !glowEffect.isPlaying)
|
||||
{
|
||||
glowEffect.Play();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateVisualContent()
|
||||
{
|
||||
// Update glow rotation for visual interest
|
||||
if (glowTransform != null)
|
||||
{
|
||||
glowTransform.Rotate(Vector3.forward * 30f * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleBoosterOpened(BoosterPackDraggable booster)
|
||||
{
|
||||
PlayOpeningAnimation();
|
||||
}
|
||||
|
||||
private void HandleTapped(BoosterPackDraggable booster, int currentTap, int maxTaps)
|
||||
{
|
||||
PlayShakeAnimation(currentTap, maxTaps);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play progressive shake animation based on tap intensity
|
||||
/// </summary>
|
||||
public void PlayShakeAnimation(int intensity, int maxIntensity)
|
||||
{
|
||||
float normalizedIntensity = (float)intensity / maxIntensity;
|
||||
float shakeAmount = Mathf.Lerp(5f, 30f, normalizedIntensity);
|
||||
float shakeDuration = 0.15f;
|
||||
|
||||
// Shake rotation
|
||||
Vector3 shakeRotation = new Vector3(
|
||||
Random.Range(-shakeAmount, shakeAmount),
|
||||
Random.Range(-shakeAmount, shakeAmount),
|
||||
Random.Range(-shakeAmount, shakeAmount)
|
||||
);
|
||||
|
||||
Tween.Rotation(transform, transform.eulerAngles + shakeRotation,
|
||||
shakeDuration, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () => {
|
||||
Tween.Rotation(transform, Vector3.zero,
|
||||
shakeDuration, 0f, Tween.EaseInBack);
|
||||
});
|
||||
|
||||
// Scale punch (gets bigger with each tap)
|
||||
float punchScale = 1f + (normalizedIntensity * 0.2f);
|
||||
Tween.LocalScale(transform, Vector3.one * punchScale,
|
||||
shakeDuration / 2f, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () => {
|
||||
Tween.LocalScale(transform, Vector3.one,
|
||||
shakeDuration / 2f, 0f, Tween.EaseInBack);
|
||||
});
|
||||
|
||||
// Extra glow burst on final tap
|
||||
if (intensity == maxIntensity && glowEffect != null)
|
||||
{
|
||||
var emission = glowEffect.emission;
|
||||
emission.rateOverTimeMultiplier = 50f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Play the booster pack opening animation
|
||||
/// </summary>
|
||||
public void PlayOpeningAnimation()
|
||||
{
|
||||
// Scale punch
|
||||
Vector3 targetScale = transform.localScale * (1f + openingScalePunch);
|
||||
Tween.LocalScale(transform, targetScale, openingDuration / 2f, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () => {
|
||||
Tween.LocalScale(transform, Vector3.one, openingDuration / 2f, 0f, Tween.EaseInBack);
|
||||
});
|
||||
|
||||
// Rotation
|
||||
Tween.Rotation(transform, transform.eulerAngles + Vector3.forward * openingRotationPunch,
|
||||
openingDuration, 0f, Tween.EaseOutBack);
|
||||
|
||||
// Glow burst
|
||||
if (glowEffect != null)
|
||||
{
|
||||
glowEffect.Stop();
|
||||
glowEffect.Play();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the booster pack sprite
|
||||
/// </summary>
|
||||
public void SetPackSprite(Sprite sprite)
|
||||
{
|
||||
packSprite = sprite;
|
||||
if (packImage != null)
|
||||
{
|
||||
packImage.sprite = packSprite;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerEnterVisual()
|
||||
{
|
||||
base.OnPointerEnterVisual();
|
||||
|
||||
// Extra glow when hovering
|
||||
if (glowEffect != null)
|
||||
{
|
||||
var emission = glowEffect.emission;
|
||||
emission.rateOverTimeMultiplier = 20f;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerExitVisual()
|
||||
{
|
||||
base.OnPointerExitVisual();
|
||||
|
||||
// Restore normal glow
|
||||
if (glowEffect != null)
|
||||
{
|
||||
var emission = glowEffect.emission;
|
||||
emission.rateOverTimeMultiplier = 10f;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_boosterDraggable != null)
|
||||
{
|
||||
_boosterDraggable.OnBoosterOpened -= HandleBoosterOpened;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a7d9474ece3b4d2ebad19ae178b22f4d
|
||||
timeCreated: 1762420699
|
||||
62
Assets/Scripts/UI/CardSystem/DragDrop/CardDraggable.cs
Normal file
62
Assets/Scripts/UI/CardSystem/DragDrop/CardDraggable.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using AppleHills.Data.CardSystem;
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.CardSystem.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Card-specific implementation of DraggableObject.
|
||||
/// Manages card data and card-specific drag behavior.
|
||||
/// </summary>
|
||||
public class CardDraggable : DraggableObject
|
||||
{
|
||||
[Header("Card Data")]
|
||||
[SerializeField] private CardData cardData;
|
||||
|
||||
// Events
|
||||
public event System.Action<CardDraggable, CardData> OnCardDataChanged;
|
||||
|
||||
public CardData CardData => cardData;
|
||||
|
||||
/// <summary>
|
||||
/// Set the card data for this draggable card
|
||||
/// </summary>
|
||||
public void SetCardData(CardData data)
|
||||
{
|
||||
cardData = data;
|
||||
OnCardDataChanged?.Invoke(this, cardData);
|
||||
|
||||
// Update visual if it exists
|
||||
if (_visualInstance != null && _visualInstance is CardDraggableVisual cardVisual)
|
||||
{
|
||||
cardVisual.RefreshCardDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDragStartedHook()
|
||||
{
|
||||
base.OnDragStartedHook();
|
||||
// Card-specific drag started behavior
|
||||
}
|
||||
|
||||
protected override void OnDragEndedHook()
|
||||
{
|
||||
base.OnDragEndedHook();
|
||||
// Card-specific drag ended behavior
|
||||
}
|
||||
|
||||
protected override void OnSelectionChangedHook(bool selected)
|
||||
{
|
||||
base.OnSelectionChangedHook(selected);
|
||||
// Card-specific selection behavior
|
||||
}
|
||||
|
||||
protected override void OnSlotChangedHook(DraggableSlot previousSlot, DraggableSlot newSlot)
|
||||
{
|
||||
base.OnSlotChangedHook(previousSlot, newSlot);
|
||||
// Card-specific slot changed behavior
|
||||
// Could trigger events for card collection reordering, etc.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a2741bb7299441b9f9bd44d746ebb4b
|
||||
timeCreated: 1762420654
|
||||
121
Assets/Scripts/UI/CardSystem/DragDrop/CardDraggableVisual.cs
Normal file
121
Assets/Scripts/UI/CardSystem/DragDrop/CardDraggableVisual.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
using AppleHills.Data.CardSystem;
|
||||
using UI.DragAndDrop.Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.CardSystem.DragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Visual representation for CardDraggable.
|
||||
/// Uses the existing CardDisplay component to render the card.
|
||||
/// </summary>
|
||||
public class CardDraggableVisual : DraggableVisual
|
||||
{
|
||||
[Header("Card Visual Components")]
|
||||
[SerializeField] private CardDisplay cardDisplay;
|
||||
[SerializeField] private Transform shadowTransform;
|
||||
[SerializeField] private float shadowOffset = 20f;
|
||||
|
||||
private Vector3 _shadowInitialPosition;
|
||||
private CardDraggable _cardDraggable;
|
||||
|
||||
public CardDisplay CardDisplay => cardDisplay;
|
||||
|
||||
public override void Initialize(DraggableObject parent)
|
||||
{
|
||||
base.Initialize(parent);
|
||||
|
||||
_cardDraggable = parent as CardDraggable;
|
||||
|
||||
// Get CardDisplay component if not assigned
|
||||
if (cardDisplay == null)
|
||||
{
|
||||
cardDisplay = GetComponentInChildren<CardDisplay>();
|
||||
}
|
||||
|
||||
// Initialize shadow
|
||||
if (shadowTransform != null)
|
||||
{
|
||||
_shadowInitialPosition = shadowTransform.localPosition;
|
||||
}
|
||||
|
||||
// Subscribe to card data changes
|
||||
if (_cardDraggable != null)
|
||||
{
|
||||
_cardDraggable.OnCardDataChanged += HandleCardDataChanged;
|
||||
|
||||
// Initial card setup
|
||||
if (_cardDraggable.CardData != null && cardDisplay != null)
|
||||
{
|
||||
cardDisplay.SetupCard(_cardDraggable.CardData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateVisualContent()
|
||||
{
|
||||
// CardDisplay handles its own rendering, no need to update every frame
|
||||
// This is called every frame but we only update when card data changes
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the card display with current data
|
||||
/// </summary>
|
||||
public void RefreshCardDisplay()
|
||||
{
|
||||
if (cardDisplay != null && _cardDraggable != null && _cardDraggable.CardData != null)
|
||||
{
|
||||
cardDisplay.SetupCard(_cardDraggable.CardData);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCardDataChanged(CardDraggable draggable, CardData data)
|
||||
{
|
||||
RefreshCardDisplay();
|
||||
}
|
||||
|
||||
protected override void OnPointerDownVisual()
|
||||
{
|
||||
base.OnPointerDownVisual();
|
||||
|
||||
// Move shadow down when pressed
|
||||
if (shadowTransform != null)
|
||||
{
|
||||
shadowTransform.localPosition = _shadowInitialPosition + (-Vector3.up * shadowOffset);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnPointerUpVisual(bool longPress)
|
||||
{
|
||||
base.OnPointerUpVisual(longPress);
|
||||
|
||||
// Restore shadow position
|
||||
if (shadowTransform != null)
|
||||
{
|
||||
shadowTransform.localPosition = _shadowInitialPosition;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDragStartedVisual()
|
||||
{
|
||||
base.OnDragStartedVisual();
|
||||
// Card-specific visual effects when dragging starts
|
||||
}
|
||||
|
||||
protected override void OnDragEndedVisual()
|
||||
{
|
||||
base.OnDragEndedVisual();
|
||||
// Card-specific visual effects when dragging ends
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
base.OnDestroy();
|
||||
|
||||
if (_cardDraggable != null)
|
||||
{
|
||||
_cardDraggable.OnCardDataChanged -= HandleCardDataChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a4c3884410d44f98182cd8119a972a4
|
||||
timeCreated: 1762420668
|
||||
3
Assets/Scripts/UI/DragAndDrop.meta
Normal file
3
Assets/Scripts/UI/DragAndDrop.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9818aa1de299458b8b1fc95cdabc3f7f
|
||||
timeCreated: 1762420597
|
||||
3
Assets/Scripts/UI/DragAndDrop/Core.meta
Normal file
3
Assets/Scripts/UI/DragAndDrop/Core.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: de2fa1660c564a13ab22715e94b45e4c
|
||||
timeCreated: 1762420597
|
||||
577
Assets/Scripts/UI/DragAndDrop/Core/DraggableObject.cs
Normal file
577
Assets/Scripts/UI/DragAndDrop/Core/DraggableObject.cs
Normal file
@@ -0,0 +1,577 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using Pixelplacement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace UI.DragAndDrop.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for draggable UI objects.
|
||||
/// Handles drag logic, slot snapping, and events.
|
||||
/// Spawns and manages a separate DraggableVisual for rendering.
|
||||
/// Touch-compatible via Unity's pointer event system.
|
||||
/// Note: Optionally uses Image or CanvasGroup for automatic raycast toggling during drag.
|
||||
/// </summary>
|
||||
public abstract class DraggableObject : MonoBehaviour,
|
||||
IBeginDragHandler, IDragHandler, IEndDragHandler,
|
||||
IPointerEnterHandler, IPointerExitHandler,
|
||||
IPointerUpHandler, IPointerDownHandler
|
||||
{
|
||||
[Header("Draggable Settings")]
|
||||
[SerializeField] protected float moveSpeed = 50f;
|
||||
[SerializeField] protected bool smoothMovement = false; // Disabled for instant cursor tracking
|
||||
[SerializeField] protected float snapDuration = 0.3f;
|
||||
|
||||
[Header("Visual")]
|
||||
[SerializeField] protected DraggableVisual visual;
|
||||
|
||||
[Header("Selection")]
|
||||
[SerializeField] protected bool isSelectable = true;
|
||||
[SerializeField] protected float selectionOffset = 50f;
|
||||
|
||||
// State
|
||||
protected bool _isDragging;
|
||||
protected bool _isHovering;
|
||||
protected bool _isSelected;
|
||||
protected bool _wasDragged;
|
||||
protected bool _isDraggingEnabled = true;
|
||||
|
||||
// References
|
||||
protected Canvas _canvas;
|
||||
protected Image _imageComponent;
|
||||
protected CanvasGroup _canvasGroup;
|
||||
protected GraphicRaycaster _raycaster;
|
||||
protected DraggableSlot _currentSlot;
|
||||
protected DraggableVisual _visualInstance;
|
||||
|
||||
// Drag tracking
|
||||
protected Vector3 _dragOffset;
|
||||
protected Vector3 _lastPointerPosition;
|
||||
protected float _pointerDownTime;
|
||||
protected float _pointerUpTime;
|
||||
|
||||
// Events
|
||||
public event Action<DraggableObject> OnDragStarted;
|
||||
public event Action<DraggableObject> OnDragEnded;
|
||||
public event Action<DraggableObject> OnPointerEntered;
|
||||
public event Action<DraggableObject> OnPointerExited;
|
||||
public event Action<DraggableObject> OnPointerDowned;
|
||||
public event Action<DraggableObject, bool> OnPointerUpped; // bool = long press
|
||||
public event Action<DraggableObject, bool> OnSelected; // bool = selected state
|
||||
public event Action<DraggableObject, DraggableSlot> OnSlotChanged;
|
||||
|
||||
// Properties
|
||||
public bool IsDragging => _isDragging;
|
||||
public bool IsHovering => _isHovering;
|
||||
public bool IsSelected => _isSelected;
|
||||
public bool WasDragged => _wasDragged;
|
||||
public DraggableSlot CurrentSlot => _currentSlot;
|
||||
public DraggableVisual Visual => _visualInstance;
|
||||
public Vector3 WorldPosition => transform.position;
|
||||
public RectTransform RectTransform => transform as RectTransform;
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Initialize();
|
||||
}
|
||||
|
||||
protected virtual void Initialize()
|
||||
{
|
||||
Debug.Log($"[DraggableObject] Initializing {name} at world pos {transform.position}, local pos {transform.localPosition}, parent: {(transform.parent != null ? transform.parent.name : "NULL")}");
|
||||
|
||||
_canvas = GetComponentInParent<Canvas>();
|
||||
Debug.Log($"[DraggableObject] {name} found canvas: {(_canvas != null ? _canvas.name : "NULL")}, canvas pos: {(_canvas != null ? _canvas.transform.position.ToString() : "N/A")}");
|
||||
|
||||
_imageComponent = GetComponent<Image>();
|
||||
_canvasGroup = GetComponent<CanvasGroup>();
|
||||
_raycaster = _canvas?.GetComponent<GraphicRaycaster>();
|
||||
|
||||
// If no Image component exists, add an invisible one for raycast detection
|
||||
// Unity UI requires a Graphic component to receive pointer events
|
||||
if (_imageComponent == null && _canvasGroup != null)
|
||||
{
|
||||
_imageComponent = gameObject.AddComponent<Image>();
|
||||
_imageComponent.color = new Color(1, 1, 1, 0.01f); // Nearly transparent (0 doesn't work)
|
||||
_imageComponent.raycastTarget = true;
|
||||
Debug.Log($"[DraggableObject] Added invisible Image to {name} for raycast detection");
|
||||
}
|
||||
|
||||
// Use assigned visual, or find in children recursively if not assigned
|
||||
if (visual != null)
|
||||
{
|
||||
_visualInstance = visual;
|
||||
}
|
||||
else
|
||||
{
|
||||
_visualInstance = GetComponentInChildren<DraggableVisual>(true);
|
||||
}
|
||||
|
||||
// Initialize the visual if found
|
||||
if (_visualInstance != null)
|
||||
{
|
||||
_visualInstance.Initialize(this);
|
||||
}
|
||||
|
||||
// If we're already in a slot, register with it
|
||||
DraggableSlot parentSlot = GetComponentInParent<DraggableSlot>();
|
||||
if (parentSlot != null)
|
||||
{
|
||||
AssignToSlot(parentSlot, false);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (_isDragging && smoothMovement)
|
||||
{
|
||||
SmoothMoveTowardPointer();
|
||||
}
|
||||
|
||||
// Only clamp for non-overlay canvases (WorldSpace/ScreenSpaceCamera)
|
||||
if (_canvas != null && _canvas.renderMode != RenderMode.ScreenSpaceOverlay)
|
||||
{
|
||||
ClampToScreen();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SmoothMoveTowardPointer()
|
||||
{
|
||||
if (RectTransform == null)
|
||||
return;
|
||||
|
||||
// For ScreenSpaceOverlay, work with screen/anchoredPosition
|
||||
if (_canvas != null && _canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
{
|
||||
Vector2 targetPos = (Vector2)_lastPointerPosition - (Vector2)_dragOffset;
|
||||
Vector2 currentPos = RectTransform.position;
|
||||
Vector2 direction = (targetPos - currentPos).normalized;
|
||||
float distance = Vector2.Distance(currentPos, targetPos);
|
||||
float speed = Mathf.Min(moveSpeed, distance / Time.deltaTime);
|
||||
|
||||
RectTransform.position = currentPos + direction * speed * Time.deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For WorldSpace/ScreenSpaceCamera, use world coordinates
|
||||
Vector3 targetPosition = _lastPointerPosition - _dragOffset;
|
||||
Vector3 direction = (targetPosition - transform.position).normalized;
|
||||
float distance = Vector3.Distance(transform.position, targetPosition);
|
||||
float speed = Mathf.Min(moveSpeed, distance / Time.deltaTime);
|
||||
|
||||
transform.Translate(direction * speed * Time.deltaTime, Space.World);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ClampToScreen()
|
||||
{
|
||||
// This method is only called for WorldSpace/ScreenSpaceCamera canvases
|
||||
if (Camera.main == null || RectTransform == null)
|
||||
return;
|
||||
|
||||
Vector3[] corners = new Vector3[4];
|
||||
RectTransform.GetWorldCorners(corners);
|
||||
|
||||
// Simple clamping - can be improved
|
||||
Vector3 clampedPosition = transform.position;
|
||||
Vector2 screenBounds = Camera.main.ScreenToWorldPoint(new Vector3(Screen.width, Screen.height, 0));
|
||||
|
||||
clampedPosition.x = Mathf.Clamp(clampedPosition.x, -screenBounds.x, screenBounds.x);
|
||||
clampedPosition.y = Mathf.Clamp(clampedPosition.y, -screenBounds.y, screenBounds.y);
|
||||
|
||||
transform.position = clampedPosition;
|
||||
}
|
||||
|
||||
#region Unity Pointer Event Handlers
|
||||
|
||||
public virtual void OnBeginDrag(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.button != PointerEventData.InputButton.Left)
|
||||
return;
|
||||
|
||||
// Check if dragging is enabled BEFORE setting any state
|
||||
if (!_isDraggingEnabled)
|
||||
return;
|
||||
|
||||
_isDragging = true;
|
||||
_wasDragged = true;
|
||||
|
||||
// ...existing code...
|
||||
if (_canvas != null && _canvas.renderMode == RenderMode.ScreenSpaceOverlay && RectTransform != null)
|
||||
{
|
||||
// For overlay, use screen position directly
|
||||
_dragOffset = (Vector3)eventData.position - RectTransform.position;
|
||||
_lastPointerPosition = eventData.position;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For WorldSpace/ScreenSpaceCamera, convert to world coords
|
||||
Vector3 worldPointer = GetWorldPosition(eventData);
|
||||
_dragOffset = worldPointer - transform.position;
|
||||
_lastPointerPosition = worldPointer;
|
||||
}
|
||||
|
||||
// Reset base rotation to identity (0°) for clean dragging
|
||||
Tween.Rotation(transform, Quaternion.identity, 0.2f, 0f, Tween.EaseOutBack);
|
||||
|
||||
// Disable raycasting to allow detecting slots underneath
|
||||
if (_raycaster != null)
|
||||
_raycaster.enabled = false;
|
||||
if (_imageComponent != null)
|
||||
_imageComponent.raycastTarget = false;
|
||||
if (_canvasGroup != null)
|
||||
_canvasGroup.blocksRaycasts = false;
|
||||
|
||||
// Notify current slot we're leaving
|
||||
if (_currentSlot != null)
|
||||
{
|
||||
_currentSlot.Vacate();
|
||||
}
|
||||
|
||||
OnDragStarted?.Invoke(this);
|
||||
OnDragStartedHook();
|
||||
}
|
||||
|
||||
public virtual void OnDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!_isDragging)
|
||||
return;
|
||||
|
||||
// Update last pointer position based on canvas type
|
||||
if (_canvas != null && _canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
{
|
||||
_lastPointerPosition = eventData.position;
|
||||
|
||||
if (!smoothMovement && RectTransform != null)
|
||||
{
|
||||
RectTransform.position = (Vector2)_lastPointerPosition - (Vector2)_dragOffset;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastPointerPosition = GetWorldPosition(eventData);
|
||||
|
||||
if (!smoothMovement)
|
||||
{
|
||||
transform.position = _lastPointerPosition - _dragOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnEndDrag(PointerEventData eventData)
|
||||
{
|
||||
if (!_isDragging)
|
||||
return;
|
||||
|
||||
_isDragging = false;
|
||||
|
||||
// Re-enable raycasting
|
||||
if (_raycaster != null)
|
||||
_raycaster.enabled = true;
|
||||
if (_imageComponent != null)
|
||||
_imageComponent.raycastTarget = true;
|
||||
if (_canvasGroup != null)
|
||||
_canvasGroup.blocksRaycasts = true;
|
||||
|
||||
// Find closest slot and snap
|
||||
FindAndSnapToSlot();
|
||||
|
||||
// Snap base rotation back to slot rotation (if in a slot)
|
||||
if (_currentSlot != null)
|
||||
{
|
||||
Tween.Rotation(transform, _currentSlot.transform.rotation, 0.3f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
OnDragEnded?.Invoke(this);
|
||||
OnDragEndedHook();
|
||||
|
||||
// Reset wasDragged after a frame
|
||||
StartCoroutine(ResetWasDraggedFlag());
|
||||
}
|
||||
|
||||
public virtual void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
_isHovering = true;
|
||||
OnPointerEntered?.Invoke(this);
|
||||
OnPointerEnterHook();
|
||||
}
|
||||
|
||||
public virtual void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
_isHovering = false;
|
||||
OnPointerExited?.Invoke(this);
|
||||
OnPointerExitHook();
|
||||
}
|
||||
|
||||
public virtual void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.button != PointerEventData.InputButton.Left)
|
||||
return;
|
||||
|
||||
_pointerDownTime = Time.time;
|
||||
OnPointerDowned?.Invoke(this);
|
||||
OnPointerDownHook();
|
||||
}
|
||||
|
||||
public virtual void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.button != PointerEventData.InputButton.Left)
|
||||
return;
|
||||
|
||||
_pointerUpTime = Time.time;
|
||||
bool isLongPress = (_pointerUpTime - _pointerDownTime) > 0.2f;
|
||||
|
||||
OnPointerUpped?.Invoke(this, isLongPress);
|
||||
OnPointerUpHook(isLongPress);
|
||||
|
||||
// Handle selection (only if not long press and not dragged)
|
||||
if (!isLongPress && !_wasDragged && isSelectable)
|
||||
{
|
||||
ToggleSelection();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Slot Management
|
||||
|
||||
protected virtual void FindAndSnapToSlot()
|
||||
{
|
||||
SlotContainer[] containers = FindObjectsOfType<SlotContainer>();
|
||||
DraggableSlot closestSlot = null;
|
||||
float closestDistance = float.MaxValue;
|
||||
|
||||
// Use RectTransform.position for overlay, transform.position for others
|
||||
Vector3 myPosition = (_canvas != null && _canvas.renderMode == RenderMode.ScreenSpaceOverlay && RectTransform != null)
|
||||
? RectTransform.position
|
||||
: transform.position;
|
||||
|
||||
foreach (var container in containers)
|
||||
{
|
||||
DraggableSlot slot = container.FindClosestSlot(myPosition, this);
|
||||
if (slot != null)
|
||||
{
|
||||
Vector3 slotPosition = slot.RectTransform != null ? slot.RectTransform.position : slot.transform.position;
|
||||
float distance = Vector3.Distance(myPosition, slotPosition);
|
||||
if (distance < closestDistance)
|
||||
{
|
||||
closestDistance = distance;
|
||||
closestSlot = slot;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (closestSlot != null)
|
||||
{
|
||||
// Check if slot is occupied
|
||||
if (closestSlot.IsOccupied && closestSlot.Occupant != this)
|
||||
{
|
||||
// Swap with occupant
|
||||
SwapWithSlot(closestSlot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move to empty slot
|
||||
AssignToSlot(closestSlot, true);
|
||||
}
|
||||
}
|
||||
else if (_currentSlot != null)
|
||||
{
|
||||
// Return to current slot if no valid slot found
|
||||
SnapToCurrentSlot();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SwapWithSlot(DraggableSlot targetSlot)
|
||||
{
|
||||
DraggableSlot mySlot = _currentSlot;
|
||||
DraggableObject otherObject = targetSlot.Occupant;
|
||||
|
||||
if (otherObject != null)
|
||||
{
|
||||
// Both objects swap slots
|
||||
targetSlot.Vacate();
|
||||
if (mySlot != null)
|
||||
mySlot.Vacate();
|
||||
|
||||
AssignToSlot(targetSlot, true);
|
||||
if (mySlot != null)
|
||||
otherObject.AssignToSlot(mySlot, true);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AssignToSlot(DraggableSlot slot, bool animate)
|
||||
{
|
||||
if (slot == null)
|
||||
return;
|
||||
|
||||
Debug.Log($"[DraggableObject] Assigning {name} to slot {slot.name}, animate={animate}, current pos={transform.position}, slot pos={slot.transform.position}");
|
||||
|
||||
DraggableSlot previousSlot = _currentSlot;
|
||||
_currentSlot = slot;
|
||||
|
||||
if (slot.Occupy(this))
|
||||
{
|
||||
if (animate)
|
||||
{
|
||||
SnapToSlot(slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.SetParent(slot.transform);
|
||||
transform.localPosition = _isSelected ? new Vector3(0, selectionOffset, 0) : Vector3.zero;
|
||||
transform.localRotation = Quaternion.identity;
|
||||
Debug.Log($"[DraggableObject] {name} assigned to slot {slot.name}, new world pos={transform.position}, local pos={transform.localPosition}");
|
||||
}
|
||||
|
||||
OnSlotChanged?.Invoke(this, slot);
|
||||
OnSlotChangedHook(previousSlot, slot);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SnapToSlot(DraggableSlot slot)
|
||||
{
|
||||
transform.SetParent(slot.transform);
|
||||
|
||||
Vector3 targetLocalPos = _isSelected ? new Vector3(0, selectionOffset, 0) : Vector3.zero;
|
||||
|
||||
if (RectTransform != null)
|
||||
{
|
||||
Tween.LocalPosition(RectTransform, targetLocalPos, snapDuration, 0f, Tween.EaseOutBack);
|
||||
Tween.LocalRotation(transform, Quaternion.identity, snapDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.localPosition = targetLocalPos;
|
||||
transform.localRotation = Quaternion.identity;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void SnapToCurrentSlot()
|
||||
{
|
||||
if (_currentSlot != null)
|
||||
{
|
||||
SnapToSlot(_currentSlot);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Selection
|
||||
|
||||
public virtual void ToggleSelection()
|
||||
{
|
||||
SetSelected(!_isSelected);
|
||||
}
|
||||
|
||||
public virtual void SetSelected(bool selected)
|
||||
{
|
||||
if (!isSelectable)
|
||||
return;
|
||||
|
||||
_isSelected = selected;
|
||||
|
||||
// Update position based on selection
|
||||
Vector3 targetLocalPos = _isSelected ? new Vector3(0, selectionOffset, 0) : Vector3.zero;
|
||||
|
||||
if (RectTransform != null && _currentSlot != null)
|
||||
{
|
||||
Tween.LocalPosition(RectTransform, targetLocalPos, 0.15f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
OnSelected?.Invoke(this, _isSelected);
|
||||
OnSelectionChangedHook(_isSelected);
|
||||
}
|
||||
|
||||
public virtual void Deselect()
|
||||
{
|
||||
SetSelected(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dragging Control
|
||||
|
||||
/// <summary>
|
||||
/// Enable or disable dragging functionality
|
||||
/// </summary>
|
||||
public virtual void SetDraggingEnabled(bool enabled)
|
||||
{
|
||||
_isDraggingEnabled = enabled;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Methods
|
||||
|
||||
protected Vector3 GetWorldPosition(PointerEventData eventData)
|
||||
{
|
||||
if (Camera.main == null)
|
||||
return Vector3.zero;
|
||||
|
||||
// For screen space overlay canvas
|
||||
if (_canvas != null && _canvas.renderMode == RenderMode.ScreenSpaceOverlay)
|
||||
{
|
||||
return eventData.position;
|
||||
}
|
||||
|
||||
// For world space or camera space
|
||||
return Camera.main.ScreenToWorldPoint(new Vector3(eventData.position.x, eventData.position.y, _canvas.planeDistance));
|
||||
}
|
||||
|
||||
protected IEnumerator ResetWasDraggedFlag()
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
_wasDragged = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Abstract/Virtual Hooks for Subclasses
|
||||
|
||||
protected virtual void OnDragStartedHook() { }
|
||||
protected virtual void OnDragEndedHook() { }
|
||||
protected virtual void OnPointerEnterHook() { }
|
||||
protected virtual void OnPointerExitHook() { }
|
||||
protected virtual void OnPointerDownHook() { }
|
||||
protected virtual void OnPointerUpHook(bool longPress) { }
|
||||
protected virtual void OnSelectionChangedHook(bool selected) { }
|
||||
protected virtual void OnSlotChangedHook(DraggableSlot previousSlot, DraggableSlot newSlot) { }
|
||||
|
||||
#endregion
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
if (_visualInstance != null)
|
||||
{
|
||||
Destroy(_visualInstance.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
public int GetSiblingCount()
|
||||
{
|
||||
return _currentSlot != null && _currentSlot.transform.parent != null
|
||||
? _currentSlot.transform.parent.childCount - 1
|
||||
: 0;
|
||||
}
|
||||
|
||||
public int GetSlotIndex()
|
||||
{
|
||||
return _currentSlot != null ? _currentSlot.SlotIndex : 0;
|
||||
}
|
||||
|
||||
public float GetNormalizedSlotPosition()
|
||||
{
|
||||
if (_currentSlot == null || _currentSlot.transform.parent == null)
|
||||
return 0f;
|
||||
|
||||
int siblingCount = _currentSlot.transform.parent.childCount - 1;
|
||||
if (siblingCount <= 0)
|
||||
return 0f;
|
||||
|
||||
return (float)_currentSlot.SlotIndex / siblingCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 062198d83a0940538c140da0999f4de9
|
||||
timeCreated: 1762420597
|
||||
182
Assets/Scripts/UI/DragAndDrop/Core/DraggableSlot.cs
Normal file
182
Assets/Scripts/UI/DragAndDrop/Core/DraggableSlot.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using Pixelplacement;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.DragAndDrop.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a position where draggable objects can snap to.
|
||||
/// Can be occupied by one DraggableObject at a time.
|
||||
/// </summary>
|
||||
public class DraggableSlot : MonoBehaviour
|
||||
{
|
||||
public enum OccupantSizeMode
|
||||
{
|
||||
None, // Don't modify occupant size
|
||||
MatchSlotSize, // Set occupant RectTransform size to match slot size
|
||||
Scale // Apply scale multiplier to occupant
|
||||
}
|
||||
|
||||
[Header("Slot Settings")]
|
||||
[SerializeField] private int slotIndex;
|
||||
[SerializeField] private bool isLocked;
|
||||
[SerializeField] private bool hideImageOnPlay = false;
|
||||
|
||||
[Header("Type Filtering")]
|
||||
[SerializeField] private bool filterByType;
|
||||
[SerializeField] private string[] allowedTypeNames;
|
||||
|
||||
[Header("Occupant Size Control")]
|
||||
[SerializeField] private OccupantSizeMode occupantSizeMode = OccupantSizeMode.None;
|
||||
[SerializeField] private Vector3 occupantScale = Vector3.one;
|
||||
[SerializeField] private float scaleTransitionDuration = 0.3f;
|
||||
|
||||
// Current occupant
|
||||
private DraggableObject _occupant;
|
||||
|
||||
// Events
|
||||
public event Action<DraggableObject> OnOccupied;
|
||||
public event Action<DraggableObject> OnVacated;
|
||||
|
||||
public int SlotIndex => slotIndex;
|
||||
public bool IsOccupied => _occupant != null;
|
||||
public bool IsLocked => isLocked;
|
||||
public DraggableObject Occupant => _occupant;
|
||||
public Vector3 WorldPosition => transform.position;
|
||||
public RectTransform RectTransform => transform as RectTransform;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (hideImageOnPlay)
|
||||
{
|
||||
UnityEngine.UI.Image image = GetComponent<UnityEngine.UI.Image>();
|
||||
if (image != null)
|
||||
{
|
||||
Destroy(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to occupy this slot with a draggable object
|
||||
/// </summary>
|
||||
public bool Occupy(DraggableObject draggable)
|
||||
{
|
||||
if (isLocked)
|
||||
return false;
|
||||
|
||||
if (!CanAccept(draggable))
|
||||
return false;
|
||||
|
||||
if (_occupant != null && _occupant != draggable)
|
||||
return false;
|
||||
|
||||
_occupant = draggable;
|
||||
draggable.transform.SetParent(transform);
|
||||
|
||||
// Apply size modification based on mode
|
||||
switch (occupantSizeMode)
|
||||
{
|
||||
case OccupantSizeMode.MatchSlotSize:
|
||||
if (draggable.RectTransform != null && RectTransform != null)
|
||||
{
|
||||
Vector2 targetSize = RectTransform.sizeDelta;
|
||||
Tween.Value(draggable.RectTransform.sizeDelta, targetSize,
|
||||
(val) => draggable.RectTransform.sizeDelta = val,
|
||||
scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
break;
|
||||
|
||||
case OccupantSizeMode.Scale:
|
||||
Tween.LocalScale(draggable.transform, occupantScale, scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
|
||||
// Also scale the visual if it exists (since visual is now independent)
|
||||
if (draggable.Visual != null)
|
||||
{
|
||||
Tween.LocalScale(draggable.Visual.transform, occupantScale, scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
break;
|
||||
|
||||
case OccupantSizeMode.None:
|
||||
default:
|
||||
// Don't modify size
|
||||
break;
|
||||
}
|
||||
|
||||
OnOccupied?.Invoke(draggable);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Vacate this slot, removing the current occupant
|
||||
/// </summary>
|
||||
public void Vacate()
|
||||
{
|
||||
if (_occupant != null)
|
||||
{
|
||||
DraggableObject previousOccupant = _occupant;
|
||||
_occupant = null;
|
||||
OnVacated?.Invoke(previousOccupant);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this slot can accept a specific draggable type
|
||||
/// </summary>
|
||||
public bool CanAccept(DraggableObject draggable)
|
||||
{
|
||||
if (!filterByType || allowedTypeNames == null || allowedTypeNames.Length == 0)
|
||||
return true;
|
||||
|
||||
string draggableTypeName = draggable.GetType().Name;
|
||||
|
||||
foreach (string allowedType in allowedTypeNames)
|
||||
{
|
||||
if (draggableTypeName == allowedType)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Swap occupants with another slot
|
||||
/// </summary>
|
||||
public void SwapWith(DraggableSlot otherSlot)
|
||||
{
|
||||
if (otherSlot == null || otherSlot == this)
|
||||
return;
|
||||
|
||||
DraggableObject thisOccupant = _occupant;
|
||||
DraggableObject otherOccupant = otherSlot._occupant;
|
||||
|
||||
// Vacate both slots
|
||||
Vacate();
|
||||
otherSlot.Vacate();
|
||||
|
||||
// Occupy with swapped objects
|
||||
if (otherOccupant != null)
|
||||
Occupy(otherOccupant);
|
||||
|
||||
if (thisOccupant != null)
|
||||
otherSlot.Occupy(thisOccupant);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lock/unlock this slot
|
||||
/// </summary>
|
||||
public void SetLocked(bool locked)
|
||||
{
|
||||
isLocked = locked;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the slot index
|
||||
/// </summary>
|
||||
public void SetSlotIndex(int index)
|
||||
{
|
||||
slotIndex = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Scripts/UI/DragAndDrop/Core/DraggableSlot.cs.meta
Normal file
3
Assets/Scripts/UI/DragAndDrop/Core/DraggableSlot.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee43b700f9dd44dba39deb8c5bcd688c
|
||||
timeCreated: 1762420738
|
||||
529
Assets/Scripts/UI/DragAndDrop/Core/DraggableVisual.cs
Normal file
529
Assets/Scripts/UI/DragAndDrop/Core/DraggableVisual.cs
Normal file
@@ -0,0 +1,529 @@
|
||||
using Pixelplacement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem; // Added for new Input System
|
||||
|
||||
namespace UI.DragAndDrop.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Abstract base class for visual representation of draggable objects.
|
||||
/// Follows the parent DraggableObject with lerping and visual effects.
|
||||
/// Inspired by Balatro's CardVisual system.
|
||||
/// </summary>
|
||||
public abstract class DraggableVisual : MonoBehaviour
|
||||
{
|
||||
[Header("References")]
|
||||
[SerializeField] protected Canvas canvas;
|
||||
[SerializeField] protected CanvasGroup canvasGroup;
|
||||
[SerializeField] protected Transform tiltParent;
|
||||
[SerializeField] protected Transform shakeParent;
|
||||
|
||||
[Header("Follow Parameters")]
|
||||
[SerializeField] protected float followSpeed = 30f;
|
||||
[SerializeField] protected bool useFollowDelay = true;
|
||||
|
||||
[Header("Rotation/Tilt Parameters")]
|
||||
[SerializeField] protected float rotationAmount = 20f;
|
||||
[SerializeField] protected float rotationSpeed = 20f;
|
||||
[SerializeField] protected float autoTiltAmount = 10f; // Reduced from 30f
|
||||
[SerializeField] protected float manualTiltAmount = 20f;
|
||||
[SerializeField] protected float tiltSpeed = 20f;
|
||||
|
||||
[Header("Scale Parameters")]
|
||||
[SerializeField] protected bool useScaleAnimations = true;
|
||||
[SerializeField] protected float scaleOnHover = 1.15f;
|
||||
[SerializeField] protected float scaleOnDrag = 1.25f;
|
||||
[SerializeField] protected float scaleTransitionDuration = 0.15f;
|
||||
|
||||
[Header("Idle Animation")]
|
||||
[SerializeField] protected bool useIdleAnimation = true;
|
||||
[SerializeField] protected float idleAnimationSpeed = 0.5f; // Slowed down from 1f
|
||||
|
||||
// State
|
||||
protected DraggableObject _parentDraggable;
|
||||
protected bool _isInitialized;
|
||||
protected Vector3 _movementDelta;
|
||||
protected Vector3 _rotationDelta;
|
||||
protected int _savedSlotIndex;
|
||||
protected Vector3 _lastPosition;
|
||||
|
||||
// Properties
|
||||
public DraggableObject ParentDraggable => _parentDraggable;
|
||||
public bool IsInitialized => _isInitialized;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the visual with its parent draggable object
|
||||
/// </summary>
|
||||
public virtual void Initialize(DraggableObject parent)
|
||||
{
|
||||
_parentDraggable = parent;
|
||||
|
||||
Canvas parentCanvas = parent.GetComponentInParent<Canvas>();
|
||||
Debug.Log($"[DraggableVisual] Initializing visual for {parent.name} at world pos {parent.transform.position}, parent canvas: {(parentCanvas != null ? parentCanvas.name + " (renderMode: " + parentCanvas.renderMode + ")" : "NULL")}");
|
||||
|
||||
// CRITICAL: Reparent visual to canvas (not base) so it can move independently
|
||||
// This enables the delayed follow effect
|
||||
if (parentCanvas != null)
|
||||
{
|
||||
transform.SetParent(parentCanvas.transform, true); // worldPositionStays = true
|
||||
Debug.Log($"[DraggableVisual] Reparented visual {name} to canvas {parentCanvas.name} for independent movement");
|
||||
}
|
||||
|
||||
// Get components if assigned (don't auto-create Canvas to avoid Unity's auto-reparenting)
|
||||
if (canvas == null)
|
||||
canvas = GetComponent<Canvas>();
|
||||
|
||||
if (canvasGroup == null)
|
||||
canvasGroup = GetComponent<CanvasGroup>();
|
||||
if (canvasGroup == null)
|
||||
canvasGroup = gameObject.AddComponent<CanvasGroup>();
|
||||
|
||||
// Subscribe to parent events
|
||||
SubscribeToParentEvents();
|
||||
|
||||
// Initial position to match parent
|
||||
transform.position = parent.transform.position;
|
||||
_lastPosition = transform.position;
|
||||
|
||||
// Set rotation to match base's current rotation (will be maintained via separate rotation management)
|
||||
transform.rotation = parent.transform.rotation;
|
||||
|
||||
// Reset shake and tilt parent rotations to zero (local space) for clean wobble
|
||||
if (shakeParent != null)
|
||||
{
|
||||
shakeParent.localRotation = Quaternion.identity;
|
||||
}
|
||||
if (tiltParent != null)
|
||||
{
|
||||
tiltParent.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
Debug.Log($"[DraggableVisual] Visual {name} initialized at world pos {transform.position}, local pos {transform.localPosition}, local rotation {transform.localRotation.eulerAngles}, parent at world pos {parent.transform.position}, local pos {parent.transform.localPosition}, rotation {parent.transform.rotation.eulerAngles}");
|
||||
|
||||
_isInitialized = true;
|
||||
|
||||
OnInitialized();
|
||||
}
|
||||
|
||||
protected virtual void SubscribeToParentEvents()
|
||||
{
|
||||
if (_parentDraggable == null)
|
||||
return;
|
||||
|
||||
_parentDraggable.OnDragStarted += HandleDragStarted;
|
||||
_parentDraggable.OnDragEnded += HandleDragEnded;
|
||||
_parentDraggable.OnPointerEntered += HandlePointerEnter;
|
||||
_parentDraggable.OnPointerExited += HandlePointerExit;
|
||||
_parentDraggable.OnPointerDowned += HandlePointerDown;
|
||||
_parentDraggable.OnPointerUpped += HandlePointerUp;
|
||||
_parentDraggable.OnSelected += HandleSelection;
|
||||
}
|
||||
|
||||
protected virtual void UnsubscribeFromParentEvents()
|
||||
{
|
||||
if (_parentDraggable == null)
|
||||
return;
|
||||
|
||||
_parentDraggable.OnDragStarted -= HandleDragStarted;
|
||||
_parentDraggable.OnDragEnded -= HandleDragEnded;
|
||||
_parentDraggable.OnPointerEntered -= HandlePointerEnter;
|
||||
_parentDraggable.OnPointerExited -= HandlePointerExit;
|
||||
_parentDraggable.OnPointerDowned -= HandlePointerDown;
|
||||
_parentDraggable.OnPointerUpped -= HandlePointerUp;
|
||||
_parentDraggable.OnSelected -= HandleSelection;
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (!_isInitialized || _parentDraggable == null)
|
||||
return;
|
||||
|
||||
UpdateFollowPosition();
|
||||
UpdateFollowRotation(); // Track base rotation changes
|
||||
UpdateRotation();
|
||||
UpdateTilt();
|
||||
UpdateVisualContent();
|
||||
}
|
||||
|
||||
#region Position & Movement
|
||||
|
||||
protected virtual void UpdateFollowRotation()
|
||||
{
|
||||
if (_parentDraggable == null)
|
||||
return;
|
||||
|
||||
// Smoothly follow base rotation (since we're no longer a child)
|
||||
// Base rotation changes when picking up (→ 0°) or dropping (→ slot rotation)
|
||||
transform.rotation = Quaternion.Lerp(transform.rotation, _parentDraggable.transform.rotation, 10f * Time.deltaTime);
|
||||
}
|
||||
|
||||
protected virtual void UpdateFollowPosition()
|
||||
{
|
||||
if (_parentDraggable == null)
|
||||
return;
|
||||
|
||||
// For ScreenSpaceOverlay, use RectTransform.position consistently
|
||||
Canvas parentCanvas = _parentDraggable.GetComponentInParent<Canvas>();
|
||||
bool isOverlay = parentCanvas != null && parentCanvas.renderMode == RenderMode.ScreenSpaceOverlay;
|
||||
|
||||
Vector3 targetPosition;
|
||||
Vector3 currentPosition;
|
||||
|
||||
if (isOverlay && _parentDraggable.RectTransform != null && GetComponent<RectTransform>() != null)
|
||||
{
|
||||
// Use RectTransform.position for overlay (screen space)
|
||||
RectTransform myRect = GetComponent<RectTransform>();
|
||||
targetPosition = _parentDraggable.RectTransform.position;
|
||||
currentPosition = myRect.position;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use transform.position for WorldSpace/ScreenSpaceCamera
|
||||
targetPosition = _parentDraggable.transform.position;
|
||||
currentPosition = transform.position;
|
||||
}
|
||||
|
||||
// Debug log if position is drastically different (likely a clustering issue)
|
||||
float distance = Vector3.Distance(currentPosition, targetPosition);
|
||||
if (distance > 500f)
|
||||
{
|
||||
Debug.LogWarning($"[DraggableVisual] Large position delta detected! Visual {name} at {currentPosition}, target {targetPosition}, parent {_parentDraggable.name}, distance: {distance}");
|
||||
}
|
||||
|
||||
// Apply follow logic with snappy easing
|
||||
if (useFollowDelay)
|
||||
{
|
||||
// Calculate lerp factor with snappy ease
|
||||
float rawT = followSpeed * Time.deltaTime;
|
||||
|
||||
// Apply EaseOutCubic for snappier movement: 1 - (1-t)^3
|
||||
float t = 1f - Mathf.Pow(1f - rawT, 3f);
|
||||
|
||||
Vector3 newPosition = Vector3.Lerp(currentPosition, targetPosition, t);
|
||||
|
||||
if (isOverlay && GetComponent<RectTransform>() != null)
|
||||
{
|
||||
GetComponent<RectTransform>().position = newPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position = newPosition;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isOverlay && GetComponent<RectTransform>() != null)
|
||||
{
|
||||
GetComponent<RectTransform>().position = targetPosition;
|
||||
}
|
||||
else
|
||||
{
|
||||
transform.position = targetPosition;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate movement delta for tilt
|
||||
Vector3 actualCurrentPos = isOverlay && GetComponent<RectTransform>() != null
|
||||
? GetComponent<RectTransform>().position
|
||||
: transform.position;
|
||||
Vector3 movement = actualCurrentPos - _lastPosition;
|
||||
_movementDelta = Vector3.Lerp(_movementDelta, movement, 25f * Time.deltaTime);
|
||||
_lastPosition = actualCurrentPos;
|
||||
}
|
||||
|
||||
protected virtual Vector3 GetTargetPosition()
|
||||
{
|
||||
Canvas parentCanvas = _parentDraggable.GetComponentInParent<Canvas>();
|
||||
bool isOverlay = parentCanvas != null && parentCanvas.renderMode == RenderMode.ScreenSpaceOverlay;
|
||||
|
||||
if (isOverlay && _parentDraggable.RectTransform != null)
|
||||
{
|
||||
return _parentDraggable.RectTransform.position;
|
||||
}
|
||||
|
||||
return _parentDraggable.transform.position;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Rotation & Tilt
|
||||
|
||||
protected virtual void UpdateRotation()
|
||||
{
|
||||
if (_parentDraggable == null || shakeParent == null)
|
||||
return;
|
||||
|
||||
// Apply rotation based on movement to shakeParent (not main transform)
|
||||
// This way it's additive on top of the base rotation in transform.rotation
|
||||
// Negated to make the bottom "drag behind" instead of leading
|
||||
Vector3 movementRotation = _movementDelta * -rotationAmount; // Flipped direction
|
||||
|
||||
_rotationDelta = Vector3.Lerp(_rotationDelta, movementRotation, rotationSpeed * Time.deltaTime);
|
||||
|
||||
// Apply Z-axis rotation to shakeParent as local rotation
|
||||
float clampedZ = Mathf.Clamp(_rotationDelta.x, -60f, 60f);
|
||||
shakeParent.localEulerAngles = new Vector3(0, 0, clampedZ);
|
||||
}
|
||||
|
||||
protected virtual void UpdateTilt()
|
||||
{
|
||||
if (tiltParent == null)
|
||||
return;
|
||||
|
||||
// Save slot index when not dragging for idle animation
|
||||
_savedSlotIndex = _parentDraggable.IsDragging
|
||||
? _savedSlotIndex
|
||||
: _parentDraggable.GetSlotIndex();
|
||||
|
||||
// Idle animation (sine/cosine wobble with different frequencies)
|
||||
float idleMultiplier = _parentDraggable.IsHovering ? 0.2f : 1f;
|
||||
float time = Time.time * idleAnimationSpeed + _savedSlotIndex;
|
||||
|
||||
// Use sine for X wobble, cosine for Y wobble (different phases)
|
||||
float sineWobbleX = Mathf.Sin(time) * idleMultiplier;
|
||||
float cosineWobbleY = Mathf.Cos(time) * idleMultiplier;
|
||||
|
||||
// Use slower cosine for Z rotation wobble (half speed for subtle rotation)
|
||||
float cosineWobbleZ = Mathf.Cos(time * 0.5f) * idleMultiplier * 0.3f; // Much subtler
|
||||
|
||||
// Manual tilt based on pointer position (when hovering)
|
||||
float manualTiltX = 0f;
|
||||
float manualTiltY = 0f;
|
||||
|
||||
if (_parentDraggable.IsHovering)
|
||||
{
|
||||
Canvas parentCanvas = _parentDraggable.GetComponentInParent<Canvas>();
|
||||
bool isOverlay = parentCanvas != null && parentCanvas.renderMode == RenderMode.ScreenSpaceOverlay;
|
||||
|
||||
Vector3 mousePos;
|
||||
Vector3 myPos;
|
||||
|
||||
// Get mouse position using new Input System
|
||||
Vector2 mouseScreenPos = Mouse.current != null ? Mouse.current.position.ReadValue() : Vector2.zero;
|
||||
|
||||
if (isOverlay)
|
||||
{
|
||||
// For overlay, use screen coordinates directly
|
||||
mousePos = mouseScreenPos;
|
||||
myPos = GetComponent<RectTransform>() != null
|
||||
? GetComponent<RectTransform>().position
|
||||
: transform.position;
|
||||
}
|
||||
else if (Camera.main != null)
|
||||
{
|
||||
// For WorldSpace/ScreenSpaceCamera, convert to world coords
|
||||
mousePos = Camera.main.ScreenToWorldPoint(mouseScreenPos);
|
||||
myPos = transform.position;
|
||||
}
|
||||
else
|
||||
{
|
||||
mousePos = myPos = Vector3.zero;
|
||||
}
|
||||
|
||||
Vector3 offset = myPos - mousePos;
|
||||
manualTiltX = (offset.y * -1f) * manualTiltAmount;
|
||||
manualTiltY = offset.x * manualTiltAmount;
|
||||
}
|
||||
|
||||
// Combine auto and manual tilt
|
||||
// X uses sine wobble, Y uses cosine wobble, Z uses slower cosine for rotation
|
||||
float targetTiltX = manualTiltX + (useIdleAnimation ? sineWobbleX * autoTiltAmount : 0f);
|
||||
float targetTiltY = manualTiltY + (useIdleAnimation ? cosineWobbleY * autoTiltAmount : 0f);
|
||||
float targetTiltZ = _parentDraggable.IsDragging ? tiltParent.localEulerAngles.z : (useIdleAnimation ? cosineWobbleZ * autoTiltAmount : 0f);
|
||||
|
||||
// Lerp to target tilt using LOCAL rotation
|
||||
float lerpX = Mathf.LerpAngle(tiltParent.localEulerAngles.x, targetTiltX, tiltSpeed * Time.deltaTime);
|
||||
float lerpY = Mathf.LerpAngle(tiltParent.localEulerAngles.y, targetTiltY, tiltSpeed * Time.deltaTime);
|
||||
float lerpZ = Mathf.LerpAngle(tiltParent.localEulerAngles.z, targetTiltZ, (tiltSpeed / 2f) * Time.deltaTime);
|
||||
|
||||
tiltParent.localEulerAngles = new Vector3(lerpX, lerpY, lerpZ);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
protected virtual void HandleDragStarted(DraggableObject draggable)
|
||||
{
|
||||
if (useScaleAnimations)
|
||||
{
|
||||
Tween.LocalScale(transform, Vector3.one * scaleOnDrag, scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
if (canvas != null)
|
||||
{
|
||||
canvas.overrideSorting = true;
|
||||
}
|
||||
|
||||
// Reset shake parent rotation (movement wobble)
|
||||
if (shakeParent != null)
|
||||
{
|
||||
Tween.LocalRotation(shakeParent, Quaternion.identity, 0.2f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
// Reset tilt parent rotation (idle wobble)
|
||||
if (tiltParent != null)
|
||||
{
|
||||
Tween.LocalRotation(tiltParent, Quaternion.identity, 0.2f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
// Reset rotation delta for fresh movement wobble
|
||||
_rotationDelta = Vector3.zero;
|
||||
|
||||
OnDragStartedVisual();
|
||||
}
|
||||
|
||||
protected virtual void HandleDragEnded(DraggableObject draggable)
|
||||
{
|
||||
if (canvas != null)
|
||||
{
|
||||
canvas.overrideSorting = false;
|
||||
}
|
||||
|
||||
// Only reset scale if NOT in a slot (let slots handle their own scaling)
|
||||
if (draggable.CurrentSlot == null)
|
||||
{
|
||||
Tween.LocalScale(transform, Vector3.one, scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
|
||||
// Reset shake parent (movement wobble) to zero for fresh start
|
||||
if (shakeParent != null)
|
||||
{
|
||||
Tween.LocalRotation(shakeParent, Quaternion.identity, 0.3f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
// Reset tilt parent (idle wobble) to zero for fresh start
|
||||
if (tiltParent != null)
|
||||
{
|
||||
Tween.LocalRotation(tiltParent, Quaternion.identity, 0.3f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
OnDragEndedVisual();
|
||||
}
|
||||
|
||||
protected virtual void HandlePointerEnter(DraggableObject draggable)
|
||||
{
|
||||
if (useScaleAnimations)
|
||||
{
|
||||
Tween.LocalScale(transform, Vector3.one * scaleOnHover, scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
// Punch rotation effect
|
||||
if (shakeParent != null)
|
||||
{
|
||||
Tween.Rotation(shakeParent, shakeParent.eulerAngles + Vector3.forward * 5f, 0.15f, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
OnPointerEnterVisual();
|
||||
}
|
||||
|
||||
protected virtual void HandlePointerExit(DraggableObject draggable)
|
||||
{
|
||||
if (!draggable.WasDragged && useScaleAnimations)
|
||||
{
|
||||
Tween.LocalScale(transform, Vector3.one, scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
OnPointerExitVisual();
|
||||
}
|
||||
|
||||
protected virtual void HandlePointerDown(DraggableObject draggable)
|
||||
{
|
||||
if (useScaleAnimations)
|
||||
{
|
||||
Tween.LocalScale(transform, Vector3.one * scaleOnDrag, scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
OnPointerDownVisual();
|
||||
}
|
||||
|
||||
protected virtual void HandlePointerUp(DraggableObject draggable, bool longPress)
|
||||
{
|
||||
float targetScale = longPress ? scaleOnHover : scaleOnDrag;
|
||||
|
||||
if (useScaleAnimations)
|
||||
{
|
||||
Tween.LocalScale(transform, Vector3.one * targetScale, scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
OnPointerUpVisual(longPress);
|
||||
}
|
||||
|
||||
protected virtual void HandleSelection(DraggableObject draggable, bool selected)
|
||||
{
|
||||
// Punch effect on selection
|
||||
if (shakeParent != null && selected)
|
||||
{
|
||||
Vector3 punchAmount = shakeParent.up * 20f;
|
||||
Tween.Position(shakeParent, shakeParent.position + punchAmount, 0.15f, 0f, Tween.EaseOutBack,
|
||||
completeCallback: () => Tween.Position(shakeParent, shakeParent.position - punchAmount, 0.15f, 0f, Tween.EaseInBack));
|
||||
}
|
||||
|
||||
if (useScaleAnimations)
|
||||
{
|
||||
float targetScale = selected ? scaleOnDrag : scaleOnHover;
|
||||
Tween.LocalScale(transform, Vector3.one * targetScale, scaleTransitionDuration, 0f, Tween.EaseOutBack);
|
||||
}
|
||||
|
||||
OnSelectionVisual(selected);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Abstract/Virtual Hooks for Subclasses
|
||||
|
||||
/// <summary>
|
||||
/// Called after initialization is complete
|
||||
/// </summary>
|
||||
protected virtual void OnInitialized() { }
|
||||
|
||||
/// <summary>
|
||||
/// Update the actual visual content (sprites, UI elements, etc.)
|
||||
/// Called every frame
|
||||
/// </summary>
|
||||
protected abstract void UpdateVisualContent();
|
||||
|
||||
/// <summary>
|
||||
/// Visual-specific behavior when drag starts
|
||||
/// </summary>
|
||||
protected virtual void OnDragStartedVisual() { }
|
||||
|
||||
/// <summary>
|
||||
/// Visual-specific behavior when drag ends
|
||||
/// </summary>
|
||||
protected virtual void OnDragEndedVisual() { }
|
||||
|
||||
/// <summary>
|
||||
/// Visual-specific behavior when pointer enters
|
||||
/// </summary>
|
||||
protected virtual void OnPointerEnterVisual() { }
|
||||
|
||||
/// <summary>
|
||||
/// Visual-specific behavior when pointer exits
|
||||
/// </summary>
|
||||
protected virtual void OnPointerExitVisual() { }
|
||||
|
||||
/// <summary>
|
||||
/// Visual-specific behavior when pointer down
|
||||
/// </summary>
|
||||
protected virtual void OnPointerDownVisual() { }
|
||||
|
||||
/// <summary>
|
||||
/// Visual-specific behavior when pointer up
|
||||
/// </summary>
|
||||
protected virtual void OnPointerUpVisual(bool longPress) { }
|
||||
|
||||
/// <summary>
|
||||
/// Visual-specific behavior when selection changes
|
||||
/// </summary>
|
||||
protected virtual void OnSelectionVisual(bool selected) { }
|
||||
|
||||
#endregion
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
UnsubscribeFromParentEvents();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 203c649ed73845c6999682bcf8383ee8
|
||||
timeCreated: 1762420644
|
||||
258
Assets/Scripts/UI/DragAndDrop/Core/SlotContainer.cs
Normal file
258
Assets/Scripts/UI/DragAndDrop/Core/SlotContainer.cs
Normal file
@@ -0,0 +1,258 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UI.DragAndDrop.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages a collection of DraggableSlots.
|
||||
/// Handles layout, slot finding, and reordering.
|
||||
/// </summary>
|
||||
public class SlotContainer : MonoBehaviour
|
||||
{
|
||||
[Header("Container Settings")]
|
||||
[SerializeField] private LayoutType layoutType = LayoutType.Horizontal;
|
||||
[SerializeField] private float spacing = 100f;
|
||||
[SerializeField] private bool centerSlots = true;
|
||||
[SerializeField] private bool autoRegisterChildren = true;
|
||||
|
||||
[Header("Curve Layout (Horizontal Only)")]
|
||||
[SerializeField] private bool useCurveLayout;
|
||||
[SerializeField] private AnimationCurve positionCurve = AnimationCurve.Linear(0, 0, 1, 0);
|
||||
[SerializeField] private float curveHeight = 50f;
|
||||
|
||||
private List<DraggableSlot> _slots = new List<DraggableSlot>();
|
||||
|
||||
public enum LayoutType
|
||||
{
|
||||
Horizontal,
|
||||
Vertical,
|
||||
Grid,
|
||||
Custom
|
||||
}
|
||||
|
||||
// Events
|
||||
public event Action<DraggableSlot> OnSlotAdded;
|
||||
public event Action<DraggableSlot> OnSlotRemoved;
|
||||
public event Action OnLayoutChanged;
|
||||
|
||||
public List<DraggableSlot> Slots => _slots;
|
||||
public int SlotCount => _slots.Count;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (autoRegisterChildren)
|
||||
{
|
||||
RegisterChildSlots();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Automatically register all child DraggableSlot components
|
||||
/// </summary>
|
||||
private void RegisterChildSlots()
|
||||
{
|
||||
DraggableSlot[] childSlots = GetComponentsInChildren<DraggableSlot>();
|
||||
foreach (var slot in childSlots)
|
||||
{
|
||||
RegisterSlot(slot);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a slot with this container
|
||||
/// </summary>
|
||||
public void RegisterSlot(DraggableSlot slot)
|
||||
{
|
||||
if (slot == null || _slots.Contains(slot))
|
||||
return;
|
||||
|
||||
_slots.Add(slot);
|
||||
slot.SetSlotIndex(_slots.Count - 1);
|
||||
|
||||
OnSlotAdded?.Invoke(slot);
|
||||
UpdateLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregister a slot from this container
|
||||
/// </summary>
|
||||
public void UnregisterSlot(DraggableSlot slot)
|
||||
{
|
||||
if (slot == null || !_slots.Contains(slot))
|
||||
return;
|
||||
|
||||
_slots.Remove(slot);
|
||||
OnSlotRemoved?.Invoke(slot);
|
||||
|
||||
// Re-index remaining slots
|
||||
for (int i = 0; i < _slots.Count; i++)
|
||||
{
|
||||
_slots[i].SetSlotIndex(i);
|
||||
}
|
||||
|
||||
UpdateLayout();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the closest slot to a world position
|
||||
/// </summary>
|
||||
public DraggableSlot FindClosestSlot(Vector3 worldPosition, DraggableObject draggable = null)
|
||||
{
|
||||
if (_slots.Count == 0)
|
||||
return null;
|
||||
|
||||
DraggableSlot closest = null;
|
||||
float closestDistance = float.MaxValue;
|
||||
|
||||
foreach (var slot in _slots)
|
||||
{
|
||||
// Skip locked slots or slots that can't accept this type
|
||||
if (slot.IsLocked)
|
||||
continue;
|
||||
|
||||
if (draggable != null && !slot.CanAccept(draggable))
|
||||
continue;
|
||||
|
||||
float distance = Vector3.Distance(worldPosition, slot.WorldPosition);
|
||||
if (distance < closestDistance)
|
||||
{
|
||||
closestDistance = distance;
|
||||
closest = slot;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all available (empty) slots
|
||||
/// </summary>
|
||||
public List<DraggableSlot> GetAvailableSlots()
|
||||
{
|
||||
return _slots.Where(s => !s.IsOccupied && !s.IsLocked).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all occupied slots
|
||||
/// </summary>
|
||||
public List<DraggableSlot> GetOccupiedSlots()
|
||||
{
|
||||
return _slots.Where(s => s.IsOccupied).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get slot at specific index
|
||||
/// </summary>
|
||||
public DraggableSlot GetSlotAtIndex(int index)
|
||||
{
|
||||
if (index < 0 || index >= _slots.Count)
|
||||
return null;
|
||||
|
||||
return _slots[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the layout of all slots based on layout type
|
||||
/// </summary>
|
||||
public void UpdateLayout()
|
||||
{
|
||||
if (layoutType == LayoutType.Custom)
|
||||
{
|
||||
OnLayoutChanged?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
int count = _slots.Count;
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
switch (layoutType)
|
||||
{
|
||||
case LayoutType.Horizontal:
|
||||
LayoutHorizontal();
|
||||
break;
|
||||
case LayoutType.Vertical:
|
||||
LayoutVertical();
|
||||
break;
|
||||
case LayoutType.Grid:
|
||||
LayoutGrid();
|
||||
break;
|
||||
}
|
||||
|
||||
OnLayoutChanged?.Invoke();
|
||||
}
|
||||
|
||||
private void LayoutHorizontal()
|
||||
{
|
||||
float totalWidth = (_slots.Count - 1) * spacing;
|
||||
float startX = centerSlots ? -totalWidth / 2f : 0f;
|
||||
|
||||
for (int i = 0; i < _slots.Count; i++)
|
||||
{
|
||||
if (_slots[i].RectTransform != null)
|
||||
{
|
||||
float xPos = startX + (i * spacing);
|
||||
float yPos = 0;
|
||||
|
||||
// Apply curve if enabled
|
||||
if (useCurveLayout && _slots.Count > 1)
|
||||
{
|
||||
float normalizedPos = i / (float)(_slots.Count - 1);
|
||||
yPos = positionCurve.Evaluate(normalizedPos) * curveHeight;
|
||||
}
|
||||
|
||||
_slots[i].RectTransform.anchoredPosition = new Vector2(xPos, yPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LayoutVertical()
|
||||
{
|
||||
float totalHeight = (_slots.Count - 1) * spacing;
|
||||
float startY = centerSlots ? totalHeight / 2f : 0f;
|
||||
|
||||
for (int i = 0; i < _slots.Count; i++)
|
||||
{
|
||||
if (_slots[i].RectTransform != null)
|
||||
{
|
||||
float yPos = startY - (i * spacing);
|
||||
_slots[i].RectTransform.anchoredPosition = new Vector2(0, yPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LayoutGrid()
|
||||
{
|
||||
// Simple grid layout - can be expanded
|
||||
int columns = Mathf.CeilToInt(Mathf.Sqrt(_slots.Count));
|
||||
|
||||
for (int i = 0; i < _slots.Count; i++)
|
||||
{
|
||||
if (_slots[i].RectTransform != null)
|
||||
{
|
||||
int row = i / columns;
|
||||
int col = i % columns;
|
||||
|
||||
float xPos = col * spacing;
|
||||
float yPos = -row * spacing;
|
||||
|
||||
_slots[i].RectTransform.anchoredPosition = new Vector2(xPos, yPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all slots
|
||||
/// </summary>
|
||||
public void ClearAllSlots()
|
||||
{
|
||||
foreach (var slot in _slots)
|
||||
{
|
||||
slot.Vacate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
3
Assets/Scripts/UI/DragAndDrop/Core/SlotContainer.cs.meta
Normal file
3
Assets/Scripts/UI/DragAndDrop/Core/SlotContainer.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f1347da8005d4687a85dbc4db9a1bbbb
|
||||
timeCreated: 1762420766
|
||||
3
Assets/Scripts/UI/DragAndDrop/Editor.meta
Normal file
3
Assets/Scripts/UI/DragAndDrop/Editor.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4b9473a4ece8425d97676fe7630d25ac
|
||||
timeCreated: 1762428015
|
||||
@@ -0,0 +1,95 @@
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UI.DragAndDrop.Core;
|
||||
|
||||
namespace UI.DragAndDrop.Editor
|
||||
{
|
||||
[CustomEditor(typeof(DraggableObject), true)]
|
||||
public class DraggableObjectEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
|
||||
DraggableObject draggable = (DraggableObject)target;
|
||||
|
||||
// Only show button in edit mode
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Editor Tools", EditorStyles.boldLabel);
|
||||
|
||||
if (GUILayout.Button("Snap to Parent Slot"))
|
||||
{
|
||||
SnapToParentSlot(draggable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SnapToParentSlot(DraggableObject draggable)
|
||||
{
|
||||
// Find parent slot
|
||||
DraggableSlot parentSlot = draggable.GetComponentInParent<DraggableSlot>();
|
||||
|
||||
if (parentSlot == null)
|
||||
{
|
||||
Debug.LogWarning("No parent DraggableSlot found!");
|
||||
return;
|
||||
}
|
||||
|
||||
Undo.RecordObject(draggable.transform, "Snap to Parent Slot");
|
||||
|
||||
// Reset position and rotation
|
||||
draggable.transform.localPosition = Vector3.zero;
|
||||
draggable.transform.localRotation = Quaternion.identity;
|
||||
|
||||
// Apply slot's size mode
|
||||
RectTransform draggableRect = draggable.GetComponent<RectTransform>();
|
||||
RectTransform slotRect = parentSlot.GetComponent<RectTransform>();
|
||||
|
||||
if (draggableRect != null && slotRect != null)
|
||||
{
|
||||
// Use reflection to access private fields
|
||||
System.Reflection.FieldInfo modeField = typeof(DraggableSlot).GetField("occupantSizeMode",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
System.Reflection.FieldInfo scaleField = typeof(DraggableSlot).GetField("occupantScale",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
if (modeField != null && scaleField != null)
|
||||
{
|
||||
var sizeMode = modeField.GetValue(parentSlot);
|
||||
var occupantScale = (Vector3)scaleField.GetValue(parentSlot);
|
||||
|
||||
// Get enum type
|
||||
System.Type enumType = sizeMode.GetType();
|
||||
string modeName = System.Enum.GetName(enumType, sizeMode);
|
||||
|
||||
Undo.RecordObject(draggableRect, "Apply Slot Size Mode");
|
||||
|
||||
switch (modeName)
|
||||
{
|
||||
case "MatchSlotSize":
|
||||
draggableRect.sizeDelta = slotRect.sizeDelta;
|
||||
draggableRect.localScale = Vector3.one;
|
||||
Debug.Log($"Matched slot size: {slotRect.sizeDelta}");
|
||||
break;
|
||||
|
||||
case "Scale":
|
||||
draggableRect.localScale = occupantScale;
|
||||
Debug.Log($"Applied scale: {occupantScale}");
|
||||
break;
|
||||
|
||||
case "None":
|
||||
default:
|
||||
// Keep current size
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(draggable.gameObject);
|
||||
Debug.Log($"Snapped {draggable.name} to parent slot {parentSlot.name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 290619d598ef4b199862482abc7188a3
|
||||
timeCreated: 1762428015
|
||||
@@ -5,16 +5,16 @@ public class ScrapbookController : MonoBehaviour
|
||||
{
|
||||
private void OnEnable()
|
||||
{
|
||||
InputManager.Instance.SetInputMode(InputMode.UI);
|
||||
// InputManager.Instance.SetInputMode(InputMode.UI);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
InputManager.Instance.SetInputMode (InputMode.Game);
|
||||
// InputManager.Instance.SetInputMode (InputMode.Game);
|
||||
}
|
||||
|
||||
public void DebugClick()
|
||||
{
|
||||
Debug.Log("Yey I was clicked!");
|
||||
// Debug.Log("Yey I was clicked!");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,340 +0,0 @@
|
||||
# Apple Hills Card System – Designer Playbook
|
||||
|
||||
This playbook is for designers working with the rarity‑based Card System in Apple Hills. It provides a high‑level 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 How‑Tos (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`
|
||||
- Player’s 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 you’ll 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 don’t 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 card’s 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 How‑Tos (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`. It’s 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 won’t persist player collection.
|
||||
- Booster Opening polish (timings/art/audio) is ongoing.
|
||||
- Collection statistics/achievements UI pending.
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
- Q: My new `CardDefinition` doesn’t 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.
|
||||
Reference in New Issue
Block a user