Update Project
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
using UnityEngine;
|
||||
using Zenject;
|
||||
using FlappyBird.Gameplay.Pipes;
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Pure C# service — owns all death side-effects.
|
||||
/// Subscribes to IGameStateManager.OnStateChanged at construction time
|
||||
/// and reacts when the state becomes GameOver.
|
||||
///
|
||||
/// Side-effects on death:
|
||||
/// 1. Freeze the bird Rigidbody2D (velocity + gravity)
|
||||
/// 2. Reset the pipe spawner timer so pipes start cleanly on restart
|
||||
/// </summary>
|
||||
public sealed class DeathHandler : IDeathHandler, IInitializable
|
||||
{
|
||||
private readonly Rigidbody2D _birdRigidbody;
|
||||
private readonly IGameStateManager _gameStateManager;
|
||||
private readonly IPipeSpawner _pipeSpawner;
|
||||
public DeathHandler(
|
||||
Rigidbody2D birdRigidbody,
|
||||
IGameStateManager gameStateManager,
|
||||
IPipeSpawner pipeSpawner)
|
||||
{
|
||||
_birdRigidbody = birdRigidbody;
|
||||
_gameStateManager = gameStateManager;
|
||||
_pipeSpawner = pipeSpawner;
|
||||
}
|
||||
// ── IInitializable ─────────────────────────────────────────────────
|
||||
public void Initialize()
|
||||
{
|
||||
_gameStateManager.OnStateChanged += OnStateChanged;
|
||||
}
|
||||
// ── IDeathHandler ──────────────────────────────────────────────────
|
||||
/// <inheritdoc/>
|
||||
public void HandleDeath()
|
||||
{
|
||||
FreezeBird();
|
||||
_pipeSpawner.ResetTimer();
|
||||
}
|
||||
// ── Private helpers ────────────────────────────────────────────────
|
||||
private void OnStateChanged(GameState newState)
|
||||
{
|
||||
if (newState == GameState.Playing)
|
||||
UnfreezeBird();
|
||||
}
|
||||
/// <summary>
|
||||
/// Kills velocity and disables gravity so the bird hangs in place
|
||||
/// during the GameOver freeze (Time.timeScale is 0, but we set this
|
||||
/// explicitly so the bird does not drop when timeScale resumes on restart).
|
||||
/// </summary>
|
||||
private void FreezeBird()
|
||||
{
|
||||
_birdRigidbody.linearVelocity = Vector2.zero;
|
||||
_birdRigidbody.angularVelocity = 0f;
|
||||
_birdRigidbody.gravityScale = 0f;
|
||||
}
|
||||
/// <summary>Restore gravity when a new game begins.</summary>
|
||||
private void UnfreezeBird()
|
||||
{
|
||||
_birdRigidbody.gravityScale = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d023d969944224b458f29abc062d3c07
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
/// <summary>All possible game states.</summary>
|
||||
public enum GameState
|
||||
{
|
||||
Menu,
|
||||
Playing,
|
||||
GameOver
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b946714168a978e49b501c41b0c81379
|
||||
@@ -0,0 +1,45 @@
|
||||
using UnityEngine;
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Pure C# service — central authority for all game-state transitions.
|
||||
/// No MonoBehaviour, no Unity lifecycle. Injected via Zenject.
|
||||
///
|
||||
/// Transition side-effects:
|
||||
/// Playing -> Time.timeScale = 1 (unfreeze)
|
||||
/// GameOver -> Time.timeScale = 0 (freeze everything)
|
||||
/// Menu -> Time.timeScale = 0 (frozen until game starts)
|
||||
/// </summary>
|
||||
public sealed class GameStateManager : IGameStateManager
|
||||
{
|
||||
// ── IGameStateManager ──────────────────────────────────────────────
|
||||
/// <inheritdoc/>
|
||||
public GameState CurrentState { get; private set; } = GameState.Menu;
|
||||
/// <inheritdoc/>
|
||||
public event System.Action<GameState> OnStateChanged;
|
||||
// ── Constructor ────────────────────────────────────────────────────
|
||||
public GameStateManager()
|
||||
{
|
||||
// Start frozen; gameplay begins only when ChangeState(Playing) is called.
|
||||
Time.timeScale = 0f;
|
||||
}
|
||||
// ── IGameStateManager ──────────────────────────────────────────────
|
||||
/// <inheritdoc/>
|
||||
public void ChangeState(GameState newState)
|
||||
{
|
||||
if (CurrentState == newState) return;
|
||||
CurrentState = newState;
|
||||
ApplyTimeScale(newState);
|
||||
OnStateChanged?.Invoke(newState);
|
||||
}
|
||||
// ── Private helpers ────────────────────────────────────────────────
|
||||
/// <summary>
|
||||
/// Only Playing runs at normal speed.
|
||||
/// Menu and GameOver both freeze Time so no physics or Update logic runs.
|
||||
/// </summary>
|
||||
private static void ApplyTimeScale(GameState state)
|
||||
{
|
||||
Time.timeScale = state == GameState.Playing ? 1f : 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 05899132f4565a94590c4e2a093e54ba
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Contract for the death-handling service.
|
||||
/// Implementations react to a GameOver state transition and apply
|
||||
/// all necessary side-effects (freeze bird, clear pipes, etc.).
|
||||
/// </summary>
|
||||
public interface IDeathHandler
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes all death side-effects.
|
||||
/// Called by the subscriber wired up in the installer.
|
||||
/// </summary>
|
||||
void HandleDeath();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d145c7d22d47c16479e3be770cfc1cbe
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Contract for the central game-state service.
|
||||
/// Matches the interface defined in AI_RULES.md.
|
||||
/// </summary>
|
||||
public interface IGameStateManager
|
||||
{
|
||||
/// <summary>The currently active game state.</summary>
|
||||
GameState CurrentState { get; }
|
||||
/// <summary>
|
||||
/// Transitions to <paramref name="newState"/>.
|
||||
/// Fires <see cref="OnStateChanged"/> after the transition completes.
|
||||
/// </summary>
|
||||
void ChangeState(GameState newState);
|
||||
/// <summary>
|
||||
/// Fired every time the game state changes.
|
||||
/// Passes the new <see cref="GameState"/> value.
|
||||
/// </summary>
|
||||
event System.Action<GameState> OnStateChanged;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4002324bd9e6d054ea59da95339ab344
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Implement on any Component that is managed by the object pool.
|
||||
/// </summary>
|
||||
public interface IPoolable
|
||||
{
|
||||
/// <summary>Called by the pool just before the object is handed out.</summary>
|
||||
void OnSpawn();
|
||||
/// <summary>Called by the pool just after the object is returned.</summary>
|
||||
void OnDespawn();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ea87bd7742f91e4a9550011fb8354fc
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Contract for the score management service.
|
||||
/// Matches the interface defined in AI_RULES.md.
|
||||
/// </summary>
|
||||
public interface IScoreManager
|
||||
{
|
||||
/// <summary>Current score for this session.</summary>
|
||||
int CurrentScore { get; }
|
||||
/// <summary>All-time high score (persisted via PlayerPrefs).</summary>
|
||||
int HighScore { get; }
|
||||
/// <summary>Adds <paramref name="points"/> to the current score.</summary>
|
||||
void AddScore(int points);
|
||||
/// <summary>Resets the current score to zero (call on game restart).</summary>
|
||||
void ResetScore();
|
||||
/// <summary>Fired whenever the current score changes. Passes the new score value.</summary>
|
||||
event System.Action<int> OnScoreChanged;
|
||||
/// <summary>Fired when a new high score is set. Passes the new high score value.</summary>
|
||||
event System.Action<int> OnHighScoreBeaten;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c6a5d584113c9e644892894fabe9970b
|
||||
@@ -0,0 +1,48 @@
|
||||
using UnityEngine;
|
||||
namespace FlappyBird.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Pure C# service — all scoring logic lives here.
|
||||
/// No MonoBehaviour, no Unity lifecycle. Injected via Zenject.
|
||||
/// High score is persisted with PlayerPrefs.
|
||||
/// </summary>
|
||||
public sealed class ScoreManager : IScoreManager
|
||||
{
|
||||
private const string HIGH_SCORE_KEY = "HighScore";
|
||||
// ── IScoreManager ──────────────────────────────────────────────────
|
||||
/// <inheritdoc/>
|
||||
public int CurrentScore { get; private set; }
|
||||
/// <inheritdoc/>
|
||||
public int HighScore { get; private set; }
|
||||
/// <inheritdoc/>
|
||||
public event System.Action<int> OnScoreChanged;
|
||||
/// <inheritdoc/>
|
||||
public event System.Action<int> OnHighScoreBeaten;
|
||||
// ── Constructor ────────────────────────────────────────────────────
|
||||
public ScoreManager()
|
||||
{
|
||||
HighScore = PlayerPrefs.GetInt(HIGH_SCORE_KEY, 0);
|
||||
}
|
||||
// ── IScoreManager ──────────────────────────────────────────────────
|
||||
/// <inheritdoc/>
|
||||
public void AddScore(int points)
|
||||
{
|
||||
if (points <= 0) return;
|
||||
CurrentScore += points;
|
||||
OnScoreChanged?.Invoke(CurrentScore);
|
||||
if (CurrentScore > HighScore)
|
||||
{
|
||||
HighScore = CurrentScore;
|
||||
PlayerPrefs.SetInt(HIGH_SCORE_KEY, HighScore);
|
||||
PlayerPrefs.Save();
|
||||
OnHighScoreBeaten?.Invoke(HighScore);
|
||||
}
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void ResetScore()
|
||||
{
|
||||
CurrentScore = 0;
|
||||
OnScoreChanged?.Invoke(CurrentScore);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8cb16782bb35543409bdbf67b4f6c2d6
|
||||
Reference in New Issue
Block a user