using Minigames.Airplane.Core.Spawning; using Minigames.Airplane.Data; using UnityEditor; using UnityEngine; namespace Editor.Minigames.Airplane { /// /// Custom editor for ParallaxBackgroundSpawner. /// Enforces exactly 3 pools with custom labels (Background/Middle/Foreground). /// Prevents array manipulation and provides clean, designer-friendly interface. /// [CustomEditor(typeof(ParallaxBackgroundSpawner))] public class ParallaxBackgroundSpawnerEditor : UnityEditor.Editor { private SerializedProperty _poolsProperty; private SerializedProperty _parallaxSortLayerProperty; private SerializedProperty _backgroundSortOrderProperty; private SerializedProperty _sortOrderIncrementProperty; private SerializedProperty _globalStrengthProperty; private SerializedProperty _backgroundSpeedProperty; private SerializedProperty _middleSpeedProperty; private SerializedProperty _foregroundSpeedProperty; private SerializedProperty _cameraManagerProperty; private SerializedProperty _showDebugLogsProperty; private readonly string[] _layerNames = { "Background Layer", "Middle Layer", "Foreground Layer" }; private readonly string[] _layerDescriptions = { "Slowest parallax - furthest back", "Medium parallax - middle depth", "Fastest parallax - closest to camera" }; private bool[] _poolFoldouts = new bool[3]; private void OnEnable() { _poolsProperty = serializedObject.FindProperty("pools"); _parallaxSortLayerProperty = serializedObject.FindProperty("parallaxSortLayer"); _backgroundSortOrderProperty = serializedObject.FindProperty("backgroundSortOrder"); _sortOrderIncrementProperty = serializedObject.FindProperty("sortOrderIncrement"); _globalStrengthProperty = serializedObject.FindProperty("globalStrength"); _backgroundSpeedProperty = serializedObject.FindProperty("backgroundSpeed"); _middleSpeedProperty = serializedObject.FindProperty("middleSpeed"); _foregroundSpeedProperty = serializedObject.FindProperty("foregroundSpeed"); _cameraManagerProperty = serializedObject.FindProperty("cameraManager"); _showDebugLogsProperty = serializedObject.FindProperty("showDebugLogs"); // Ensure exactly 3 pools exist EnsureThreePools(); } private void EnsureThreePools() { if (_poolsProperty.arraySize != 3) { _poolsProperty.arraySize = 3; // Initialize pool descriptions for (int i = 0; i < 3; i++) { var poolElement = _poolsProperty.GetArrayElementAtIndex(i); var descProperty = poolElement.FindPropertyRelative("description"); if (string.IsNullOrEmpty(descProperty.stringValue)) { descProperty.stringValue = _layerNames[i]; } } serializedObject.ApplyModifiedProperties(); } } public override void OnInspectorGUI() { serializedObject.Update(); EditorGUILayout.HelpBox("Note: Spawn distances, recency tracking, and debug settings are configured globally in AirplaneSettings. Containers are configured in AirplaneSpawnManager.", MessageType.Info); EditorGUILayout.Space(); // Camera Integration EditorGUILayout.PropertyField(_cameraManagerProperty); EditorGUILayout.Space(); // Parallax Configuration (CENTRALIZED SETTINGS) EditorGUILayout.LabelField("Parallax Configuration", EditorStyles.boldLabel); EditorGUILayout.HelpBox("These settings apply to ALL parallax elements spawned by this spawner. Adjust speeds to control depth perception.", MessageType.Info); EditorGUILayout.PropertyField(_globalStrengthProperty, new GUIContent("Global Strength", "Overall parallax intensity (0 = no effect, 1 = full)")); EditorGUILayout.Space(5); EditorGUILayout.LabelField("Per-Layer Speeds", EditorStyles.miniBoldLabel); EditorGUI.indentLevel++; EditorGUILayout.PropertyField(_backgroundSpeedProperty, new GUIContent("Background Speed", "Slowest layer, appears furthest (e.g., 0.3)")); EditorGUILayout.PropertyField(_middleSpeedProperty, new GUIContent("Middle Speed", "Medium speed layer (e.g., 0.6)")); EditorGUILayout.PropertyField(_foregroundSpeedProperty, new GUIContent("Foreground Speed", "Fastest layer, appears closest (e.g., 0.9)")); EditorGUI.indentLevel--; // Validation warnings if (_backgroundSpeedProperty.floatValue >= _middleSpeedProperty.floatValue) { EditorGUILayout.HelpBox("Warning: Background speed should be less than Middle speed for proper depth effect", MessageType.Warning); } if (_middleSpeedProperty.floatValue >= _foregroundSpeedProperty.floatValue) { EditorGUILayout.HelpBox("Warning: Middle speed should be less than Foreground speed for proper depth effect", MessageType.Warning); } EditorGUILayout.Space(); // Sort Configuration EditorGUILayout.LabelField("Sort Configuration", EditorStyles.boldLabel); EditorGUILayout.HelpBox("All parallax objects use the same sort layer with different sort orders for depth.", MessageType.Info); EditorGUILayout.PropertyField(_parallaxSortLayerProperty, new GUIContent("Sort Layer", "Sort layer for all parallax elements (typically 'Background')")); EditorGUILayout.PropertyField(_backgroundSortOrderProperty, new GUIContent("Background Sort Order", "Sort order for furthest back layer (e.g., -50)")); EditorGUILayout.PropertyField(_sortOrderIncrementProperty, new GUIContent("Sort Order Increment", "Increment between layers (e.g., 10 → Middle: -40, Foreground: -30)")); // Show calculated sort orders int middleOrder = _backgroundSortOrderProperty.intValue + _sortOrderIncrementProperty.intValue; int foregroundOrder = _backgroundSortOrderProperty.intValue + (_sortOrderIncrementProperty.intValue * 2); EditorGUILayout.LabelField($"Calculated: Background={_backgroundSortOrderProperty.intValue}, Middle={middleOrder}, Foreground={foregroundOrder}", EditorStyles.miniLabel); EditorGUILayout.Space(); // Pool Mode (locked to Exclusive) EditorGUILayout.LabelField("Spawn Mode", EditorStyles.boldLabel); EditorGUI.BeginDisabledGroup(true); EditorGUILayout.TextField("Pool Mode", "Exclusive (Fixed)"); EditorGUI.EndDisabledGroup(); EditorGUILayout.HelpBox("Parallax spawner always uses Exclusive mode - each layer spawns independently", MessageType.Info); EditorGUILayout.Space(); // Parallax Layers (exactly 3, custom display) EditorGUILayout.LabelField("Parallax Layers (Fixed: 3 layers)", EditorStyles.boldLabel); EnsureThreePools(); // Safety check for (int i = 0; i < 3; i++) { var poolElement = _poolsProperty.GetArrayElementAtIndex(i); var prefabsProperty = poolElement.FindPropertyRelative("prefabs"); var descProperty = poolElement.FindPropertyRelative("description"); var overrideMinDistProperty = poolElement.FindPropertyRelative("overrideMinDistance"); var overrideMaxDistProperty = poolElement.FindPropertyRelative("overrideMaxDistance"); EditorGUILayout.BeginVertical(EditorStyles.helpBox); // Foldout with custom label _poolFoldouts[i] = EditorGUILayout.Foldout(_poolFoldouts[i], $"{_layerNames[i]} (Pool {i})", true, EditorStyles.foldoutHeader); if (_poolFoldouts[i]) { EditorGUI.indentLevel++; EditorGUILayout.LabelField(_layerDescriptions[i], EditorStyles.miniLabel); EditorGUILayout.Space(5); EditorGUILayout.PropertyField(descProperty, new GUIContent("Description")); EditorGUILayout.PropertyField(prefabsProperty, new GUIContent("Prefabs"), true); EditorGUILayout.Space(5); EditorGUILayout.LabelField("Spawn Distance Overrides", EditorStyles.miniBoldLabel); EditorGUILayout.HelpBox("Leave at 0 to use global settings. Set > 0 to override for this layer.", MessageType.Info); EditorGUILayout.PropertyField(overrideMinDistProperty, new GUIContent("Min Distance Override", "Minimum distance between objects (0 = use global)")); EditorGUILayout.PropertyField(overrideMaxDistProperty, new GUIContent("Max Distance Override", "Maximum distance between objects (0 = use global)")); // Show info about prefab count if (prefabsProperty.arraySize == 0) { EditorGUILayout.HelpBox("Add prefabs for this layer", MessageType.Warning); } else { EditorGUILayout.LabelField($"Prefabs: {prefabsProperty.arraySize}", EditorStyles.miniLabel); } EditorGUI.indentLevel--; } EditorGUILayout.EndVertical(); EditorGUILayout.Space(5); } EditorGUILayout.Space(); serializedObject.ApplyModifiedProperties(); } } }