2025-10-08 12:36:08 +02:00
|
|
|
|
using System.Collections;
|
|
|
|
|
|
using UnityEngine;
|
2025-09-24 13:33:43 +00:00
|
|
|
|
using AppleHills.Core.Settings;
|
2025-10-08 12:36:08 +02:00
|
|
|
|
using AppleHills.Core.Interfaces;
|
2025-09-10 09:36:15 +02:00
|
|
|
|
|
|
|
|
|
|
namespace Minigames.DivingForPictures
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Spawns bubbles at intervals, randomizing their properties and assigning a random sprite to each.
|
|
|
|
|
|
/// </summary>
|
2025-10-08 12:36:08 +02:00
|
|
|
|
public class BubbleSpawner : MonoBehaviour, IPausable
|
2025-09-10 09:36:15 +02:00
|
|
|
|
{
|
|
|
|
|
|
public Bubble bubblePrefab;
|
|
|
|
|
|
public Sprite[] bubbleSprites; // Assign in inspector
|
2025-09-16 12:23:25 +02:00
|
|
|
|
|
2025-09-24 13:33:43 +00:00
|
|
|
|
private DivingDeveloperSettings _devSettings;
|
|
|
|
|
|
private IDivingMinigameSettings _gameSettings;
|
2025-09-22 12:16:32 +00:00
|
|
|
|
|
2025-09-10 09:36:15 +02:00
|
|
|
|
private float _timer;
|
|
|
|
|
|
private float _nextSpawnInterval;
|
2025-09-16 12:23:25 +02:00
|
|
|
|
private BubblePool _bubblePool;
|
2025-10-10 14:31:51 +02:00
|
|
|
|
private UnityEngine.Camera _mainCamera; // Cache camera reference
|
2025-09-22 12:16:32 +00:00
|
|
|
|
private bool _isSurfacing = false;
|
2025-10-08 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
|
// Pause state
|
|
|
|
|
|
private bool _isPaused = false;
|
|
|
|
|
|
|
|
|
|
|
|
// Coroutines for pause/resume
|
|
|
|
|
|
private Coroutine _spawnCoroutine;
|
|
|
|
|
|
|
|
|
|
|
|
// IPausable implementation
|
|
|
|
|
|
public bool IsPaused => _isPaused;
|
2025-09-16 12:23:25 +02:00
|
|
|
|
|
|
|
|
|
|
void Awake()
|
|
|
|
|
|
{
|
2025-10-10 14:31:51 +02:00
|
|
|
|
_mainCamera = UnityEngine.Camera.main;
|
2025-09-16 12:23:25 +02:00
|
|
|
|
|
2025-09-24 13:33:43 +00:00
|
|
|
|
// 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)
|
2025-09-16 12:23:25 +02:00
|
|
|
|
{
|
|
|
|
|
|
// Create the bubble pool
|
|
|
|
|
|
GameObject poolGO = new GameObject("BubblePool");
|
|
|
|
|
|
poolGO.transform.SetParent(transform);
|
|
|
|
|
|
_bubblePool = poolGO.AddComponent<BubblePool>();
|
2025-09-24 13:33:43 +00:00
|
|
|
|
_bubblePool.initialPoolSize = _devSettings.BubbleInitialPoolSize;
|
|
|
|
|
|
_bubblePool.maxPoolSize = _devSettings.BubbleMaxPoolSize;
|
2025-09-16 12:23:25 +02:00
|
|
|
|
_bubblePool.Initialize(bubblePrefab);
|
2025-09-16 15:02:50 +02:00
|
|
|
|
|
|
|
|
|
|
// Periodically check for pool statistics in debug builds
|
|
|
|
|
|
#if DEVELOPMENT_BUILD || UNITY_EDITOR
|
|
|
|
|
|
InvokeRepeating(nameof(LogPoolStats), 5f, 30f);
|
|
|
|
|
|
#endif
|
2025-09-16 12:23:25 +02:00
|
|
|
|
}
|
2025-10-08 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
|
SetNextSpawnInterval();
|
2025-09-16 12:23:25 +02:00
|
|
|
|
}
|
2025-09-10 09:36:15 +02:00
|
|
|
|
|
|
|
|
|
|
void Start()
|
|
|
|
|
|
{
|
2025-10-10 14:31:51 +02:00
|
|
|
|
DivingGameManager.Instance.RegisterPausableComponent(this);
|
2025-10-08 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
|
// Start spawning if not paused
|
|
|
|
|
|
StartSpawningCoroutine();
|
2025-09-10 09:36:15 +02:00
|
|
|
|
}
|
2025-10-08 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
|
void OnDestroy()
|
2025-09-10 09:36:15 +02:00
|
|
|
|
{
|
2025-10-10 14:31:51 +02:00
|
|
|
|
DivingGameManager.Instance.UnregisterPausableComponent(this);
|
2025-10-08 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log("[BubbleSpawner] Paused");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Resumes the bubble spawner and all bubbles
|
|
|
|
|
|
/// </summary>
|
2025-10-10 14:31:51 +02:00
|
|
|
|
public void DoResume()
|
2025-10-08 12:36:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
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)
|
|
|
|
|
|
{
|
2025-10-10 14:31:51 +02:00
|
|
|
|
bubble.DoResume();
|
2025-10-08 12:36:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log("[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());
|
2025-09-10 09:36:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-08 12:36:08 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets the next spawn interval using randomized timing
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void SetNextSpawnInterval()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_devSettings == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
_nextSpawnInterval = GetRandomizedInterval();
|
|
|
|
|
|
Debug.Log($"[BubbleSpawner] Next spawn interval set to: {_nextSpawnInterval:F2}s");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 09:36:15 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Returns a randomized interval for bubble spawning.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>Randomized interval in seconds.</returns>
|
|
|
|
|
|
float GetRandomizedInterval()
|
|
|
|
|
|
{
|
2025-09-24 13:33:43 +00:00
|
|
|
|
return _devSettings.BubbleSpawnInterval * Random.Range(0.8f, 1.2f);
|
2025-09-10 09:36:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Spawns a bubble with randomized properties and assigns a random sprite.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
void SpawnBubble()
|
|
|
|
|
|
{
|
2025-09-24 13:33:43 +00:00
|
|
|
|
float x = Random.Range(_devSettings.BubbleSpawnXMin, _devSettings.BubbleSpawnXMax);
|
|
|
|
|
|
Vector3 spawnPos = new Vector3(x, _devSettings.BubbleSpawnY, 0f);
|
2025-09-16 12:23:25 +02:00
|
|
|
|
|
|
|
|
|
|
Bubble bubble;
|
2025-09-24 13:33:43 +00:00
|
|
|
|
if (_devSettings.BubbleUseObjectPooling && _bubblePool != null)
|
2025-09-16 12:23:25 +02:00
|
|
|
|
{
|
|
|
|
|
|
bubble = _bubblePool.GetBubble();
|
|
|
|
|
|
bubble.transform.position = spawnPos;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
bubble = Instantiate(bubblePrefab, spawnPos, Quaternion.identity, transform);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-10 09:36:15 +02:00
|
|
|
|
// Randomize bubble properties
|
2025-09-24 13:33:43 +00:00
|
|
|
|
float baseSpeed = Random.Range(_devSettings.BubbleSpeedRange.x, _devSettings.BubbleSpeedRange.y);
|
2025-09-22 12:16:32 +00:00
|
|
|
|
|
|
|
|
|
|
// Apply surfacing speed reduction if needed
|
|
|
|
|
|
if (_isSurfacing)
|
|
|
|
|
|
{
|
2025-09-24 13:33:43 +00:00
|
|
|
|
bubble.speed = baseSpeed * _devSettings.BubbleSurfacingSpeedFactor;
|
2025-09-22 12:16:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
bubble.speed = baseSpeed;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 13:33:43 +00:00
|
|
|
|
bubble.wobbleSpeed = Random.Range(_devSettings.BubbleWobbleSpeedRange.x, _devSettings.BubbleWobbleSpeedRange.y);
|
2025-09-16 12:23:25 +02:00
|
|
|
|
|
|
|
|
|
|
// Set base scale (initial size) for the bubble
|
2025-09-24 13:33:43 +00:00
|
|
|
|
float baseScale = Random.Range(_devSettings.BubbleScaleRange.x, _devSettings.BubbleScaleRange.y);
|
2025-09-16 12:23:25 +02:00
|
|
|
|
bubble.SetBaseScale(baseScale);
|
|
|
|
|
|
|
2025-09-24 13:33:43 +00:00
|
|
|
|
// Assign random sprite to BubbleSprite
|
2025-09-10 09:36:15 +02:00
|
|
|
|
if (bubbleSprites != null && bubbleSprites.Length > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Sprite randomSprite = bubbleSprites[Random.Range(0, bubbleSprites.Length)];
|
2025-09-16 12:23:25 +02:00
|
|
|
|
bubble.SetBubbleSprite(randomSprite);
|
2025-09-10 09:36:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Random rotation
|
|
|
|
|
|
bubble.transform.rotation = Quaternion.Euler(0f, 0f, Random.Range(0f, 360f));
|
2025-09-16 12:23:25 +02:00
|
|
|
|
|
2025-09-10 09:36:15 +02:00
|
|
|
|
// Pass min/max scale for wobble clamping
|
2025-09-24 13:33:43 +00:00
|
|
|
|
bubble.SetWobbleScaleLimits(_devSettings.BubbleWobbleMinScale, _devSettings.BubbleWobbleMaxScale);
|
2025-10-08 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
|
// If the game is already paused, pause this bubble immediately
|
|
|
|
|
|
if (_isPaused)
|
|
|
|
|
|
{
|
|
|
|
|
|
bubble.Pause();
|
|
|
|
|
|
}
|
2025-09-10 09:36:15 +02:00
|
|
|
|
}
|
2025-09-16 15:02:50 +02:00
|
|
|
|
|
2025-09-22 12:16:32 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Start surfacing mode - slow down all bubbles
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void StartSurfacing()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_isSurfacing) return; // Already surfacing
|
|
|
|
|
|
|
|
|
|
|
|
_isSurfacing = true;
|
|
|
|
|
|
|
|
|
|
|
|
// Slow down all existing bubbles
|
2025-09-22 14:50:48 +02:00
|
|
|
|
Bubble[] activeBubbles = FindObjectsByType<Bubble>(FindObjectsSortMode.None);
|
2025-09-22 12:16:32 +00:00
|
|
|
|
foreach (Bubble bubble in activeBubbles)
|
|
|
|
|
|
{
|
2025-09-24 13:33:43 +00:00
|
|
|
|
bubble.speed *= _devSettings.BubbleSurfacingSpeedFactor;
|
2025-09-22 12:16:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 13:33:43 +00:00
|
|
|
|
Debug.Log($"[BubbleSpawner] Started surfacing mode. Bubbles slowed to {_devSettings.BubbleSurfacingSpeedFactor * 100}% speed.");
|
2025-09-22 12:16:32 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-16 15:02:50 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Logs the current pool statistics for debugging
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void LogPoolStats()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_bubblePool != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_bubblePool.LogPoolStats();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-08 12:36:08 +02:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Coroutine that handles the bubble spawning logic
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
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");
|
|
|
|
|
|
}
|
2025-09-10 09:36:15 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|