Add a pausable interface and make minigameobjecs pausable
This commit is contained in:
@@ -4,6 +4,7 @@ using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using Pooling;
|
||||
using AppleHills.Core.Settings;
|
||||
using AppleHills.Core.Interfaces;
|
||||
|
||||
namespace Minigames.DivingForPictures
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace Minigames.DivingForPictures
|
||||
/// Spawns and manages mobile obstacles for the diving minigame.
|
||||
/// Uses object pooling and validates spawn positions to avoid colliding with tiles.
|
||||
/// </summary>
|
||||
public class ObstacleSpawner : MonoBehaviour
|
||||
public class ObstacleSpawner : MonoBehaviour, IPausable
|
||||
{
|
||||
[Header("Obstacle Prefabs")]
|
||||
[Tooltip("List of possible obstacle prefabs to spawn")]
|
||||
@@ -34,11 +35,19 @@ namespace Minigames.DivingForPictures
|
||||
private float _screenBottom;
|
||||
private float _spawnRangeX;
|
||||
private Coroutine _spawnCoroutine;
|
||||
private Coroutine _moveCoroutine;
|
||||
private Coroutine _despawnCoroutine;
|
||||
private readonly List<GameObject> _activeObstacles = new List<GameObject>();
|
||||
private int _obstacleCounter = 0; // Counter for unique obstacle naming
|
||||
private bool _isSurfacing = false; // Flag to track surfacing state
|
||||
private float _velocityFactor = 1.0f; // Current velocity factor from the game manager
|
||||
|
||||
// Pause state
|
||||
private bool _isPaused = false;
|
||||
|
||||
// IPausable implementation
|
||||
public bool IsPaused => _isPaused;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
@@ -75,14 +84,15 @@ namespace Minigames.DivingForPictures
|
||||
|
||||
private void Start()
|
||||
{
|
||||
|
||||
|
||||
// Find DivingGameManager and subscribe to its initialization event
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
{
|
||||
gameManager.OnGameInitialized += Initialize;
|
||||
|
||||
// Register with the DivingGameManager for pause/resume events
|
||||
gameManager.RegisterPausableComponent(this);
|
||||
|
||||
// If game is already initialized, initialize immediately
|
||||
if (gameManager.GetType().GetField("_isGameInitialized",
|
||||
System.Reflection.BindingFlags.NonPublic |
|
||||
@@ -97,20 +107,130 @@ namespace Minigames.DivingForPictures
|
||||
Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
// Unregister from DivingGameManager
|
||||
DivingGameManager gameManager = FindFirstObjectByType<DivingGameManager>();
|
||||
if (gameManager != null)
|
||||
{
|
||||
gameManager.UnregisterPausableComponent(this);
|
||||
}
|
||||
|
||||
// Clean up any active coroutines
|
||||
StopAllCoroutines();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the obstacle spawner when triggered by DivingGameManager
|
||||
/// </summary>
|
||||
private void Initialize()
|
||||
{
|
||||
// Calculate screen bounds
|
||||
CalculateScreenBounds();
|
||||
StartSpawning();
|
||||
|
||||
// Start coroutines if not paused
|
||||
StartSpawnCoroutine();
|
||||
StartMoveCoroutine();
|
||||
StartDespawnCoroutine();
|
||||
|
||||
Debug.Log("[ObstacleSpawner] Initialized");
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
|
||||
/// <summary>
|
||||
/// Pauses the spawner and all associated processes
|
||||
/// </summary>
|
||||
public void Pause()
|
||||
{
|
||||
StopSpawning();
|
||||
if (_isPaused) return; // Already paused
|
||||
|
||||
_isPaused = true;
|
||||
|
||||
// Stop spawning coroutine
|
||||
if (_spawnCoroutine != null)
|
||||
{
|
||||
StopCoroutine(_spawnCoroutine);
|
||||
_spawnCoroutine = null;
|
||||
}
|
||||
|
||||
// Pause all active obstacles
|
||||
foreach (var obstacle in _activeObstacles.ToArray())
|
||||
{
|
||||
if (obstacle != null)
|
||||
{
|
||||
FloatingObstacle obstacleComponent = obstacle.GetComponent<FloatingObstacle>();
|
||||
if (obstacleComponent != null)
|
||||
{
|
||||
obstacleComponent.Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[ObstacleSpawner] Paused with {_activeObstacles.Count} active obstacles");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resumes the spawner and all associated processes
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
{
|
||||
if (!_isPaused) return; // Already running
|
||||
|
||||
_isPaused = false;
|
||||
|
||||
// Restart spawning coroutine if not in surfacing mode
|
||||
if (!_isSurfacing)
|
||||
{
|
||||
StartSpawnCoroutine();
|
||||
}
|
||||
|
||||
// Resume all active obstacles
|
||||
foreach (var obstacle in _activeObstacles.ToArray())
|
||||
{
|
||||
if (obstacle != null)
|
||||
{
|
||||
FloatingObstacle obstacleComponent = obstacle.GetComponent<FloatingObstacle>();
|
||||
if (obstacleComponent != null)
|
||||
{
|
||||
obstacleComponent.Resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[ObstacleSpawner] Resumed with {_activeObstacles.Count} active obstacles");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the obstacle spawning coroutine if not already running
|
||||
/// </summary>
|
||||
private void StartSpawnCoroutine()
|
||||
{
|
||||
if (_spawnCoroutine == null && !_isPaused && !_isSurfacing)
|
||||
{
|
||||
_spawnCoroutine = StartCoroutine(SpawnObstacleRoutine());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the obstacle movement coroutine if not already running
|
||||
/// </summary>
|
||||
private void StartMoveCoroutine()
|
||||
{
|
||||
if (_moveCoroutine == null && !_isPaused)
|
||||
{
|
||||
_moveCoroutine = StartCoroutine(MoveObstaclesRoutine());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the obstacle despawning coroutine if not already running
|
||||
/// </summary>
|
||||
private void StartDespawnCoroutine()
|
||||
{
|
||||
if (_despawnCoroutine == null && !_isPaused)
|
||||
{
|
||||
_despawnCoroutine = StartCoroutine(DespawnObstaclesRoutine());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -444,8 +564,14 @@ namespace Minigames.DivingForPictures
|
||||
_settings.ObstacleMinMoveSpeed,
|
||||
_settings.ObstacleMaxMoveSpeed);
|
||||
|
||||
// Set spawner reference (since FloatingObstacle has this built-in now)
|
||||
// Set spawner reference
|
||||
obstacleComponent.SetSpawner(this);
|
||||
|
||||
// If spawner is already paused, pause the obstacle immediately
|
||||
if (_isPaused)
|
||||
{
|
||||
obstacleComponent.Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,6 +685,147 @@ namespace Minigames.DivingForPictures
|
||||
/// </summary>
|
||||
public int ActiveObstacleCount => _activeObstacles.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles obstacle spawning at regular intervals
|
||||
/// </summary>
|
||||
private IEnumerator SpawnObstacleRoutine()
|
||||
{
|
||||
Debug.Log("[ObstacleSpawner] Started spawning coroutine");
|
||||
|
||||
while (enabled && gameObject.activeInHierarchy && !_isPaused && !_isSurfacing)
|
||||
{
|
||||
// Calculate next spawn time with variation
|
||||
float nextSpawnTime = _settings.ObstacleSpawnInterval +
|
||||
Random.Range(-_settings.ObstacleSpawnIntervalVariation,
|
||||
_settings.ObstacleSpawnIntervalVariation);
|
||||
nextSpawnTime = Mathf.Max(0.1f, nextSpawnTime); // Ensure minimum interval
|
||||
|
||||
// Attempt to spawn an obstacle
|
||||
TrySpawnObstacle();
|
||||
|
||||
yield return new WaitForSeconds(nextSpawnTime);
|
||||
}
|
||||
|
||||
// Clear coroutine reference when stopped
|
||||
_spawnCoroutine = null;
|
||||
|
||||
Debug.Log("[ObstacleSpawner] Spawning coroutine ended");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that handles checking obstacle positions
|
||||
/// Unlike the previous implementation, we don't need to move obstacles manually
|
||||
/// since the FloatingObstacle handles its own movement via coroutines
|
||||
/// </summary>
|
||||
private IEnumerator MoveObstaclesRoutine()
|
||||
{
|
||||
Debug.Log("[ObstacleSpawner] Started obstacle monitoring coroutine");
|
||||
|
||||
// This coroutine now just monitors obstacles, not moves them
|
||||
while (enabled && gameObject.activeInHierarchy && !_isPaused)
|
||||
{
|
||||
// Clean up any null references in the active obstacles list
|
||||
_activeObstacles.RemoveAll(obstacle => obstacle == null);
|
||||
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
|
||||
// Clear coroutine reference when stopped
|
||||
_moveCoroutine = null;
|
||||
|
||||
Debug.Log("[ObstacleSpawner] Obstacle monitoring coroutine ended");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coroutine that checks for obstacles that are off-screen and should be despawned
|
||||
/// </summary>
|
||||
private IEnumerator DespawnObstaclesRoutine()
|
||||
{
|
||||
const float checkInterval = 0.5f; // Check every half second
|
||||
Debug.Log("[ObstacleSpawner] Started despawn coroutine with interval: " + checkInterval);
|
||||
|
||||
while (enabled && gameObject.activeInHierarchy && !_isPaused)
|
||||
{
|
||||
// Calculate screen bounds for despawning
|
||||
float despawnBuffer = 2f; // Extra buffer beyond screen edges
|
||||
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
_mainCamera = Camera.main;
|
||||
if (_mainCamera == null)
|
||||
{
|
||||
yield return new WaitForSeconds(checkInterval);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 topWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 1f, _mainCamera.transform.position.z));
|
||||
float _screenTop = topWorldPoint.y;
|
||||
|
||||
Vector3 bottomWorldPoint = _mainCamera.ViewportToWorldPoint(new Vector3(0.5f, 0f, _mainCamera.transform.position.z));
|
||||
float _screenBottom = bottomWorldPoint.y;
|
||||
|
||||
float topEdge = _screenTop + despawnBuffer;
|
||||
float bottomEdge = _screenBottom - despawnBuffer;
|
||||
|
||||
// Find obstacles that need to be despawned
|
||||
List<GameObject> obstaclesToRemove = new List<GameObject>();
|
||||
|
||||
foreach (var obstacle in _activeObstacles)
|
||||
{
|
||||
if (obstacle == null)
|
||||
{
|
||||
obstaclesToRemove.Add(obstacle);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if obstacle is out of screen bounds
|
||||
float obstacleY = obstacle.transform.position.y;
|
||||
|
||||
bool shouldDespawn;
|
||||
if (_isSurfacing)
|
||||
{
|
||||
// When surfacing, despawn obstacles below the bottom edge
|
||||
shouldDespawn = obstacleY < bottomEdge;
|
||||
}
|
||||
else
|
||||
{
|
||||
// When descending, despawn obstacles above the top edge
|
||||
shouldDespawn = obstacleY > topEdge;
|
||||
}
|
||||
|
||||
if (shouldDespawn)
|
||||
{
|
||||
obstaclesToRemove.Add(obstacle);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove and despawn obstacles
|
||||
foreach (var obstacle in obstaclesToRemove)
|
||||
{
|
||||
FloatingObstacle obstacleComponent = obstacle?.GetComponent<FloatingObstacle>();
|
||||
if (obstacleComponent != null)
|
||||
{
|
||||
// Instead of calling a non-existent DespawnObstacle method,
|
||||
// use FloatingObstacle's ForceReturnToPool method
|
||||
obstacleComponent.ForceReturnToPool();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fallback if component not found
|
||||
ReturnObstacleToPool(obstacle, -1);
|
||||
}
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(checkInterval);
|
||||
}
|
||||
|
||||
// Clear coroutine reference when stopped
|
||||
_despawnCoroutine = null;
|
||||
|
||||
Debug.Log("[ObstacleSpawner] Despawn coroutine ended");
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user