using System.Collections.Generic; using System.IO; using System.Linq; using AppleHills.Data.CardSystem; using UnityEditor; using UnityEditor.AddressableAssets.Settings; using UnityEngine; using UnityEngine.UI; using AppleHills.Editor.Utilities; using UI.CardSystem; namespace AppleHills.Editor.CardSystem { /// /// Editor utility for managing card definitions without directly editing scriptable objects. /// Provides a searchable list and visual preview of cards. /// public class CardEditorWindow : EditorWindow { // Paths private const string CardDefinitionsPath = "Assets/Data/Cards"; private const string MenuPath = "AppleHills/Card Editor"; private const string CardUIPrefabPath = "Assets/Prefabs/UI/Cards/SIngleCardDisplayUI.prefab"; private const string CardVisualConfigPath = CardDefinitionsPath + "/CardVisualConfig.asset"; // Editor state private List _cards = new List(); private CardDefinition _selectedCard; private CardDefinition _editingCard; // Clone of selected card for editing private Vector2 _cardListScrollPosition; private Vector2 _cardEditScrollPosition; private string _searchQuery = ""; private bool _showPreview = true; private bool _isDirty = false; // UI state for card preview private GameObject _previewCardObject; private CardUIElement _previewCardElement; private CardData _previewCardData; private Rect _previewRect; private GameObject _cardUIPrefab; private CardVisualConfig _cardVisualConfig; private UnityEditor.Editor _cardPreviewEditor; private Texture2D _staticPreviewTexture; private bool _previewNeedsUpdate = true; // Preview settings private float _previewZoom = 1.0f; private Vector2 _previewOffset = Vector2.zero; private bool _debugMode = false; private float _zoomMultiplier = 1.5f; // Default multiplier (no zoom) private const float DEFAULT_ZOOM = 1.5f; // Store default zoom as a constant private const float BASE_ORTHO_SIZE = 400.0f; // Increased from 0.8f to 8.0f for a much wider view // PreviewRenderUtility for rendering the card in a hidden scene private PreviewRenderUtility _previewUtility; private float _orbitRotation = 0f; private float _autoRotateSpeed = 0f; private bool _autoRotate = false; private GameObject _previewInstance; [MenuItem(MenuPath)] public static void ShowWindow() { var window = GetWindow("Card Editor"); window.minSize = new Vector2(800, 600); window.Show(); } private void OnEnable() { // Load all card definitions LoadCardDefinitions(); // Initialize preview InitializePreview(); // Register for undo/redo Undo.undoRedoPerformed += OnUndoRedo; // Set up a repaint on script compilation EditorApplication.update += OnEditorUpdate; EditorApplication.playModeStateChanged += OnPlayModeStateChanged; } private void OnDisable() { // Clean up preview CleanupPreview(); // Unregister from undo/redo Undo.undoRedoPerformed -= OnUndoRedo; // Unregister from update EditorApplication.update -= OnEditorUpdate; EditorApplication.playModeStateChanged -= OnPlayModeStateChanged; } private void OnEditorUpdate() { if (_showPreview && _previewNeedsUpdate) { Repaint(); } // Auto-rotate the preview if enabled if (_autoRotate && _previewUtility != null) { _orbitRotation += _autoRotateSpeed * Time.deltaTime; Repaint(); } } private void OnUndoRedo() { // Reload card definitions and refresh LoadCardDefinitions(); Repaint(); } private void OnPlayModeStateChanged(PlayModeStateChange stateChange) { if (stateChange == PlayModeStateChange.EnteredEditMode) { LoadCardDefinitions(); InitializePreview(); } else if (stateChange == PlayModeStateChange.ExitingEditMode) { CleanupPreview(); } } private void LoadCardDefinitions() { _cards.Clear(); // Create the directory structure if it doesn't exist string dataDirectory = Path.GetDirectoryName(CardDefinitionsPath); // Gets the "Assets/Data" part if (!Directory.Exists(dataDirectory)) { Directory.CreateDirectory(dataDirectory); } if (!Directory.Exists(CardDefinitionsPath)) { Directory.CreateDirectory(CardDefinitionsPath); AssetDatabase.Refresh(); } // Find all card definition assets string[] guids = AssetDatabase.FindAssets("t:CardDefinition", new[] { CardDefinitionsPath }); foreach (string guid in guids) { string path = AssetDatabase.GUIDToAssetPath(guid); CardDefinition card = AssetDatabase.LoadAssetAtPath(path); if (card != null) { _cards.Add(card); } } // Sort by name _cards = _cards.OrderBy(c => c.Name).ToList(); // If we had a selected card, try to find it again if (_selectedCard != null) { _selectedCard = _cards.FirstOrDefault(c => c.Id == _selectedCard.Id); if (_selectedCard != null) { _editingCard = CloneCard(_selectedCard); UpdatePreview(); } } } private void InitializePreview() { // Initialize PreviewRenderUtility for 3D preview if (_previewUtility == null) { _previewUtility = new PreviewRenderUtility(); // Configure camera for 2D UI rendering var cam = _previewUtility.camera; cam.clearFlags = CameraClearFlags.Color; cam.backgroundColor = Color.gray; // Background color when nothing is rendered cam.orthographic = true; // Orthographic is better for 2D UI cam.orthographicSize = 1f; cam.nearClipPlane = 0.01f; cam.farClipPlane = 100f; cam.cullingMask = ~0; // Render everything cam.transform.position = new Vector3(0, 0, -6); cam.transform.rotation = Quaternion.identity; // Configure lights for better UI visibility // Key light _previewUtility.lights[0].intensity = 1.2f; _previewUtility.lights[0].color = Color.white; _previewUtility.lights[0].transform.rotation = Quaternion.Euler(30f, 30f, 0f); // Fill light _previewUtility.lights[1].intensity = 0.7f; _previewUtility.lights[1].color = new Color(0.7f, 0.7f, 0.8f); _previewUtility.lights[1].transform.rotation = Quaternion.Euler(-30f, -30f, 0f); } // Load the prefab _cardUIPrefab = AssetDatabase.LoadAssetAtPath(CardUIPrefabPath); if (_cardUIPrefab == null) { Debug.LogError($"[CardEditorWindow] Could not find card UI prefab at {CardUIPrefabPath}"); return; } // Try to load the visual config _cardVisualConfig = AssetDatabase.LoadAssetAtPath(CardVisualConfigPath); if (_cardVisualConfig == null) { Debug.LogWarning($"[CardEditorWindow] Could not find card visual config at {CardVisualConfigPath}"); } // Create preview card object CreatePreviewCardObject(); } private void CreatePreviewCardObject() { // Clean up any existing preview objects CleanupPreviewInstance(); if (_cardUIPrefab == null) { Debug.LogError("[CardEditorWindow] Card UI prefab not loaded"); return; } try { // Create the preview instance in the PreviewRenderUtility's scene if (_previewUtility != null) { // 1. First create a root GameObject in the preview scene _previewInstance = new GameObject("PreviewRoot"); _previewInstance.hideFlags = HideFlags.HideAndDontSave; // 2. Create a Canvas to hold our card UI GameObject canvasGO = new GameObject("PreviewCanvas"); canvasGO.transform.SetParent(_previewInstance.transform, false); // 3. Add Canvas component and configure it Canvas canvas = canvasGO.AddComponent(); canvas.renderMode = RenderMode.WorldSpace; // Changed from ScreenSpaceCamera to WorldSpace canvas.worldCamera = _previewUtility.camera; // Set fixed world space size for the canvas RectTransform canvasRect = canvasGO.GetComponent(); canvasRect.sizeDelta = new Vector2(30f, 30f * (9f/16f)); // Increased from 10f to 30f for better sizing with larger ortho canvasRect.localPosition = Vector3.zero; // Center position canvasRect.localScale = Vector3.one * 0.8f; // Slightly scaled down for better proportions // 4. Add CanvasScaler but configure it for constant physical size instead of responsive scaling CanvasScaler scaler = canvasGO.AddComponent(); scaler.uiScaleMode = CanvasScaler.ScaleMode.ConstantPixelSize; // Use constant pixel size instead of screen-based scaling scaler.scaleFactor = 1f; // Base scale factor // 5. Add GraphicRaycaster (required for UI) canvasGO.AddComponent(); // 6. Instantiate the card prefab as a child of the canvas GameObject cardInstance = GameObject.Instantiate(_cardUIPrefab, canvasGO.transform, false); // 7. Position the card in the center of the canvas and set a fixed size RectTransform cardRect = cardInstance.GetComponent(); if (cardRect != null) { cardRect.anchorMin = new Vector2(0.5f, 0.5f); cardRect.anchorMax = new Vector2(0.5f, 0.5f); cardRect.pivot = new Vector2(0.5f, 0.5f); cardRect.anchoredPosition = Vector2.zero; // Use the original size of the card prefab // No automatic scaling will occur in world space mode } // 8. Get the CardUIElement component _previewCardElement = cardInstance.GetComponent(); if (_previewCardElement == null) { _previewCardElement = cardInstance.GetComponentInChildren(); } // 9. Add the root to the preview scene _previewUtility.AddSingleGO(_previewInstance); // 10. Set the visual config if (_cardVisualConfig != null && _previewCardElement != null) { var serializedObject = new SerializedObject(_previewCardElement); var visualConfigProperty = serializedObject.FindProperty("visualConfig"); if (visualConfigProperty != null) { visualConfigProperty.objectReferenceValue = _cardVisualConfig; serializedObject.ApplyModifiedProperties(); } } // 11. Update the preview with card data if available if (_previewCardData != null && _previewCardElement != null) { _previewCardElement.SetupCard(_previewCardData); } // Set up the camera to frame the canvas properly SetupPreviewCamera(); Debug.Log($"[CardEditorWindow] Successfully created preview card with UI canvas in PreviewRenderUtility"); } else { // Fallback to the original method for creating the preview object _previewCardObject = Instantiate(_cardUIPrefab); _previewCardObject.hideFlags = _debugMode ? HideFlags.DontSave : HideFlags.HideAndDontSave; foreach (var renderer in _previewCardObject.GetComponentsInChildren()) { renderer.enabled = false; } _previewCardElement = _previewCardObject.GetComponent(); if (_previewCardElement == null) { _previewCardElement = _previewCardObject.GetComponentInChildren(); } // Create an Editor for the preview object to generate previews if (_cardPreviewEditor != null) { DestroyImmediate(_cardPreviewEditor); } _cardPreviewEditor = UnityEditor.Editor.CreateEditor(_previewCardObject); } _previewNeedsUpdate = true; } catch (System.Exception e) { Debug.LogError($"[CardEditorWindow] Error creating preview card: {e.Message}\n{e.StackTrace}"); } } private void SetupPreviewCamera() { if (_previewUtility == null) return; // Configure camera for UI rendering Camera cam = _previewUtility.camera; cam.clearFlags = CameraClearFlags.SolidColor; cam.backgroundColor = new Color(0.2f, 0.2f, 0.2f); // Dark gray background // Apply the base orthographic size with the current zoom multiplier cam.orthographicSize = BASE_ORTHO_SIZE / _zoomMultiplier; // Position the camera to look at the origin cam.transform.position = new Vector3(0, 0, -5); cam.transform.rotation = Quaternion.identity; if (_debugMode) { Debug.Log($"[CardEditorWindow] Setup preview camera at position: {cam.transform.position}, orthographicSize: {cam.orthographicSize}"); } } private void CleanupPreviewInstance() { if (_previewInstance != null) { DestroyImmediate(_previewInstance); _previewInstance = null; } _previewCardElement = null; } private void CleanupPreview() { CleanupPreviewInstance(); if (_previewCardObject != null) { DestroyImmediate(_previewCardObject); _previewCardObject = null; } if (_cardPreviewEditor != null) { DestroyImmediate(_cardPreviewEditor); _cardPreviewEditor = null; } if (_staticPreviewTexture != null) { DestroyImmediate(_staticPreviewTexture); _staticPreviewTexture = null; } if (_previewUtility != null) { _previewUtility.Cleanup(); _previewUtility = null; } } private void UpdatePreview() { if (_editingCard == null || !_showPreview) return; try { // Update the preview card UI element if (_previewCardElement != null) { // First set the cardDefinition field via reflection or SerializedObject // (since it's a serialized field we don't have direct access) var serializedObject = new SerializedObject(_previewCardElement); var cardDefinitionProperty = serializedObject.FindProperty("cardDefinition"); if (cardDefinitionProperty != null) { cardDefinitionProperty.objectReferenceValue = _editingCard; serializedObject.ApplyModifiedProperties(); } // Now directly call CreateFromDefinition which will create the card data and update visuals var createFromDefMethod = typeof(CardUIElement).GetMethod("CreateFromDefinition", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic); if (createFromDefMethod != null) { createFromDefMethod.Invoke(_previewCardElement, null); // Store a reference to the card data for other uses if needed var cardDataField = typeof(CardUIElement).GetField("cardData", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); if (cardDataField != null) { _previewCardData = (CardData)cardDataField.GetValue(_previewCardElement); } _previewNeedsUpdate = true; } else { Debug.LogError("[CardEditorWindow] Could not find CreateFromDefinition method in CardUIElement"); } // Auto-frame the card in the preview by calculating its bounds if (_previewInstance != null) { AutoFramePreviewObject(); } if (_debugMode) { Debug.Log($"[CardEditorWindow] Updated preview card data via CreateFromDefinition: {_editingCard.Name}"); } } } catch (System.Exception ex) { Debug.LogError($"[CardEditorWindow] Error updating preview: {ex.Message}\n{ex.StackTrace}"); } } private void AutoFramePreviewObject() { if (_previewInstance == null || _previewUtility == null) return; // Find all visible elements to calculate bounds Bounds bounds = new Bounds(); bool boundsInitialized = false; // Calculate bounds from RectTransforms properly by using their corners in world space foreach (var rectTransform in _previewInstance.GetComponentsInChildren()) { // Get the corners of the RectTransform in world space Vector3[] corners = new Vector3[4]; rectTransform.GetWorldCorners(corners); // Initialize or expand bounds based on corners if (!boundsInitialized) { bounds = new Bounds(corners[0], Vector3.zero); boundsInitialized = true; // Expand initial bounds to include all corners for (int i = 1; i < 4; i++) { bounds.Encapsulate(corners[i]); } } else { // Encapsulate all corners for existing bounds foreach (Vector3 corner in corners) { bounds.Encapsulate(corner); } } } // Also check regular Renderers (if any) foreach (var renderer in _previewInstance.GetComponentsInChildren()) { if (!boundsInitialized) { bounds = renderer.bounds; boundsInitialized = true; } else { bounds.Encapsulate(renderer.bounds); } } // Keep using the existing camera settings, only update the orthographic size based on zoom Camera cam = _previewUtility.camera; cam.orthographicSize = BASE_ORTHO_SIZE / _zoomMultiplier; if (_debugMode) { Debug.Log($"[CardEditorWindow] Updated zoom: orthoSize={cam.orthographicSize}, zoomMultiplier={_zoomMultiplier}, position={cam.transform.position}"); } } private void RenderCardPreview(Rect rect) { if (_editingCard == null) { DrawSimplePreview(rect); return; } // Use PreviewRenderUtility if available if (_previewUtility != null && _previewInstance != null) { try { // Store the current orthographic size before rendering (to restore it if changed) float currentOrthoSize = _previewUtility.camera.orthographicSize; // Start the preview rendering - using a fixed size to maintain consistency _previewUtility.BeginPreview(rect, GUIStyle.none); // Ensure the camera's orthographic size is correct before rendering _previewUtility.camera.orthographicSize = BASE_ORTHO_SIZE / _zoomMultiplier; // Apply rotation based on orbit angle if (_previewInstance != null) { _previewInstance.transform.rotation = Quaternion.Euler(0, _orbitRotation, 0); } // Render the preview scene _previewUtility.camera.Render(); // Get the rendered texture and draw it Texture rendered = _previewUtility.EndPreview(); // Keep a constant aspect ratio when drawing float textureAspect = (float)rendered.width / rendered.height; float rectAspect = rect.width / rect.height; Rect drawRect = rect; // Adjust the drawing rectangle to maintain aspect ratio if (textureAspect > rectAspect) // Texture is wider than rect { float newHeight = rect.width / textureAspect; drawRect.y += (rect.height - newHeight) * 0.5f; drawRect.height = newHeight; } else // Texture is taller than rect { float newWidth = rect.height * textureAspect; drawRect.x += (rect.width - newWidth) * 0.5f; drawRect.width = newWidth; } // Draw with fixed aspect ratio GUI.DrawTexture(drawRect, rendered, ScaleMode.StretchToFill); // Draw overlay info GUI.Label(new Rect(rect.x + 10, rect.y + 10, rect.width - 20, 20), $"Preview: {_editingCard.Name}", EditorStyles.boldLabel); if (_debugMode) { // Show size debugging info GUI.Label(new Rect(rect.x + 10, rect.y + rect.height - 40, rect.width - 20, 20), $"Texture: {rendered.width}x{rendered.height}, Rect: {rect.width}x{rect.height}", EditorStyles.miniLabel); } return; } catch (System.Exception ex) { Debug.LogError($"[CardEditorWindow] Error rendering with PreviewRenderUtility: {ex.Message}"); // Fall through to backup rendering method } } // Fallback to the original preview method if PreviewRenderUtility failed if (_cardPreviewEditor != null) { try { // Use the editor's OnInteractivePreviewGUI to render our card preview _cardPreviewEditor.OnPreviewGUI(rect, EditorStyles.helpBox); // Draw overlay information GUI.Label(new Rect(rect.x + 10, rect.y + 10, rect.width - 20, 20), $"Preview: {_editingCard.Name}", EditorStyles.boldLabel); return; } catch (System.Exception ex) { Debug.LogError($"[CardEditorWindow] Error rendering with Editor preview: {ex.Message}"); } } // Ultimate fallback if all else fails DrawSimplePreview(rect); } private void OnGUI() { GUILayout.BeginHorizontal(); // Card list panel (left) DrawCardListPanel(); // Add padding between sections GUILayout.Space(10); // Card editor panel (middle) DrawCardEditPanel(); // Add padding between sections GUILayout.Space(10); // Card preview panel (right) DrawCardPreviewPanel(); GUILayout.EndHorizontal(); } private void DrawCardListPanel() { GUILayout.BeginVertical(GUILayout.Width(250)); // Header GUILayout.Label("Cards", EditorStyles.boldLabel); // Search bar EditorGUI.BeginChangeCheck(); _searchQuery = EditorGUILayout.TextField("Search", _searchQuery, GUILayout.ExpandWidth(true)); if (EditorGUI.EndChangeCheck()) { Repaint(); } // Create new card button if (GUILayout.Button("Create New Card")) { CreateNewCard(); } // Card list _cardListScrollPosition = EditorGUILayout.BeginScrollView(_cardListScrollPosition); // Filter by search query if any List filteredCards = _cards; if (!string.IsNullOrWhiteSpace(_searchQuery)) { string search = _searchQuery.ToLowerInvariant(); filteredCards = _cards.Where(c => c.Name.ToLowerInvariant().Contains(search) || c.Description.ToLowerInvariant().Contains(search) || c.Zone.ToString().ToLowerInvariant().Contains(search) || c.Rarity.ToString().ToLowerInvariant().Contains(search) ).ToList(); } // Display cards foreach (var card in filteredCards) { bool isSelected = _selectedCard != null && _selectedCard.Id == card.Id; GUIStyle style = isSelected ? EditorStyles.selectionRect : EditorStyles.label; EditorGUILayout.BeginHorizontal(style); if (GUILayout.Button(card.Name, EditorStyles.label, GUILayout.ExpandWidth(true))) { SelectCard(card); } GUILayout.Label(card.Rarity.ToString(), GUILayout.Width(70)); EditorGUILayout.EndHorizontal(); } EditorGUILayout.EndScrollView(); GUILayout.EndVertical(); } private void DrawCardEditPanel() { GUILayout.BeginVertical(GUILayout.ExpandWidth(true)); if (_editingCard != null) { // Header GUILayout.Label("Edit Card", EditorStyles.boldLabel); _cardEditScrollPosition = EditorGUILayout.BeginScrollView(_cardEditScrollPosition); // Basic info EditorGUI.BeginChangeCheck(); _editingCard.Name = EditorGUILayout.TextField("Name", _editingCard.Name); _editingCard.Description = EditorGUILayout.TextArea(_editingCard.Description, GUILayout.Height(100)); _editingCard.Rarity = (CardRarity)EditorGUILayout.EnumPopup("Rarity", _editingCard.Rarity); _editingCard.Zone = (CardZone)EditorGUILayout.EnumPopup("Zone", _editingCard.Zone); // Visual elements EditorGUILayout.Space(); GUILayout.Label("Visual Elements", EditorStyles.boldLabel); _editingCard.CardImage = (Sprite)EditorGUILayout.ObjectField("Card Image", _editingCard.CardImage, typeof(Sprite), false); // Display derived properties (for information only) GUI.enabled = false; EditorGUILayout.ColorField("Background Color (from Zone)", _editingCard.GetBackgroundColor()); EditorGUILayout.TextField("Frame Shape (from Rarity)", $"Frame_{_editingCard.Rarity}"); GUI.enabled = true; // Collection info EditorGUILayout.Space(); GUILayout.Label("Collection Info", EditorStyles.boldLabel); _editingCard.CollectionIndex = EditorGUILayout.IntField("Collection Index", _editingCard.CollectionIndex); if (EditorGUI.EndChangeCheck()) { _isDirty = true; UpdatePreview(); } GUILayout.FlexibleSpace(); // Action buttons GUILayout.BeginHorizontal(); if (GUILayout.Button("Apply Changes", GUILayout.Height(30))) { ApplyChanges(); } if (GUILayout.Button("Duplicate", GUILayout.Height(30))) { DuplicateSelectedCard(); } if (GUILayout.Button("Delete", GUILayout.Height(30))) { if (EditorUtility.DisplayDialog("Delete Card", $"Are you sure you want to delete '{_selectedCard.Name}'?", "Delete", "Cancel")) { DeleteSelectedCard(); } } GUILayout.EndHorizontal(); // Warning if dirty if (_isDirty) { EditorGUILayout.HelpBox("You have unsaved changes.", MessageType.Info); } EditorGUILayout.EndScrollView(); } else { GUILayout.Label("Select a card to edit", EditorStyles.centeredGreyMiniLabel); } GUILayout.EndVertical(); } private void DrawCardPreviewPanel() { GUILayout.BeginVertical(GUILayout.Width(300)); // Header with toggle GUILayout.BeginHorizontal(); GUILayout.Label("Preview", EditorStyles.boldLabel); EditorGUI.BeginChangeCheck(); _showPreview = EditorGUILayout.Toggle("Show Preview", _showPreview); if (EditorGUI.EndChangeCheck()) { if (_showPreview) { // Recreate the preview when toggling it on CreatePreviewCardObject(); UpdatePreview(); } } GUILayout.EndHorizontal(); if (_showPreview && _editingCard != null) { // Preview area _previewRect = GUILayoutUtility.GetRect(300, 400); // Render the preview RenderCardPreview(_previewRect); // Preview controls GUILayout.BeginHorizontal(); // Refresh button if (GUILayout.Button("Refresh Preview")) { CreatePreviewCardObject(); UpdatePreview(); } // Auto-rotate toggle EditorGUI.BeginChangeCheck(); _autoRotate = GUILayout.Toggle(_autoRotate, "Auto-Rotate"); if (EditorGUI.EndChangeCheck() && _autoRotate) { _autoRotateSpeed = 20f; // Degrees per second } // Debug toggle EditorGUI.BeginChangeCheck(); _debugMode = GUILayout.Toggle(_debugMode, "Debug Mode"); if (EditorGUI.EndChangeCheck()) { // If entering debug mode, make objects visible in hierarchy if (_previewCardObject != null) { _previewCardObject.hideFlags = _debugMode ? HideFlags.DontSave : HideFlags.HideAndDontSave; } } GUILayout.EndHorizontal(); // Manual rotation slider if (!_autoRotate) { EditorGUI.BeginChangeCheck(); _orbitRotation = EditorGUILayout.Slider("Rotation", _orbitRotation, 0f, 360f); if (EditorGUI.EndChangeCheck()) { Repaint(); } } // Zoom control EditorGUI.BeginChangeCheck(); _zoomMultiplier = EditorGUILayout.Slider("Zoom", _zoomMultiplier, 0.0f, 4.0f); if (EditorGUI.EndChangeCheck()) { if (_previewUtility != null) { // Adjust the orthographic size of the camera based on zoom level _previewUtility.camera.orthographicSize = BASE_ORTHO_SIZE / _zoomMultiplier; if (_debugMode) { Debug.Log($"[CardEditorWindow] Adjusted camera orthographic size for zoom. Zoom: {_zoomMultiplier}, Ortho: {_previewUtility.camera.orthographicSize}"); } } } // Debug info if (_debugMode) { EditorGUILayout.LabelField($"Card: {_editingCard.Name}"); EditorGUILayout.LabelField($"Rarity: {_editingCard.Rarity}"); EditorGUILayout.LabelField($"Zone: {_editingCard.Zone}"); EditorGUILayout.LabelField($"Preview Method: {(_previewUtility != null ? "PreviewRenderUtility" : "Editor Preview")}"); EditorGUILayout.LabelField($"UI Component: {(_previewCardElement != null ? "Found" : "Missing")}"); if (_previewUtility != null && _previewInstance != null) { EditorGUILayout.LabelField($"Camera Position: {_previewUtility.camera.transform.position}"); EditorGUILayout.LabelField($"Card Rotation: {_previewInstance.transform.rotation.eulerAngles}"); } } EditorGUILayout.HelpBox( "This preview uses PreviewRenderUtility to render the card prefab in a hidden scene. " + "You can rotate the card to see it from different angles.", MessageType.Info); } GUILayout.EndVertical(); } private void DrawSimplePreview(Rect rect) { // This is the original simple preview code as a fallback // Draw a colored background to represent the card EditorGUI.DrawRect(rect, _editingCard.GetBackgroundColor()); // Draw a border Rect borderRect = new Rect(rect.x + 2, rect.y + 2, rect.width - 4, rect.height - 4); EditorGUI.DrawRect(borderRect, Color.white); // Draw card name Rect nameRect = new Rect(rect.x + 20, rect.y + 20, rect.width - 40, 30); EditorGUI.LabelField(nameRect, _editingCard.Name, EditorStyles.whiteLargeLabel); // Draw card image if available if (_editingCard.CardImage != null) { Rect imageRect = new Rect(rect.x + 50, rect.y + 60, rect.width - 100, 200); GUI.DrawTexture(imageRect, AssetPreview.GetAssetPreview(_editingCard.CardImage) ?? _editingCard.CardImage.texture); } // Draw card description Rect descRect = new Rect(rect.x + 20, rect.y + 270, rect.width - 40, 100); EditorGUI.LabelField(descRect, _editingCard.Description, EditorStyles.wordWrappedLabel); // Draw rarity at the bottom Rect rarityRect = new Rect(rect.x + 20, rect.y + rect.height - 40, rect.width - 40, 30); EditorGUI.LabelField(rarityRect, $"Rarity: {_editingCard.Rarity}", EditorStyles.boldLabel); } private Color GetRarityColor(CardRarity rarity) { switch (rarity) { case CardRarity.Common: return Color.gray; case CardRarity.Uncommon: return Color.green; case CardRarity.Rare: return Color.blue; case CardRarity.Epic: return new Color(0.5f, 0f, 0.5f); // Purple case CardRarity.Legendary: return Color.yellow; default: return Color.white; } } private void SelectCard(CardDefinition card) { // Check for unsaved changes if (_isDirty && _selectedCard != null) { if (EditorUtility.DisplayDialog("Unsaved Changes", "You have unsaved changes. Do you want to apply them before switching cards?", "Apply Changes", "Discard Changes")) { ApplyChanges(); } } _selectedCard = card; _editingCard = CloneCard(card); _isDirty = false; // Force a complete recreation of the preview when selecting a new card if (_showPreview) { // Clean up previous preview objects to ensure a fresh start CleanupPreviewInstance(); // Create a new preview instance with the selected card CreatePreviewCardObject(); // Update preview with the new card data UpdatePreview(); } } private void CreateNewCard() { // Create a new card definition CardDefinition newCard = CreateInstance(); // Set default values newCard.Id = System.Guid.NewGuid().ToString(); newCard.Name = "New Card"; newCard.Description = "Description goes here"; newCard.Rarity = CardRarity.Common; newCard.Zone = CardZone.AppleHills; // Note: No longer setting BackgroundColor since it's derived from Zone newCard.CollectionIndex = _cards.Count; // Save the asset string path = $"{CardDefinitionsPath}/Card_{newCard.Name}.asset"; path = AssetDatabase.GenerateUniqueAssetPath(path); AssetDatabase.CreateAsset(newCard, path); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // Reload cards and select the new one LoadCardDefinitions(); SelectCard(newCard); } private void DuplicateSelectedCard() { if (_selectedCard == null) return; // Create a new card definition CardDefinition newCard = CloneCard(_editingCard); // Give it a new ID newCard.Id = System.Guid.NewGuid().ToString(); newCard.Name += " (Copy)"; // Save the asset string path = $"{CardDefinitionsPath}/Card_{newCard.Name}.asset"; path = AssetDatabase.GenerateUniqueAssetPath(path); AssetDatabase.CreateAsset(newCard, path); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // Reload cards and select the new one LoadCardDefinitions(); SelectCard(newCard); } private void DeleteSelectedCard() { if (_selectedCard == null) return; // Get the path of the selected card string path = AssetDatabase.GetAssetPath(_selectedCard); // Delete the asset AssetDatabase.DeleteAsset(path); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); // Clear selection and reload _selectedCard = null; _editingCard = null; _isDirty = false; LoadCardDefinitions(); } private void ApplyChanges() { if (_selectedCard == null || _editingCard == null) return; // Apply changes to the selected card EditorUtility.CopySerialized(_editingCard, _selectedCard); // Mark as dirty and save EditorUtility.SetDirty(_selectedCard); AssetDatabase.SaveAssets(); // Add to Addressables group "BlokkemonCards" and apply "BlokkemonCard" label string assetPath = AssetDatabase.GetAssetPath(_selectedCard); if (!string.IsNullOrEmpty(assetPath)) { if (AddressablesUtility.EnsureAssetInGroupWithLabel(assetPath, "BlokkemonCards", "BlokkemonCard")) { AddressablesUtility.SaveAddressableAssets(); } else { Debug.LogError("Failed to add card to Addressables. Please ensure Addressables are set up in this project."); } } // Clear dirty flag _isDirty = false; } private CardDefinition CloneCard(CardDefinition original) { CardDefinition clone = CreateInstance(); EditorUtility.CopySerialized(original, clone); return clone; } } }