using System.Collections; using UnityEngine; using Pooling; using AppleHills.Core.Settings; using AppleHills.Core.Interfaces; namespace Minigames.DivingForPictures { /// /// Spawns bubbles at intervals, randomizing their properties and assigning a random sprite to each. /// public class BubbleSpawner : MonoBehaviour, IPausable { public Bubble bubblePrefab; public Sprite[] bubbleSprites; // Assign in inspector private DivingDeveloperSettings _devSettings; private IDivingMinigameSettings _gameSettings; private float _timer; private float _nextSpawnInterval; private BubblePool _bubblePool; private Camera _mainCamera; // Cache camera reference private bool _isSurfacing = false; // Pause state private bool _isPaused = false; // Coroutines for pause/resume private Coroutine _spawnCoroutine; // IPausable implementation public bool IsPaused => _isPaused; void Awake() { _mainCamera = Camera.main; // Get developer settings and game settings _devSettings = GameManager.GetDeveloperSettings(); _gameSettings = GameManager.GetSettingsObject(); if (_devSettings == null) { Debug.LogError("[BubbleSpawner] Failed to load developer settings!"); return; } if (_devSettings.BubbleUseObjectPooling) { // Create the bubble pool GameObject poolGO = new GameObject("BubblePool"); poolGO.transform.SetParent(transform); _bubblePool = poolGO.AddComponent(); _bubblePool.initialPoolSize = _devSettings.BubbleInitialPoolSize; _bubblePool.maxPoolSize = _devSettings.BubbleMaxPoolSize; _bubblePool.Initialize(bubblePrefab); // Periodically check for pool statistics in debug builds #if DEVELOPMENT_BUILD || UNITY_EDITOR InvokeRepeating(nameof(LogPoolStats), 5f, 30f); #endif } SetNextSpawnInterval(); } void Start() { // Register with DivingGameManager for pause/resume events DivingGameManager gameManager = FindFirstObjectByType(); if (gameManager != null) { gameManager.RegisterPausableComponent(this); } // Start spawning if not paused StartSpawningCoroutine(); } void OnDestroy() { // Unregister from DivingGameManager DivingGameManager gameManager = FindFirstObjectByType(); if (gameManager != null) { gameManager.UnregisterPausableComponent(this); } // Clean up any active coroutines StopAllCoroutines(); } /// /// Pauses the bubble spawner and all bubbles /// public void Pause() { if (_isPaused) return; // Already paused _isPaused = true; // Stop spawning coroutine if (_spawnCoroutine != null) { StopCoroutine(_spawnCoroutine); _spawnCoroutine = null; } // Pause all active bubbles Bubble[] activeBubbles = FindObjectsOfType(); foreach (var bubble in activeBubbles) { if (bubble != null) { bubble.Pause(); } } Debug.Log("[BubbleSpawner] Paused"); } /// /// Resumes the bubble spawner and all bubbles /// public void Resume() { if (!_isPaused) return; // Already running _isPaused = false; // Restart spawning coroutine StartSpawningCoroutine(); // Resume all active bubbles Bubble[] activeBubbles = FindObjectsOfType(); foreach (var bubble in activeBubbles) { if (bubble != null) { bubble.Resume(); } } Debug.Log("[BubbleSpawner] Resumed"); } /// /// Starts the bubble spawning coroutine if it's not already running /// private void StartSpawningCoroutine() { if (_spawnCoroutine == null && !_isPaused) { _spawnCoroutine = StartCoroutine(SpawnBubblesRoutine()); } } /// /// Sets the next spawn interval using randomized timing /// private void SetNextSpawnInterval() { if (_devSettings == null) return; _nextSpawnInterval = GetRandomizedInterval(); Debug.Log($"[BubbleSpawner] Next spawn interval set to: {_nextSpawnInterval:F2}s"); } /// /// Returns a randomized interval for bubble spawning. /// /// Randomized interval in seconds. float GetRandomizedInterval() { return _devSettings.BubbleSpawnInterval * Random.Range(0.8f, 1.2f); } /// /// Spawns a bubble with randomized properties and assigns a random sprite. /// void SpawnBubble() { float x = Random.Range(_devSettings.BubbleSpawnXMin, _devSettings.BubbleSpawnXMax); Vector3 spawnPos = new Vector3(x, _devSettings.BubbleSpawnY, 0f); Bubble bubble; if (_devSettings.BubbleUseObjectPooling && _bubblePool != null) { bubble = _bubblePool.GetBubble(); bubble.transform.position = spawnPos; } else { bubble = Instantiate(bubblePrefab, spawnPos, Quaternion.identity, transform); } // Randomize bubble properties float baseSpeed = Random.Range(_devSettings.BubbleSpeedRange.x, _devSettings.BubbleSpeedRange.y); // Apply surfacing speed reduction if needed if (_isSurfacing) { bubble.speed = baseSpeed * _devSettings.BubbleSurfacingSpeedFactor; } else { bubble.speed = baseSpeed; } bubble.wobbleSpeed = Random.Range(_devSettings.BubbleWobbleSpeedRange.x, _devSettings.BubbleWobbleSpeedRange.y); // Set base scale (initial size) for the bubble float baseScale = Random.Range(_devSettings.BubbleScaleRange.x, _devSettings.BubbleScaleRange.y); bubble.SetBaseScale(baseScale); // Assign random sprite to BubbleSprite if (bubbleSprites != null && bubbleSprites.Length > 0) { Sprite randomSprite = bubbleSprites[Random.Range(0, bubbleSprites.Length)]; bubble.SetBubbleSprite(randomSprite); } // Random rotation bubble.transform.rotation = Quaternion.Euler(0f, 0f, Random.Range(0f, 360f)); // Pass min/max scale for wobble clamping bubble.SetWobbleScaleLimits(_devSettings.BubbleWobbleMinScale, _devSettings.BubbleWobbleMaxScale); // If the game is already paused, pause this bubble immediately if (_isPaused) { bubble.Pause(); } } /// /// Start surfacing mode - slow down all bubbles /// public void StartSurfacing() { if (_isSurfacing) return; // Already surfacing _isSurfacing = true; // Slow down all existing bubbles Bubble[] activeBubbles = FindObjectsByType(FindObjectsSortMode.None); foreach (Bubble bubble in activeBubbles) { bubble.speed *= _devSettings.BubbleSurfacingSpeedFactor; } Debug.Log($"[BubbleSpawner] Started surfacing mode. Bubbles slowed to {_devSettings.BubbleSurfacingSpeedFactor * 100}% speed."); } /// /// Logs the current pool statistics for debugging /// private void LogPoolStats() { if (_bubblePool != null) { _bubblePool.LogPoolStats(); } } /// /// Coroutine that handles the bubble spawning logic /// private IEnumerator SpawnBubblesRoutine() { Debug.Log("[BubbleSpawner] Started bubble spawning coroutine"); while (enabled && gameObject.activeInHierarchy && !_isPaused) { SpawnBubble(); SetNextSpawnInterval(); // Set interval for next spawn yield return new WaitForSeconds(_nextSpawnInterval); } _spawnCoroutine = null; Debug.Log("[BubbleSpawner] Bubble spawning coroutine ended"); } } }