Files
AppleHillsProduction/Assets/Editor/CardSystem/CardEditorWindow.cs

1094 lines
44 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Linq;
using AppleHills.Data.CardSystem;
using AppleHills.UI.CardSystem;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
namespace AppleHills.Editor.CardSystem
{
/// <summary>
/// Editor utility for managing card definitions without directly editing scriptable objects.
/// Provides a searchable list and visual preview of cards.
/// </summary>
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/CardUI.prefab";
private const string CardVisualConfigPath = CardDefinitionsPath + "/CardVisualConfig.asset";
// Editor state
private List<CardDefinition> _cards = new List<CardDefinition>();
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<CardEditorWindow>("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<CardDefinition>(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<GameObject>(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<CardVisualConfig>(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>();
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<RectTransform>();
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<CanvasScaler>();
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<UnityEngine.UI.GraphicRaycaster>();
// 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<RectTransform>();
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<CardUIElement>();
if (_previewCardElement == null)
{
_previewCardElement = cardInstance.GetComponentInChildren<CardUIElement>();
}
// 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>())
{
renderer.enabled = false;
}
_previewCardElement = _previewCardObject.GetComponent<CardUIElement>();
if (_previewCardElement == null)
{
_previewCardElement = _previewCardObject.GetComponentInChildren<CardUIElement>();
}
// 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<RectTransform>())
{
// 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<Renderer>())
{
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<CardDefinition> 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<CardDefinition>();
// 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();
// Clear dirty flag
_isDirty = false;
}
private CardDefinition CloneCard(CardDefinition original)
{
CardDefinition clone = CreateInstance<CardDefinition>();
EditorUtility.CopySerialized(original, clone);
return clone;
}
}
}