using Core;
using Core.Lifecycle;
using Core.Settings;
using UnityEngine;
namespace Minigames.BirdPooper
{
///
/// Bird player controller with Flappy Bird-style flight mechanics.
/// Responds to tap input to flap, with manual gravity simulation.
///
public class BirdPlayerController : ManagedBehaviour, ITouchInputConsumer
{
[Header("Events")]
public UnityEngine.Events.UnityEvent OnFlap;
private Rigidbody2D rb;
private IBirdPooperSettings settings;
private float verticalVelocity = 0f;
private bool isDead = false;
private float fixedXPosition; // Store the initial X position from the scene
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Initialize event
if (OnFlap == null)
OnFlap = new UnityEngine.Events.UnityEvent();
// Load settings
settings = GameManager.GetSettingsObject();
if (settings == null)
{
Debug.LogError("[BirdPlayerController] BirdPooperSettings not found!");
return;
}
// Get Rigidbody2D component (Dynamic with gravityScale = 0)
rb = GetComponent();
if (rb != null)
{
rb.gravityScale = 0f; // Disable Unity physics gravity
rb.bodyType = RigidbodyType2D.Dynamic;
// Store the initial X position from the scene
fixedXPosition = rb.position.x;
Debug.Log($"[BirdPlayerController] Fixed X position set to: {fixedXPosition}");
}
else
{
Debug.LogError("[BirdPlayerController] Rigidbody2D component not found!");
return;
}
// Register as override consumer to capture ALL input (except UI button)
// Register as override consumer to capture ALL input (except UI button)
if (Input.InputManager.Instance != null)
{
Input.InputManager.Instance.RegisterOverrideConsumer(this);
Debug.Log("[BirdPlayerController] Registered as override input consumer");
}
else
{
Debug.LogError("[BirdPlayerController] InputManager instance not found!");
}
}
private void Update()
{
if (!isDead && settings != null && rb != null)
{
// Apply manual gravity
verticalVelocity -= settings.Gravity * Time.deltaTime;
// Cap fall speed (terminal velocity)
if (verticalVelocity < -settings.MaxFallSpeed)
verticalVelocity = -settings.MaxFallSpeed;
// Update position manually
Vector2 newPosition = rb.position;
newPosition.y += verticalVelocity * Time.deltaTime;
newPosition.x = fixedXPosition; // Keep X fixed at scene-configured position
// Clamp Y position to bounds
newPosition.y = Mathf.Clamp(newPosition.y, settings.MinY, settings.MaxY);
rb.MovePosition(newPosition);
// Update rotation based on velocity
UpdateRotation();
}
}
private void Destroy()
{
// Unregister when destroyed
if (Input.InputManager.Instance != null)
{
Input.InputManager.Instance.UnregisterOverrideConsumer(this);
Debug.Log("[BirdPlayerController] Unregistered override input consumer");
}
}
#region ITouchInputConsumer Implementation
public void OnTap(Vector2 tapPosition)
{
if (!isDead && settings != null)
{
verticalVelocity = settings.FlapForce;
Debug.Log($"[BirdPlayerController] Flap! velocity = {verticalVelocity}");
// Emit flap event
OnFlap?.Invoke();
}
}
public void OnHoldStart(Vector2 position) { }
public void OnHoldMove(Vector2 position) { }
public void OnHoldEnd(Vector2 position) { }
#endregion
#region Rotation
///
/// Updates the bird's rotation based on vertical velocity.
/// Bird tilts up when flapping, down when falling.
///
private void UpdateRotation()
{
if (settings == null) return;
// Map velocity to rotation angle
// When falling at max speed (-MaxFallSpeed): -MaxRotationAngle (down)
// When at flap velocity (+FlapForce): +MaxRotationAngle (up)
float velocityPercent = Mathf.InverseLerp(
-settings.MaxFallSpeed,
settings.FlapForce,
verticalVelocity
);
float targetAngle = Mathf.Lerp(
-settings.MaxRotationAngle,
settings.MaxRotationAngle,
velocityPercent
);
// Get current angle (handle 0-360 wrapping to -180-180)
float currentAngle = transform.rotation.eulerAngles.z;
if (currentAngle > 180f)
currentAngle -= 360f;
// Smooth interpolation to target
float smoothedAngle = Mathf.Lerp(
currentAngle,
targetAngle,
settings.RotationSpeed * Time.deltaTime
);
// Apply rotation to Z axis only (2D rotation)
transform.rotation = Quaternion.Euler(0, 0, smoothedAngle);
}
#endregion
#region Collision Detection
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.CompareTag("Obstacle"))
{
HandleDeath();
}
}
private void HandleDeath()
{
isDead = true;
verticalVelocity = 0f;
Debug.Log("[BirdPlayerController] Bird died!");
}
#endregion
#region Public Properties
public bool IsDead => isDead;
#endregion
}
}