diff --git a/Assets/Editor/DivingGameManagerEditor.cs b/Assets/Editor/DivingGameManagerEditor.cs
new file mode 100644
index 00000000..53bde798
--- /dev/null
+++ b/Assets/Editor/DivingGameManagerEditor.cs
@@ -0,0 +1,48 @@
+using UnityEditor;
+using UnityEngine;
+using Minigames.DivingForPictures;
+
+///
+/// Custom editor for DivingGameManager that adds runtime buttons for testing surfacing and other functionality
+///
+[CustomEditor(typeof(DivingGameManager))]
+public class DivingGameManagerEditor : UnityEditor.Editor
+{
+ public override void OnInspectorGUI()
+ {
+ // Draw the default inspector
+ DrawDefaultInspector();
+
+ // Get the target DivingGameManager
+ DivingGameManager manager = (DivingGameManager)target;
+
+ // Add space between default inspector and custom buttons
+ EditorGUILayout.Space(10);
+
+ // Separator line
+ EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
+
+ // Add a label for the runtime testing section
+ EditorGUILayout.LabelField("Runtime Testing", EditorStyles.boldLabel);
+
+ // Only enable the buttons during play mode
+ EditorGUI.BeginDisabledGroup(!Application.isPlaying);
+
+ // Add the button to call StartSurfacing
+ if (GUILayout.Button("Start Surfacing", GUILayout.Height(30)))
+ {
+ manager.StartSurfacing();
+ }
+
+ // Add a button for breaking a rope (for testing damage)
+ if (GUILayout.Button("Break Rope (Test Damage)", GUILayout.Height(30)))
+ {
+ manager.ForceBreakRope();
+ }
+
+ EditorGUI.EndDisabledGroup();
+
+ // Add explanatory text
+ EditorGUILayout.HelpBox("These buttons only work in Play Mode. 'Start Surfacing' will reverse the trench direction, slow bubbles, and reverse obstacles. 'Break Rope' simulates player taking damage.", MessageType.Info);
+ }
+}
diff --git a/Assets/Editor/DivingGameManagerEditor.cs.meta b/Assets/Editor/DivingGameManagerEditor.cs.meta
new file mode 100644
index 00000000..cde212bf
--- /dev/null
+++ b/Assets/Editor/DivingGameManagerEditor.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 8bbb340d8d9b4af581770757e86cc1f8
+timeCreated: 1758532258
\ No newline at end of file
diff --git a/Assets/Scripts/Minigames/DivingForPictures/BubbleSpawner.cs b/Assets/Scripts/Minigames/DivingForPictures/BubbleSpawner.cs
index 31758f57..d4859a84 100644
--- a/Assets/Scripts/Minigames/DivingForPictures/BubbleSpawner.cs
+++ b/Assets/Scripts/Minigames/DivingForPictures/BubbleSpawner.cs
@@ -26,10 +26,15 @@ namespace Minigames.DivingForPictures
public int initialPoolSize = 10;
public int maxPoolSize = 30;
+ [Header("Surfacing Settings")]
+ [Tooltip("Factor to multiply bubble speed by when surfacing (0.5 = half speed)")]
+ [SerializeField] private float surfacingSpeedFactor = 0.5f;
+
private float _timer;
private float _nextSpawnInterval;
private BubblePool _bubblePool;
private Camera _mainCamera; // Cache camera reference
+ private bool _isSurfacing = false;
void Awake()
{
@@ -98,7 +103,18 @@ namespace Minigames.DivingForPictures
}
// Randomize bubble properties
- bubble.speed = Random.Range(speedRange.x, speedRange.y);
+ float baseSpeed = Random.Range(speedRange.x, speedRange.y);
+
+ // Apply surfacing speed reduction if needed
+ if (_isSurfacing)
+ {
+ bubble.speed = baseSpeed * surfacingSpeedFactor;
+ }
+ else
+ {
+ bubble.speed = baseSpeed;
+ }
+
bubble.wobbleSpeed = Random.Range(wobbleSpeedRange.x, wobbleSpeedRange.y);
// Set base scale (initial size) for the bubble
@@ -119,6 +135,25 @@ namespace Minigames.DivingForPictures
bubble.SetWobbleScaleLimits(wobbleMinScale, wobbleMaxScale);
}
+ ///
+ /// Start surfacing mode - slow down all bubbles
+ ///
+ public void StartSurfacing()
+ {
+ if (_isSurfacing) return; // Already surfacing
+
+ _isSurfacing = true;
+
+ // Slow down all existing bubbles
+ Bubble[] activeBubbles = FindObjectsOfType();
+ foreach (Bubble bubble in activeBubbles)
+ {
+ bubble.speed *= surfacingSpeedFactor;
+ }
+
+ Debug.Log($"[BubbleSpawner] Started surfacing mode. Bubbles slowed to {surfacingSpeedFactor * 100}% speed.");
+ }
+
///
/// Logs the current pool statistics for debugging
///
diff --git a/Assets/Scripts/Minigames/DivingForPictures/DivingGameManager.cs b/Assets/Scripts/Minigames/DivingForPictures/DivingGameManager.cs
index 8b8b8b09..0f51521c 100644
--- a/Assets/Scripts/Minigames/DivingForPictures/DivingGameManager.cs
+++ b/Assets/Scripts/Minigames/DivingForPictures/DivingGameManager.cs
@@ -56,6 +56,10 @@ namespace Minigames.DivingForPictures
// Private state variables for rope system
private int currentRopeIndex = 0;
private bool isGameOver = false;
+ private bool _isSurfacing = false;
+
+ // Used to track if we're currently surfacing
+ public bool IsSurfacing => _isSurfacing;
private void Awake()
{
@@ -114,6 +118,9 @@ namespace Minigames.DivingForPictures
if (spawnPoints.Length == 0) return;
+ // If we're surfacing, don't spawn new monsters
+ if (_isSurfacing) return;
+
bool forceSpawn = timeSinceLastSpawn >= guaranteedSpawnTime;
bool onCooldown = timeSinceLastSpawn < spawnCooldown;
@@ -319,6 +326,40 @@ namespace Minigames.DivingForPictures
Debug.Log("[DivingGameManager] Rope system reset.");
}
+ ///
+ /// Starts the surfacing mode - reverses trench direction and adjusts all spawned entities
+ ///
+ public void StartSurfacing()
+ {
+ if (_isSurfacing) return; // Already surfacing
+
+ _isSurfacing = true;
+
+ // 1. Find and reverse trench tile spawner
+ TrenchTileSpawner tileSpawner = FindFirstObjectByType();
+ if (tileSpawner != null)
+ {
+ tileSpawner.StartSurfacing();
+ }
+
+ // 2. Find bubble spawner and slow down existing bubbles
+ BubbleSpawner bubbleSpawner = FindFirstObjectByType();
+ if (bubbleSpawner != null)
+ {
+ bubbleSpawner.StartSurfacing();
+ }
+
+ // 3. Find obstacle spawner and reverse existing obstacles
+ ObstacleSpawner obstacleSpawner = FindFirstObjectByType();
+ if (obstacleSpawner != null)
+ {
+ obstacleSpawner.StartSurfacing();
+ }
+
+ // Note: Monster spawning is handled automatically through the Update and OnTileSpawned methods
+ // which will check the _isSurfacing flag
+ }
+
// Call this when the game ends
public void EndGame()
{
diff --git a/Assets/Scripts/Minigames/DivingForPictures/FloatingObstacle.cs b/Assets/Scripts/Minigames/DivingForPictures/FloatingObstacle.cs
index 52338634..2f430646 100644
--- a/Assets/Scripts/Minigames/DivingForPictures/FloatingObstacle.cs
+++ b/Assets/Scripts/Minigames/DivingForPictures/FloatingObstacle.cs
@@ -44,8 +44,10 @@ namespace Minigames.DivingForPictures
private Collider2D _collider;
private Camera _mainCamera;
private float _screenTop;
+ private float _screenBottom; // Added to track bottom of screen
private Coroutine _movementCoroutine;
private Coroutine _offScreenCheckCoroutine;
+ private bool _isSurfacing = false; // Flag to track surfacing state
private void Awake()
{
@@ -165,6 +167,9 @@ namespace Minigames.DivingForPictures
Vector3 topWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 1f, _mainCamera.transform.position.z));
_screenTop = topWorldPoint.y;
+ Vector3 bottomWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 0f, _mainCamera.transform.position.z));
+ _screenBottom = bottomWorldPoint.y;
+
// Check if obstacle is significantly above screen top (obstacles move upward)
// Use a larger buffer to ensure obstacles are truly off-screen before returning to pool
if (transform.position.y > _screenTop + 5f)
@@ -172,6 +177,11 @@ namespace Minigames.DivingForPictures
Debug.Log($"[FloatingObstacle] {gameObject.name} off-screen at Y:{transform.position.y:F2}, screen top:{_screenTop:F2}");
ReturnToPool();
}
+ else if (transform.position.y < _screenBottom - 5f) // Added check for bottom screen edge
+ {
+ Debug.Log($"[FloatingObstacle] {gameObject.name} below screen at Y:{transform.position.y:F2}, screen bottom:{_screenBottom:F2}");
+ ReturnToPool();
+ }
}
///
@@ -279,5 +289,20 @@ namespace Minigames.DivingForPictures
StartObstacleBehavior();
}
}
+
+ ///
+ /// Sets surfacing mode, which reverses obstacle movement direction
+ ///
+ public void StartSurfacing()
+ {
+ if (_isSurfacing) return; // Already surfacing
+
+ _isSurfacing = true;
+
+ // Reverse movement speed (already handled by ObstacleSpawner, but this ensures consistency)
+ moveSpeed *= -1;
+
+ Debug.Log($"[FloatingObstacle] {gameObject.name} started surfacing with speed: {moveSpeed}");
+ }
}
}
diff --git a/Assets/Scripts/Minigames/DivingForPictures/ObstacleSpawner.cs b/Assets/Scripts/Minigames/DivingForPictures/ObstacleSpawner.cs
index 67f0cffd..e3ecdcd4 100644
--- a/Assets/Scripts/Minigames/DivingForPictures/ObstacleSpawner.cs
+++ b/Assets/Scripts/Minigames/DivingForPictures/ObstacleSpawner.cs
@@ -75,6 +75,7 @@ namespace Minigames.DivingForPictures
private Coroutine _spawnCoroutine;
private readonly List _activeObstacles = new List();
private int _obstacleCounter = 0; // Counter for unique obstacle naming
+ private bool _isSurfacing = false; // Flag to track surfacing state
private void Awake()
{
@@ -250,6 +251,13 @@ namespace Minigames.DivingForPictures
///
private void TrySpawnObstacle()
{
+ // Don't spawn new obstacles when surfacing
+ if (_isSurfacing)
+ {
+ Debug.Log("[ObstacleSpawner] Skipping obstacle spawn - currently surfacing");
+ return;
+ }
+
Debug.Log($"[ObstacleSpawner] TrySpawnObstacle called at {Time.time:F2}");
if (obstaclePrefabs == null || obstaclePrefabs.Count == 0)
@@ -338,25 +346,53 @@ namespace Minigames.DivingForPictures
return;
}
- Debug.Log($"[ObstacleSpawner] Got obstacle {obstacle.name} from pool, active state: {obstacle.activeInHierarchy}");
-
- // FORCE ACTIVATION - bypass pool issues
- if (!obstacle.activeInHierarchy)
- {
- Debug.LogWarning($"[ObstacleSpawner] Pool returned inactive object {obstacle.name}, force activating!");
- obstacle.SetActive(true);
- Debug.Log($"[ObstacleSpawner] After force activation, {obstacle.name} active state: {obstacle.activeInHierarchy}");
- }
-
+ // Important: Set position/parent/rotation BEFORE activation to avoid visual glitches
obstacle.transform.position = position;
obstacle.transform.rotation = prefab.transform.rotation;
obstacle.transform.SetParent(transform);
- Debug.Log($"[ObstacleSpawner] After positioning, obstacle {obstacle.name} active state: {obstacle.activeInHierarchy}");
+
+ Debug.Log($"[ObstacleSpawner] Got obstacle {obstacle.name} from pool, active state: {obstacle.activeInHierarchy}");
+
+ // ENHANCED FORCE ACTIVATION - more robust approach
+ if (!obstacle.activeInHierarchy)
+ {
+ Debug.LogWarning($"[ObstacleSpawner] Pool returned inactive object {obstacle.name}, force activating!");
+
+ // Configure obstacle BEFORE activation
+ ConfigureObstacle(obstacle, prefabIndex);
+
+ // Force activate the obstacle
+ obstacle.SetActive(true);
+
+ // Double-check activation status
+ if (!obstacle.activeInHierarchy)
+ {
+ Debug.LogError($"[ObstacleSpawner] CRITICAL ERROR: Failed to activate {obstacle.name} after multiple attempts!");
+
+ // Last resort: try to instantiate a new one instead
+ GameObject newObstacle = Instantiate(prefab, position, prefab.transform.rotation, transform);
+ if (newObstacle != null)
+ {
+ obstacle = newObstacle;
+ ConfigureObstacle(obstacle, prefabIndex);
+ }
+ }
+
+ Debug.Log($"[ObstacleSpawner] After force activation, {obstacle.name} active state: {obstacle.activeInHierarchy}");
+ }
+ else
+ {
+ // Still configure if already active
+ ConfigureObstacle(obstacle, prefabIndex);
+ }
}
else
{
Debug.Log($"[ObstacleSpawner] Instantiating new obstacle (pooling disabled)");
obstacle = Instantiate(prefab, position, prefab.transform.rotation, transform);
+
+ // Configure the newly instantiated obstacle
+ ConfigureObstacle(obstacle, prefabIndex);
}
// Assign unique name with counter
@@ -364,10 +400,6 @@ namespace Minigames.DivingForPictures
string oldName = obstacle.name;
obstacle.name = $"Obstacle{_obstacleCounter:D3}";
Debug.Log($"[ObstacleSpawner] Renamed obstacle from '{oldName}' to '{obstacle.name}', active state: {obstacle.activeInHierarchy}");
-
- // Configure the obstacle
- ConfigureObstacle(obstacle, prefabIndex);
- Debug.Log($"[ObstacleSpawner] After configuration, obstacle {obstacle.name} active state: {obstacle.activeInHierarchy}");
// Track active obstacles
_activeObstacles.Add(obstacle);
@@ -459,6 +491,32 @@ namespace Minigames.DivingForPictures
CalculateScreenBounds();
}
+ ///
+ /// Start surfacing mode - reverse direction of existing obstacles and stop spawning new ones
+ ///
+ public void StartSurfacing()
+ {
+ if (_isSurfacing) return; // Already surfacing
+
+ _isSurfacing = true;
+
+ // Reverse direction of all existing obstacles
+ foreach (GameObject obstacle in _activeObstacles)
+ {
+ if (obstacle != null)
+ {
+ FloatingObstacle obstacleComponent = obstacle.GetComponent();
+ if (obstacleComponent != null)
+ {
+ // Call StartSurfacing on the obstacle component itself
+ obstacleComponent.StartSurfacing();
+ }
+ }
+ }
+
+ Debug.Log($"[ObstacleSpawner] Started surfacing mode. Reversed direction of {_activeObstacles.Count} active obstacles.");
+ }
+
///
/// Gets the count of currently active obstacles
///