Update Project

This commit is contained in:
2026-03-20 13:04:43 +02:00
parent 9b587e6cba
commit beae2dea89
2295 changed files with 251259 additions and 33 deletions
+64
View File
@@ -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;
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: d023d969944224b458f29abc062d3c07
+10
View File
@@ -0,0 +1,10 @@
namespace FlappyBird.Core
{
/// <summary>All possible game states.</summary>
public enum GameState
{
Menu,
Playing,
GameOver
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: b946714168a978e49b501c41b0c81379
+45
View File
@@ -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
+16
View File
@@ -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
+22
View File
@@ -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
+13
View File
@@ -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();
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 7ea87bd7742f91e4a9550011fb8354fc
+22
View File
@@ -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
+48
View File
@@ -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);
}
}
}
+2
View File
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8cb16782bb35543409bdbf67b4f6c2d6