127 lines
4.0 KiB
C#
127 lines
4.0 KiB
C#
using FlappyBird.Core;
|
|
using FlappyBird.Ground;
|
|
using FlappyBird.Pipes;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using Zenject;
|
|
|
|
namespace FlappyBird.Environment
|
|
{
|
|
public class GroundSpawner : IInitializable, ITickable, IDisposable
|
|
{
|
|
private readonly IObjectPoolManager _objectPoolManager;
|
|
private readonly IGameManager _gameManager;
|
|
private readonly PipeSettings _pipeSettings;
|
|
private readonly GroundSettings _groundSettings;
|
|
private readonly GameObject _groundPrefab;
|
|
|
|
private readonly List<GroundView> _activeGrounds = new List<GroundView>();
|
|
|
|
private bool _isRunning;
|
|
|
|
[Inject]
|
|
public GroundSpawner(
|
|
IObjectPoolManager objectPoolManager,
|
|
IGameManager gameManager,
|
|
PipeSettings pipeSettings,
|
|
GroundSettings groundSettings,
|
|
[Inject(Id = "GroundPrefab")] GameObject groundPrefab)
|
|
{
|
|
_objectPoolManager = objectPoolManager;
|
|
_gameManager = gameManager;
|
|
_pipeSettings = pipeSettings;
|
|
_groundSettings = groundSettings;
|
|
_groundPrefab = groundPrefab;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes the ground system by registering the object pool and
|
|
/// spawning initial segments. They will remain stationary until the game starts.
|
|
/// </summary>
|
|
public void Initialize()
|
|
{
|
|
_objectPoolManager.RegisterPool<GroundView>(_groundPrefab, _groundSettings.initialPoolSize);
|
|
|
|
_gameManager.OnGameStateChanged += HandleGameStateChanged;
|
|
|
|
// Pre-spawn segments so the floor is visible in the Menu state
|
|
float currentX = _groundSettings.spawnX;
|
|
for (int i = 0; i < Mathf.Max(1, _groundSettings.initialPoolSize); i++)
|
|
{
|
|
SpawnGround(currentX);
|
|
currentX += _groundSettings.groundWidth;
|
|
}
|
|
|
|
// Sets initial movement state based on current GameManager state (Menu = stopped)
|
|
ApplyState(_gameManager.CurrentState);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_gameManager.OnGameStateChanged -= HandleGameStateChanged;
|
|
}
|
|
|
|
public void Tick()
|
|
{
|
|
if (!_isRunning)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MoveActiveGrounds();
|
|
}
|
|
|
|
private void HandleGameStateChanged(GameState state)
|
|
{
|
|
ApplyState(state);
|
|
}
|
|
|
|
private void ApplyState(GameState state)
|
|
{
|
|
// Ground only moves during active gameplay
|
|
_isRunning = state == GameState.Playing;
|
|
}
|
|
|
|
private void MoveActiveGrounds()
|
|
{
|
|
float step = _pipeSettings.moveSpeed * Time.deltaTime;
|
|
float rightmostX = GetRightmostGroundX();
|
|
|
|
for (int i = _activeGrounds.Count - 1; i >= 0; i--)
|
|
{
|
|
GroundView groundView = _activeGrounds[i];
|
|
groundView.MoveLeft(step);
|
|
|
|
if (groundView.XPosition > _groundSettings.despawnX)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
_activeGrounds.RemoveAt(i);
|
|
_objectPoolManager.Return(groundView);
|
|
|
|
// Re-spawn at the end of the chain
|
|
SpawnGround(rightmostX + _groundSettings.groundWidth - step);
|
|
rightmostX += _groundSettings.groundWidth;
|
|
}
|
|
}
|
|
|
|
private float GetRightmostGroundX()
|
|
{
|
|
float maxX = float.MinValue;
|
|
foreach (var ground in _activeGrounds)
|
|
{
|
|
if (ground.XPosition > maxX) maxX = ground.XPosition;
|
|
}
|
|
return maxX == float.MinValue ? _groundSettings.spawnX : maxX;
|
|
}
|
|
|
|
private void SpawnGround(float xPosition)
|
|
{
|
|
GroundView groundView = _objectPoolManager.Get<GroundView>();
|
|
groundView.SetPosition(new Vector3(xPosition, _groundSettings.yPosition, 0f));
|
|
_activeGrounds.Add(groundView);
|
|
}
|
|
}
|
|
} |