Files
AppleHillsProduction/Assets/Scripts/Minigames/DivingForPictures/Bubbles/BubbleSpawner.cs
2025-10-17 13:18:49 +02:00

277 lines
9.3 KiB
C#

using System;
using System.Collections;
using UnityEngine;
using AppleHills.Core.Settings;
using AppleHills.Core.Interfaces;
using Core;
using Random = UnityEngine.Random;
namespace Minigames.DivingForPictures
{
/// <summary>
/// Spawns bubbles at intervals, randomizing their properties and assigning a random sprite to each.
/// </summary>
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 UnityEngine.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 = UnityEngine.Camera.main;
// Get developer settings and game settings
_devSettings = GameManager.GetDeveloperSettings<DivingDeveloperSettings>();
_gameSettings = GameManager.GetSettingsObject<IDivingMinigameSettings>();
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>();
_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()
{
// Start spawning if not paused
DivingGameManager.Instance.RegisterPausableComponent(this);
StartSpawningCoroutine();
}
void OnDestroy()
{
DivingGameManager.Instance?.UnregisterPausableComponent(this);
// Clean up any active coroutines
StopAllCoroutines();
}
/// <summary>
/// Pauses the bubble spawner and all bubbles
/// </summary>
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<Bubble>();
foreach (var bubble in activeBubbles)
{
if (bubble != null)
{
bubble.Pause();
}
}
Logging.Debug("[BubbleSpawner] Paused");
}
/// <summary>
/// Resumes the bubble spawner and all bubbles
/// </summary>
public void DoResume()
{
if (!_isPaused) return; // Already running
_isPaused = false;
// Restart spawning coroutine
StartSpawningCoroutine();
// Resume all active bubbles
Bubble[] activeBubbles = FindObjectsOfType<Bubble>();
foreach (var bubble in activeBubbles)
{
if (bubble != null)
{
bubble.DoResume();
}
}
Logging.Debug("[BubbleSpawner] Resumed");
}
/// <summary>
/// Starts the bubble spawning coroutine if it's not already running
/// </summary>
private void StartSpawningCoroutine()
{
if (_spawnCoroutine == null && !_isPaused)
{
_spawnCoroutine = StartCoroutine(SpawnBubblesRoutine());
}
}
/// <summary>
/// Sets the next spawn interval using randomized timing
/// </summary>
private void SetNextSpawnInterval()
{
if (_devSettings == null) return;
_nextSpawnInterval = GetRandomizedInterval();
Logging.Debug($"[BubbleSpawner] Next spawn interval set to: {_nextSpawnInterval:F2}s");
}
/// <summary>
/// Returns a randomized interval for bubble spawning.
/// </summary>
/// <returns>Randomized interval in seconds.</returns>
float GetRandomizedInterval()
{
return _devSettings.BubbleSpawnInterval * Random.Range(0.8f, 1.2f);
}
/// <summary>
/// Spawns a bubble with randomized properties and assigns a random sprite.
/// </summary>
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();
}
}
/// <summary>
/// Start surfacing mode - slow down all bubbles
/// </summary>
public void StartSurfacing()
{
if (_isSurfacing) return; // Already surfacing
_isSurfacing = true;
// Slow down all existing bubbles
Bubble[] activeBubbles = FindObjectsByType<Bubble>(FindObjectsSortMode.None);
foreach (Bubble bubble in activeBubbles)
{
bubble.speed *= _devSettings.BubbleSurfacingSpeedFactor;
}
Logging.Debug($"[BubbleSpawner] Started surfacing mode. Bubbles slowed to {_devSettings.BubbleSurfacingSpeedFactor * 100}% speed.");
}
/// <summary>
/// Logs the current pool statistics for debugging
/// </summary>
private void LogPoolStats()
{
if (_bubblePool != null)
{
_bubblePool.LogPoolStats();
}
}
/// <summary>
/// Coroutine that handles the bubble spawning logic
/// </summary>
private IEnumerator SpawnBubblesRoutine()
{
Logging.Debug("[BubbleSpawner] Started bubble spawning coroutine");
while (enabled && gameObject.activeInHierarchy && !_isPaused)
{
SpawnBubble();
SetNextSpawnInterval(); // Set interval for next spawn
yield return new WaitForSeconds(_nextSpawnInterval);
}
_spawnCoroutine = null;
Logging.Debug("[BubbleSpawner] Bubble spawning coroutine ended");
}
}
}