Introduce background spawning with parallax effect (#86)
Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Reviewed-on: #86
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using AppleHillsCamera;
|
||||
using Minigames.BirdPooper;
|
||||
|
||||
namespace Editor
|
||||
{
|
||||
@@ -16,7 +17,7 @@ namespace Editor
|
||||
EdgeAnchor edgeAnchor = (EdgeAnchor)target;
|
||||
|
||||
// Check if there's an Obstacle component on the same GameObject
|
||||
var obstacle = edgeAnchor.GetComponent<Minigames.BirdPooper.Obstacle>();
|
||||
var obstacle = edgeAnchor.GetComponent<Obstacle>();
|
||||
|
||||
if (obstacle != null)
|
||||
{
|
||||
|
||||
8
Assets/Editor/Minigames.meta
Normal file
8
Assets/Editor/Minigames.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d8af6b4c7a999a4aa2180ec5422baf3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Editor/Minigames/Airplane.meta
Normal file
8
Assets/Editor/Minigames/Airplane.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 259f822b327e0d740ae0542eb24e1d0a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
96
Assets/Editor/Minigames/Airplane/AirplaneSettingsEditor.cs
Normal file
96
Assets/Editor/Minigames/Airplane/AirplaneSettingsEditor.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
using Minigames.Airplane.Data;
|
||||
using Minigames.Airplane.Settings;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Editor.Minigames.Airplane
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for AirplaneSettings that conditionally shows default obstacle positioning fields.
|
||||
/// Integrates with SettingsEditorWindow.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(AirplaneSettings))]
|
||||
public class AirplaneSettingsEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// Draw all properties except the default obstacle positioning section
|
||||
SerializedProperty iterator = serializedObject.GetIterator();
|
||||
bool enterChildren = true;
|
||||
|
||||
while (iterator.NextVisible(enterChildren))
|
||||
{
|
||||
enterChildren = false;
|
||||
|
||||
// Skip script field
|
||||
if (iterator.propertyPath == "m_Script")
|
||||
continue;
|
||||
|
||||
// Handle Default Obstacle Positioning section specially
|
||||
if (iterator.propertyPath == "defaultObstaclePositionMode")
|
||||
{
|
||||
DrawDefaultObstaclePositioningSection();
|
||||
|
||||
// Skip the related fields as we'll draw them conditionally
|
||||
iterator.NextVisible(false); // Skip defaultObstacleRandomYMin
|
||||
iterator.NextVisible(false); // Skip defaultObstacleRandomYMax
|
||||
continue;
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(iterator, true);
|
||||
}
|
||||
|
||||
// Apply changes and mark dirty for SettingsEditorWindow integration
|
||||
if (serializedObject.ApplyModifiedProperties())
|
||||
{
|
||||
EditorUtility.SetDirty(target);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDefaultObstaclePositioningSection()
|
||||
{
|
||||
var modeProperty = serializedObject.FindProperty("defaultObstaclePositionMode");
|
||||
var randomYMinProperty = serializedObject.FindProperty("defaultObstacleRandomYMin");
|
||||
var randomYMaxProperty = serializedObject.FindProperty("defaultObstacleRandomYMax");
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.PropertyField(modeProperty, new GUIContent("Default Position Mode"));
|
||||
|
||||
SpawnPositionMode currentMode = (SpawnPositionMode)modeProperty.enumValueIndex;
|
||||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
switch (currentMode)
|
||||
{
|
||||
case SpawnPositionMode.SnapToGround:
|
||||
EditorGUILayout.HelpBox("Obstacles will raycast to find ground and snap to it. If raycast fails, Fallback Y Position (configured in Ground Snapping section above) will be used.", MessageType.Info);
|
||||
break;
|
||||
|
||||
case SpawnPositionMode.SpecifiedY:
|
||||
EditorGUILayout.HelpBox("Obstacles will spawn at Fallback Y Position (configured in Ground Snapping section above).", MessageType.Info);
|
||||
break;
|
||||
|
||||
case SpawnPositionMode.RandomRange:
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
EditorGUILayout.LabelField("Random Y Range", EditorStyles.miniBoldLabel);
|
||||
EditorGUILayout.PropertyField(randomYMinProperty, new GUIContent("Min Y"));
|
||||
EditorGUILayout.PropertyField(randomYMaxProperty, new GUIContent("Max Y"));
|
||||
|
||||
// Validation
|
||||
if (randomYMinProperty.floatValue > randomYMaxProperty.floatValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Min Y should be less than Max Y!", MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox($"Obstacles will spawn at random Y between {randomYMinProperty.floatValue:F2} and {randomYMaxProperty.floatValue:F2}.", MessageType.Info);
|
||||
}
|
||||
EditorGUILayout.EndVertical();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: aa9b104969324b8584672126a4277d37
|
||||
timeCreated: 1765990746
|
||||
128
Assets/Editor/Minigames/Airplane/BaseDistanceSpawnerEditor.cs
Normal file
128
Assets/Editor/Minigames/Airplane/BaseDistanceSpawnerEditor.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
using Minigames.Airplane.Core.Spawning;
|
||||
using Minigames.Airplane.Data;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Editor.Minigames.Airplane
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for BaseDistanceSpawner that conditionally shows/hides spawn parameters
|
||||
/// based on the selected SpawnPoolMode.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(BaseDistanceSpawner), true)]
|
||||
public class BaseDistanceSpawnerEditor : UnityEditor.Editor
|
||||
{
|
||||
private SerializedProperty _poolModeProperty;
|
||||
private SerializedProperty _poolsProperty;
|
||||
private SerializedProperty _globalMinDistanceProperty;
|
||||
private SerializedProperty _globalMaxDistanceProperty;
|
||||
private SerializedProperty _recencyPenaltyProperty;
|
||||
private SerializedProperty _groundLayerProperty;
|
||||
private SerializedProperty _maxGroundRaycastProperty;
|
||||
private SerializedProperty _defaultObjectYOffsetProperty;
|
||||
private SerializedProperty _showDebugLogsProperty;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
_poolModeProperty = serializedObject.FindProperty("poolMode");
|
||||
_poolsProperty = serializedObject.FindProperty("pools");
|
||||
_globalMinDistanceProperty = serializedObject.FindProperty("globalMinDistance");
|
||||
_globalMaxDistanceProperty = serializedObject.FindProperty("globalMaxDistance");
|
||||
_recencyPenaltyProperty = serializedObject.FindProperty("recencyPenaltyDuration");
|
||||
_groundLayerProperty = serializedObject.FindProperty("groundLayer");
|
||||
_maxGroundRaycastProperty = serializedObject.FindProperty("maxGroundRaycastDistance");
|
||||
_defaultObjectYOffsetProperty = serializedObject.FindProperty("defaultObjectYOffset");
|
||||
_showDebugLogsProperty = serializedObject.FindProperty("showDebugLogs");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("Distance-Based Spawner", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox("Spawns objects at calculated X positions based on distance from plane. Containers are configured in AirplaneSpawnManager.", MessageType.Info);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Pool Configuration
|
||||
EditorGUILayout.LabelField("Pool Configuration", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_poolModeProperty);
|
||||
|
||||
SpawnPoolMode currentMode = (SpawnPoolMode)_poolModeProperty.enumValueIndex;
|
||||
|
||||
// Show global parameters only in Together mode
|
||||
if (currentMode == SpawnPoolMode.Together)
|
||||
{
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.LabelField("Global Spawn Parameters", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_globalMinDistanceProperty);
|
||||
EditorGUILayout.PropertyField(_globalMaxDistanceProperty);
|
||||
|
||||
EditorGUILayout.HelpBox("Together Mode: All pools use global spawn distances. Per-pool overrides are ignored.", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Exclusive Mode: Each pool spawns independently. Configure per-pool spawn distances below.", MessageType.Info);
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Pools array with custom display
|
||||
EditorGUILayout.PropertyField(_poolsProperty, true);
|
||||
|
||||
// Show per-pool parameter hints
|
||||
if (_poolsProperty.isExpanded && _poolsProperty.arraySize > 0)
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
for (int i = 0; i < _poolsProperty.arraySize; i++)
|
||||
{
|
||||
var poolProperty = _poolsProperty.GetArrayElementAtIndex(i);
|
||||
var overrideMinProperty = poolProperty.FindPropertyRelative("overrideMinDistance");
|
||||
var overrideMaxProperty = poolProperty.FindPropertyRelative("overrideMaxDistance");
|
||||
|
||||
if (currentMode == SpawnPoolMode.Together)
|
||||
{
|
||||
// Grey out per-pool overrides in Together mode
|
||||
if (overrideMinProperty.floatValue > 0 || overrideMaxProperty.floatValue > 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox($"Pool {i}: Per-pool overrides ignored in Together mode", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show active overrides in Exclusive mode
|
||||
if (overrideMinProperty.floatValue <= 0 && overrideMaxProperty.floatValue <= 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox($"Pool {i}: Using global distances (set overrides > 0 to customize)", MessageType.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Object Positioning
|
||||
EditorGUILayout.LabelField("Object Positioning", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_groundLayerProperty);
|
||||
EditorGUILayout.PropertyField(_maxGroundRaycastProperty);
|
||||
EditorGUILayout.PropertyField(_defaultObjectYOffsetProperty);
|
||||
EditorGUILayout.HelpBox("Prefabs can use PrefabSpawnEntryComponent to specify Y positioning: SnapToGround, SpecifiedY, or RandomRange", MessageType.Info);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Recency Tracking
|
||||
EditorGUILayout.LabelField("Recency Tracking", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_recencyPenaltyProperty);
|
||||
EditorGUILayout.HelpBox("Recently spawned prefabs receive lower weight for diversity", MessageType.Info);
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Debug
|
||||
EditorGUILayout.LabelField("Debug", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_showDebugLogsProperty);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 32aded175b00b8a4bae4a95861db8123
|
||||
@@ -0,0 +1,63 @@
|
||||
using Minigames.Airplane.Core.Spawning;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Editor.Minigames.Airplane
|
||||
{
|
||||
/// <summary>
|
||||
/// Simplified custom editor for GroundDistanceSpawner.
|
||||
/// Shows only ground-relevant fields. Ground Y and interval are in AirplaneSettings.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(GroundDistanceSpawner))]
|
||||
public class GroundDistanceSpawnerEditor : UnityEditor.Editor
|
||||
{
|
||||
private SerializedProperty _poolsProperty;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_poolsProperty = serializedObject.FindProperty("pools");
|
||||
|
||||
// Ensure exactly 1 pool exists
|
||||
if (_poolsProperty.arraySize != 1)
|
||||
{
|
||||
_poolsProperty.arraySize = 1;
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// Ensure exactly 1 pool
|
||||
if (_poolsProperty.arraySize != 1)
|
||||
{
|
||||
_poolsProperty.arraySize = 1;
|
||||
}
|
||||
|
||||
// Draw single pool
|
||||
EditorGUILayout.LabelField("Ground Tiles (Fixed: 1 pool)", EditorStyles.boldLabel);
|
||||
|
||||
var poolElement = _poolsProperty.GetArrayElementAtIndex(0);
|
||||
var prefabsProperty = poolElement.FindPropertyRelative("prefabs");
|
||||
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
EditorGUILayout.PropertyField(prefabsProperty, new GUIContent("Prefabs"), true);
|
||||
|
||||
if (prefabsProperty.arraySize == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Add at least one ground tile prefab", MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField($"Prefabs: {prefabsProperty.arraySize}", EditorStyles.miniLabel);
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 31225636ea0743fdbfe13fbb0c5f46af
|
||||
timeCreated: 1765987013
|
||||
@@ -0,0 +1,145 @@
|
||||
using Minigames.Airplane.Core.Spawning;
|
||||
using Minigames.Airplane.Data;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Editor.Minigames.Airplane
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for ObstacleDistanceSpawner.
|
||||
/// Enforces exactly 2 pools (Positive/Negative) with custom labels.
|
||||
/// All spawn parameters configured in AirplaneSettings.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(ObstacleDistanceSpawner))]
|
||||
public class ObstacleDistanceSpawnerEditor : UnityEditor.Editor
|
||||
{
|
||||
private SerializedProperty _poolModeProperty;
|
||||
private SerializedProperty _poolsProperty;
|
||||
|
||||
private readonly string[] _poolNames = { "Positive Obstacles", "Negative Obstacles" };
|
||||
private readonly string[] _poolDescriptions =
|
||||
{
|
||||
"Obstacles that benefit the player",
|
||||
"Obstacles that hinder the player"
|
||||
};
|
||||
|
||||
private bool[] _poolFoldouts = new bool[2];
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_poolModeProperty = serializedObject.FindProperty("poolMode");
|
||||
_poolsProperty = serializedObject.FindProperty("pools");
|
||||
|
||||
// Ensure exactly 2 pools exist
|
||||
EnsureTwoPools();
|
||||
}
|
||||
|
||||
private void EnsureTwoPools()
|
||||
{
|
||||
if (_poolsProperty != null && _poolsProperty.arraySize != 2)
|
||||
{
|
||||
_poolsProperty.arraySize = 2;
|
||||
|
||||
// Initialize pool descriptions
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var poolElement = _poolsProperty.GetArrayElementAtIndex(i);
|
||||
var descProperty = poolElement.FindPropertyRelative("description");
|
||||
if (descProperty != null && string.IsNullOrEmpty(descProperty.stringValue))
|
||||
{
|
||||
descProperty.stringValue = _poolNames[i];
|
||||
}
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.HelpBox("Note: Positive/Negative ratio, unlock times, spawn distances, recency tracking, positioning, and debug settings are configured globally in AirplaneSettings. Containers are configured in AirplaneSpawnManager.", MessageType.Info);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
if (_poolModeProperty != null)
|
||||
{
|
||||
EditorGUILayout.PropertyField(_poolModeProperty);
|
||||
|
||||
SpawnPoolMode currentMode = (SpawnPoolMode)_poolModeProperty.enumValueIndex;
|
||||
|
||||
if (currentMode == SpawnPoolMode.Together)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Together Mode: Both pools share a single spawn stream using global distances from settings.", MessageType.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox("Exclusive Mode: Each pool spawns independently. Use per-pool overrides or global distances from settings.", MessageType.Info);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Obstacle Pools (exactly 2, custom display)
|
||||
EditorGUILayout.LabelField("Obstacle Pools (Fixed: 2 pools)", EditorStyles.boldLabel);
|
||||
|
||||
EnsureTwoPools();
|
||||
|
||||
if (_poolsProperty != null && _poolsProperty.arraySize == 2)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var poolElement = _poolsProperty.GetArrayElementAtIndex(i);
|
||||
var prefabsProperty = poolElement.FindPropertyRelative("prefabs");
|
||||
var descriptionProperty = poolElement.FindPropertyRelative("description");
|
||||
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
||||
// Foldout with custom label
|
||||
_poolFoldouts[i] = EditorGUILayout.Foldout(_poolFoldouts[i], $"{_poolNames[i]} (Pool {i})", true, EditorStyles.foldoutHeader);
|
||||
|
||||
if (_poolFoldouts[i])
|
||||
{
|
||||
EditorGUI.indentLevel++;
|
||||
|
||||
EditorGUILayout.LabelField(_poolDescriptions[i], EditorStyles.miniLabel);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
if (prefabsProperty != null)
|
||||
{
|
||||
EditorGUILayout.PropertyField(prefabsProperty, new GUIContent("Prefabs"), true);
|
||||
}
|
||||
|
||||
if (descriptionProperty != null)
|
||||
{
|
||||
EditorGUILayout.PropertyField(descriptionProperty, new GUIContent("Description"));
|
||||
}
|
||||
|
||||
// Show info about prefab count
|
||||
if (prefabsProperty != null)
|
||||
{
|
||||
if (prefabsProperty.arraySize == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Add prefabs for this pool", MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.LabelField($"Prefabs: {prefabsProperty.arraySize}", EditorStyles.miniLabel);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
EditorGUILayout.EndVertical();
|
||||
EditorGUILayout.Space(5);
|
||||
}
|
||||
}
|
||||
|
||||
EditorGUILayout.Space();
|
||||
EditorGUILayout.HelpBox("Note: Positive/Negative ratio, unlock times, spawn distances, recency tracking, positioning, and debug settings are configured globally in AirplaneSettings. Containers are configured in AirplaneSpawnManager.", MessageType.Info);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d7f009b2123488381edd87bfb3ab2e8
|
||||
timeCreated: 1765985361
|
||||
@@ -0,0 +1,190 @@
|
||||
using Minigames.Airplane.Core.Spawning;
|
||||
using Minigames.Airplane.Data;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Editor.Minigames.Airplane
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for ParallaxBackgroundSpawner.
|
||||
/// Enforces exactly 3 pools with custom labels (Background/Middle/Foreground).
|
||||
/// Prevents array manipulation and provides clean, designer-friendly interface.
|
||||
/// </summary>
|
||||
[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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41d4944f4ed9436f84384fb2361f6e0c
|
||||
timeCreated: 1765972253
|
||||
@@ -0,0 +1,75 @@
|
||||
using Minigames.Airplane.Data;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Editor.Minigames.Airplane
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom editor for PrefabSpawnEntryComponent that conditionally shows fields based on spawn mode.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(PrefabSpawnEntryComponent))]
|
||||
public class PrefabSpawnEntryComponentEditor : UnityEditor.Editor
|
||||
{
|
||||
private SerializedProperty _spawnPositionModeProperty;
|
||||
private SerializedProperty _specifiedYProperty;
|
||||
private SerializedProperty _randomYMinProperty;
|
||||
private SerializedProperty _randomYMaxProperty;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_spawnPositionModeProperty = serializedObject.FindProperty("spawnPositionMode");
|
||||
_specifiedYProperty = serializedObject.FindProperty("specifiedY");
|
||||
_randomYMinProperty = serializedObject.FindProperty("randomYMin");
|
||||
_randomYMaxProperty = serializedObject.FindProperty("randomYMax");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
EditorGUILayout.LabelField("Prefab Spawn Positioning", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox("Controls how this prefab is positioned vertically when spawned. If this component is missing, spawner uses default settings from AirplaneSettings.", MessageType.Info);
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Always show the mode selector
|
||||
EditorGUILayout.PropertyField(_spawnPositionModeProperty, new GUIContent("Spawn Position Mode"));
|
||||
|
||||
SpawnPositionMode currentMode = (SpawnPositionMode)_spawnPositionModeProperty.enumValueIndex;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
// Show mode-specific fields
|
||||
switch (currentMode)
|
||||
{
|
||||
case SpawnPositionMode.SnapToGround:
|
||||
EditorGUILayout.HelpBox("Object will raycast down to find ground and snap its bottom to the surface. If no ground is found, fallback Y position from settings will be used.", MessageType.Info);
|
||||
break;
|
||||
|
||||
case SpawnPositionMode.SpecifiedY:
|
||||
EditorGUILayout.LabelField("Fixed Y Position", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_specifiedYProperty, new GUIContent("Y Position"));
|
||||
EditorGUILayout.HelpBox("Object will spawn at this exact Y coordinate.", MessageType.Info);
|
||||
break;
|
||||
|
||||
case SpawnPositionMode.RandomRange:
|
||||
EditorGUILayout.LabelField("Random Y Range", EditorStyles.boldLabel);
|
||||
EditorGUILayout.PropertyField(_randomYMinProperty, new GUIContent("Min Y"));
|
||||
EditorGUILayout.PropertyField(_randomYMaxProperty, new GUIContent("Max Y"));
|
||||
|
||||
// Validation
|
||||
if (_randomYMinProperty.floatValue > _randomYMaxProperty.floatValue)
|
||||
{
|
||||
EditorGUILayout.HelpBox("Min Y should be less than Max Y!", MessageType.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorGUILayout.HelpBox($"Object will spawn at random Y between {_randomYMinProperty.floatValue:F2} and {_randomYMaxProperty.floatValue:F2}.", MessageType.Info);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0eccbe64a21c4c07a09b2df66faf43b1
|
||||
timeCreated: 1765990535
|
||||
@@ -181,28 +181,38 @@ namespace AppleHills.Core.Settings.Editor
|
||||
return;
|
||||
}
|
||||
|
||||
SerializedObject serializedObj = serializedSettingsObjects[typeof(T).Name];
|
||||
serializedObj.Update();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Draw all properties
|
||||
SerializedProperty property = serializedObj.GetIterator();
|
||||
bool enterChildren = true;
|
||||
while (property.NextVisible(enterChildren))
|
||||
// Use CreateEditor to respect custom editors (like AirplaneSettingsEditor)
|
||||
UnityEditor.Editor editor = UnityEditor.Editor.CreateEditor(settings);
|
||||
if (editor != null)
|
||||
{
|
||||
enterChildren = false;
|
||||
|
||||
// Skip the script field
|
||||
if (property.name == "m_Script") continue;
|
||||
|
||||
EditorGUILayout.PropertyField(property, true);
|
||||
editor.OnInspectorGUI();
|
||||
DestroyImmediate(editor);
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
if (serializedObj.ApplyModifiedProperties())
|
||||
else
|
||||
{
|
||||
EditorUtility.SetDirty(settings);
|
||||
// Fallback to default drawing if no custom editor exists
|
||||
SerializedObject serializedObj = serializedSettingsObjects[typeof(T).Name];
|
||||
serializedObj.Update();
|
||||
|
||||
SerializedProperty property = serializedObj.GetIterator();
|
||||
bool enterChildren = true;
|
||||
while (property.NextVisible(enterChildren))
|
||||
{
|
||||
enterChildren = false;
|
||||
|
||||
// Skip the script field
|
||||
if (property.name == "m_Script") continue;
|
||||
|
||||
EditorGUILayout.PropertyField(property, true);
|
||||
}
|
||||
|
||||
// Apply changes
|
||||
if (serializedObj.ApplyModifiedProperties())
|
||||
{
|
||||
EditorUtility.SetDirty(settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user