using System; using System.Collections.Generic; using FlappyBird.Core; using UnityEngine; using Zenject; namespace FlappyBird.Pipes { public class PipeSpawner : IInitializable, ITickable, IDisposable { private readonly IObjectPoolManager _objectPoolManager; private readonly IGameManager _gameManager; private readonly PipeSettings _settings; private readonly GameObject _pipePrefab; private readonly List _activePipes = new List(); private bool _isRunning; private float _spawnTimer; [Inject] public PipeSpawner( IObjectPoolManager objectPoolManager, IGameManager gameManager, PipeSettings settings, [Inject(Id = "PipePrefab")] GameObject pipePrefab) { _objectPoolManager = objectPoolManager; _gameManager = gameManager; _settings = settings; _pipePrefab = pipePrefab; } /// /// Initializes the spawner by registering the Pipe pool, subscribing to game state changes, /// and setting the initial spawn timer based on settings. /// public void Initialize() { // Register the pool with the new generic ObjectPoolManager _objectPoolManager.RegisterPool(_pipePrefab, _settings.initialPoolSize); _gameManager.OnGameStateChanged += HandleGameStateChanged; ApplyState(_gameManager.CurrentState); } public void Dispose() { _gameManager.OnGameStateChanged -= HandleGameStateChanged; } public void Tick() { if (!_isRunning) { return; } MoveActivePipes(); UpdateSpawnTimer(); } private void HandleGameStateChanged(GameState state) { ApplyState(state); } private void ApplyState(GameState state) { if (state == GameState.Playing) { _isRunning = true; _spawnTimer = _settings.spawnImmediately ? 0f : _settings.spawnInterval; return; } _isRunning = false; if (state == GameState.Menu) { DespawnAllActivePipes(); } } private void UpdateSpawnTimer() { _spawnTimer -= Time.deltaTime; if (_spawnTimer > 0f) { return; } SpawnPipe(); _spawnTimer += Mathf.Max(0.01f, _settings.spawnInterval); } private void SpawnPipe() { PipeView pipeView = _objectPoolManager.Get(); // Randomize the vertical position of the gap float gapCenterY = UnityEngine.Random.Range(_settings.minGapCenterY, _settings.maxGapCenterY); pipeView.ConfigureGap(gapCenterY, _settings.gapSize, _settings.scoreTriggerWidth); pipeView.SetWorldPosition(new Vector3(_settings.spawnX, 0f, 0f)); _activePipes.Add(pipeView); } private void MoveActivePipes() { float step = _settings.moveSpeed * Time.deltaTime; for (int i = _activePipes.Count - 1; i >= 0; i--) { PipeView pipeView = _activePipes[i]; pipeView.MoveLeft(step); // Despawn if the pipe has moved past the despawn threshold if (pipeView.XPosition > _settings.despawnX) { continue; } _activePipes.RemoveAt(i); _objectPoolManager.Return(pipeView); } } private void DespawnAllActivePipes() { for (int i = _activePipes.Count - 1; i >= 0; i--) { _objectPoolManager.Return(_activePipes[i]); } _activePipes.Clear(); } } }