1094 lines
44 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|