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
@@ -0,0 +1,83 @@
using UnityEngine;
using Zenject;
using FlappyBird.Core;
namespace FlappyBird.Gameplay.Bird
{
/// <summary>
/// Thin MonoBehaviour on the bird GameObject.
/// Responsibilities:
/// - Detect solid pipe collisions (OnCollisionEnter2D)
/// - Detect trigger-based pipe collisions (OnTriggerEnter2D, tag "Obstacle")
/// - Check vertical screen bounds every frame
/// On any death condition: calls IDeathHandler then notifies GameManagerView.
/// NO death logic lives here.
/// </summary>
[RequireComponent(typeof(Collider2D))]
public sealed class BirdCollisionView : MonoBehaviour
{
private GameManagerView _gameManagerView;
private BirdSettings _birdSettings;
private IGameStateManager _gameStateManager;
private IDeathHandler _deathHandler;
[Inject]
private void Construct(
GameManagerView gameManagerView,
BirdSettings birdSettings,
IGameStateManager gameStateManager,
IDeathHandler deathHandler)
{
_gameManagerView = gameManagerView;
_birdSettings = birdSettings;
_gameStateManager = gameStateManager;
_deathHandler = deathHandler;
}
// ── Unity lifecycle ────────────────────────────────────────────────
private void Update()
{
// Bounds check only runs while playing — prevents re-triggering death.
if (_gameStateManager.CurrentState != GameState.Playing) return;
CheckBounds();
}
private void OnCollisionEnter2D(Collision2D other)
{
Die();
}
private void OnTriggerEnter2D(Collider2D other)
{
// Ignore the score trigger that sits in the gap centre.
if (other.CompareTag("Bird")) return;
if (other.CompareTag("Obstacle"))
Die();
}
// ── Private helpers ────────────────────────────────────────────────
/// <summary>
/// Position-based ceiling/floor check.
/// No boundary collider objects are required in the scene.
/// </summary>
private void CheckBounds()
{
float y = transform.position.y;
if (y > _birdSettings.MaxY || y < _birdSettings.MinY)
Die();
}
/// <summary>
/// Single death entry-point — applies side-effects then triggers GameOver.
/// Guard inside NotifyBirdDied prevents double-firing.
/// </summary>
private void Die()
{
_deathHandler.HandleDeath();
_gameManagerView.NotifyBirdDied();
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 47e600501c07d4045a3c51d0d03b1bfc
@@ -0,0 +1,68 @@
using UnityEngine;
namespace FlappyBird.Gameplay.Bird
{
/// <summary>
/// Pure C# service — all bird physics and rotation logic lives here.
/// No MonoBehaviour, no Unity lifecycle. Injected via Zenject.
/// </summary>
public sealed class BirdController : IBirdController
{
// ── Dependencies ────────────────────────────────────────────────────
private readonly Rigidbody2D _rigidbody;
private readonly BirdSettings _settings;
/// <summary>
/// Constructor injection (Zenject will call this).
/// </summary>
/// <param name="rigidbody">The bird's Rigidbody2D component.</param>
/// <param name="settings">Configurable physics parameters.</param>
public BirdController(Rigidbody2D rigidbody, BirdSettings settings)
{
_rigidbody = rigidbody;
_settings = settings;
// Lock X position so the bird never drifts horizontally.
// The illusion of forward movement comes from pipes scrolling left.
_rigidbody.constraints = RigidbodyConstraints2D.FreezePositionX;
}
// ── IBirdController ─────────────────────────────────────────────────
/// <inheritdoc/>
public void Tick()
{
ApplyRotation();
}
/// <inheritdoc/>
public void Jump()
{
// Reset vertical velocity so each tap feels consistent regardless of fall speed.
// X is frozen via constraints, but we explicitly keep it zero for safety.
_rigidbody.linearVelocity = Vector2.zero;
_rigidbody.AddForce(Vector2.up * _settings.JumpForce, ForceMode2D.Impulse);
}
// ── Private helpers ─────────────────────────────────────────────────
/// <summary>
/// Maps vertical velocity to a tilt angle and smoothly rotates the bird.
/// Velocity range [rotationUpVelocity .. rotationDownVelocity] maps to
/// [maxUpRotation .. maxDownRotation] via an inverse-lerp → lerp.
/// </summary>
private void ApplyRotation()
{
float velocityY = _rigidbody.linearVelocity.y;
// Normalise velocity to [0, 1] (0 = falling fast, 1 = rising fast).
float t = Mathf.InverseLerp(
_settings.RotationDownVelocity,
_settings.RotationUpVelocity,
velocityY);
float targetAngle = Mathf.Lerp(
_settings.MaxDownRotation,
_settings.MaxUpRotation,
t);
// Read current Z rotation from the transform via the rigidbody.
float currentAngle = _rigidbody.rotation;
float smoothedAngle = Mathf.LerpAngle(
currentAngle,
targetAngle,
_settings.RotationSpeed * Time.deltaTime);
_rigidbody.MoveRotation(smoothedAngle);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: e7a2477aa06913f429b73104a5697ca3
@@ -0,0 +1,49 @@
using UnityEngine;
namespace FlappyBird.Gameplay.Bird
{
/// <summary>
/// ScriptableObject that holds all configurable bird physics parameters.
/// Create via: Assets → Create → FlappyBird → Bird Settings
/// </summary>
[CreateAssetMenu(fileName = "BirdSettings", menuName = "FlappyBird/Bird Settings")]
public sealed class BirdSettings : ScriptableObject
{
[Header("Jump")]
[Tooltip("Upward force applied on each tap (Impulse mode).")]
[SerializeField, Min(0f)] private float jumpForce = 5f;
[Header("Rotation")]
[Tooltip("Maximum upward tilt angle in degrees (positive = nose up).")]
[SerializeField] private float maxUpRotation = 30f;
[Tooltip("Maximum downward tilt angle in degrees (positive = nose down).")]
[SerializeField] private float maxDownRotation = -90f;
[Tooltip("Velocity at which the bird reaches full upward rotation.")]
[SerializeField] private float rotationUpVelocity = 5f;
[Tooltip("Velocity at which the bird reaches full downward rotation.")]
[SerializeField] private float rotationDownVelocity = -10f;
[Tooltip("Speed at which the rotation visually lerps to the target angle.")]
[SerializeField, Min(0f)] private float rotationSpeed = 10f;
[Header("Boundaries")]
[Tooltip("Y position of the ceiling. Bird dies if it rises above this value.")]
[SerializeField] private float maxY = 5f;
[Tooltip("Y position of the ground. Bird dies if it falls below this value.")]
[SerializeField] private float minY = -5f;
// ── Public read-only accessors ──────────────────────────────────────
public float JumpForce => jumpForce;
public float MaxUpRotation => maxUpRotation;
public float MaxDownRotation => maxDownRotation;
public float RotationUpVelocity => rotationUpVelocity;
public float RotationDownVelocity => rotationDownVelocity;
public float RotationSpeed => rotationSpeed;
public float MaxY => maxY;
public float MinY => minY;
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 895930ede4bb4906be7651fd00905f76
timeCreated: 1773995571
+56
View File
@@ -0,0 +1,56 @@
using UnityEngine;
using Zenject;
using FlappyBird.Core;
namespace FlappyBird.Gameplay.Bird
{
/// <summary>
/// Thin MonoBehaviour view for the bird.
/// Responsibilities:
/// - Hold component references (Rigidbody2D)
/// - Forward Unity lifecycle events to the service
/// - Detect input in Update and delegate to IBirdController
/// NO game logic lives here.
/// </summary>
[RequireComponent(typeof(Rigidbody2D))]
public sealed class BirdView : MonoBehaviour
{
private IBirdController _birdController;
private IGameStateManager _gameStateManager;
[Inject]
private void Construct(IBirdController birdController, IGameStateManager gameStateManager)
{
_birdController = birdController;
_gameStateManager = gameStateManager;
}
// Unity lifecycle
/// <summary>Update: ONLY input detection, no logic.</summary>
private void Update()
{
if (_gameStateManager.CurrentState != GameState.Playing) return;
if (IsJumpInputDetected())
_birdController.Jump();
}
/// <summary>FixedUpdate: physics tick forwarded to the service.</summary>
private void FixedUpdate()
{
_birdController.Tick();
}
// Private helpers
/// <summary>
/// Returns true when the player taps (touch) or clicks (mouse / Space).
/// Supports mobile touch and desktop mouse / keyboard for editor testing.
/// </summary>
private static bool IsJumpInputDetected()
{
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
return true;
if (Input.GetMouseButtonDown(0))
return true;
if (Input.GetKeyDown(KeyCode.Space))
return true;
return false;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a837c412868f0ff48af1351eb1d3ca12
@@ -0,0 +1,16 @@
namespace FlappyBird.Gameplay.Bird
{
/// <summary>
/// Contract for the bird's physics and rotation logic.
/// Implementations must NOT be MonoBehaviours.
/// </summary>
public interface IBirdController
{
/// <summary>Called once per frame to update rotation based on current velocity.</summary>
void Tick();
/// <summary>Applies a configurable upward force impulse to the bird.</summary>
void Jump();
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f8720fa707d34ebabf662d71943b80a9
timeCreated: 1773995371