diff --git a/Assets/Scripts/Minigames/BirdPooper/BirdPlayerController.cs b/Assets/Scripts/Minigames/BirdPooper/BirdPlayerController.cs
index 1b36be50..17654328 100644
--- a/Assets/Scripts/Minigames/BirdPooper/BirdPlayerController.cs
+++ b/Assets/Scripts/Minigames/BirdPooper/BirdPlayerController.cs
@@ -15,12 +15,22 @@ namespace Minigames.BirdPooper
public UnityEngine.Events.UnityEvent OnFlap;
public UnityEngine.Events.UnityEvent OnPlayerDamaged;
+ [Header("Flap Cooldown")]
+ [Tooltip("Minimum seconds between flaps to prevent multi-tap issues on mobile")]
+ [SerializeField] private float flapCooldown = 0.15f;
+
private Rigidbody2D _rb;
private IBirdPooperSettings _settings;
private float _verticalVelocity;
private bool _isDead;
private float _fixedXPosition; // Store the initial X position from the scene
private bool _isInitialized; // Flag to control when physics/input are active
+ private float _lastFlapTime = -Mathf.Infinity; // Track last flap time for cooldown
+
+ // Diagnostic tracking
+ private int _updateFrameCount = 0;
+ private float _maxDeltaTime = 0f;
+ private float _minDeltaTime = float.MaxValue;
internal override void OnManagedAwake()
{
@@ -86,17 +96,44 @@ namespace Minigames.BirdPooper
}
_isInitialized = true;
- Debug.Log($"[BirdPlayerController] Initialized! Fixed X position: {_fixedXPosition}");
+
+ // DIAGNOSTIC: Log all critical values that might differ between platforms
+ Debug.Log($"[BirdPlayerController] ===== INITIALIZATION DIAGNOSTICS =====");
+ Debug.Log($"[BirdPlayerController] Fixed X position: {_fixedXPosition}");
+ Debug.Log($"[BirdPlayerController] Time.timeScale: {Time.timeScale}");
+ Debug.Log($"[BirdPlayerController] Application.targetFrameRate: {Application.targetFrameRate}");
+ Debug.Log($"[BirdPlayerController] Settings.Gravity: {_settings.Gravity}");
+ Debug.Log($"[BirdPlayerController] Settings.FlapForce: {_settings.FlapForce}");
+ Debug.Log($"[BirdPlayerController] Settings.MaxFallSpeed: {_settings.MaxFallSpeed}");
+ Debug.Log($"[BirdPlayerController] Settings.MinY: {_settings.MinY}");
+ Debug.Log($"[BirdPlayerController] Settings.MaxY: {_settings.MaxY}");
+ Debug.Log($"[BirdPlayerController] ==========================================");
}
- private void Update()
+ ///
+ /// Using FixedUpdate for physics to ensure frame-rate independence.
+ /// FixedUpdate runs at a fixed timestep (default 0.02s = 50fps) regardless of actual frame rate.
+ ///
+ private void FixedUpdate()
{
// Only run physics/movement if initialized
if (!_isInitialized || _isDead || _settings == null || _rb == null)
return;
- // Apply manual gravity
- _verticalVelocity -= _settings.Gravity * Time.deltaTime;
+ // DIAGNOSTIC: Track fixedDeltaTime (should be consistent)
+ _updateFrameCount++;
+ float dt = Time.fixedDeltaTime;
+ if (dt > _maxDeltaTime) _maxDeltaTime = dt;
+ if (dt < _minDeltaTime) _minDeltaTime = dt;
+
+ // Log diagnostics every 50 fixed frames (every second at 50fps fixed timestep)
+ if (_updateFrameCount % 50 == 0)
+ {
+ Debug.Log($"[BirdPlayerController] FixedFrame {_updateFrameCount}: fixedDeltaTime={dt:F4}, min={_minDeltaTime:F4}, max={_maxDeltaTime:F4}, velocity={_verticalVelocity:F2}, pos.y={_rb.position.y:F2}");
+ }
+
+ // Apply manual gravity using fixedDeltaTime for consistent physics
+ _verticalVelocity -= _settings.Gravity * dt;
// Cap fall speed (terminal velocity)
if (_verticalVelocity < -_settings.MaxFallSpeed)
@@ -104,15 +141,26 @@ namespace Minigames.BirdPooper
// Update position manually
Vector2 newPosition = _rb.position;
- newPosition.y += _verticalVelocity * Time.deltaTime;
+ newPosition.y += _verticalVelocity * dt;
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 runs at actual frame rate for smooth visual updates like rotation.
+ /// Physics calculations moved to FixedUpdate for frame-rate independence.
+ ///
+ private void Update()
+ {
+ // Only update visuals if initialized
+ if (!_isInitialized || _isDead || _settings == null)
+ return;
- // Update rotation based on velocity
+ // Update rotation based on velocity (visual only, runs at frame rate for smoothness)
UpdateRotation();
}
@@ -138,14 +186,25 @@ namespace Minigames.BirdPooper
///
/// Makes the bird flap, applying upward velocity.
/// Can be called by input system or externally (e.g., for first tap).
+ /// Includes cooldown to prevent multi-tap issues on mobile devices.
///
public void Flap()
{
if (!_isInitialized || _isDead || _settings == null)
return;
+ // Cooldown check to prevent multi-tap issues (especially on mobile touchscreens)
+ if (Time.time < _lastFlapTime + flapCooldown)
+ {
+ Debug.Log($"[BirdPlayerController] Flap rejected - on cooldown ({Time.time - _lastFlapTime:F3}s since last flap)");
+ return;
+ }
+
_verticalVelocity = _settings.FlapForce;
- Debug.Log($"[BirdPlayerController] Flap! velocity = {_verticalVelocity}");
+ _lastFlapTime = Time.time;
+
+ // DIAGNOSTIC: Log flap details including time values
+ Debug.Log($"[BirdPlayerController] FLAP! velocity={_verticalVelocity}, Time.time={Time.time}, Time.deltaTime={Time.deltaTime}, Time.timeScale={Time.timeScale}");
// Emit flap event
OnFlap?.Invoke();
diff --git a/Assets/Scripts/Minigames/BirdPooper/BirdPooperDebugDisplay.cs b/Assets/Scripts/Minigames/BirdPooper/BirdPooperDebugDisplay.cs
new file mode 100644
index 00000000..0f6f8c31
--- /dev/null
+++ b/Assets/Scripts/Minigames/BirdPooper/BirdPooperDebugDisplay.cs
@@ -0,0 +1,137 @@
+using UnityEngine;
+using TMPro;
+
+namespace Minigames.BirdPooper
+{
+ ///
+ /// On-screen debug display for Bird Pooper minigame diagnostics.
+ /// Shows critical values that might differ between PC and mobile platforms.
+ ///
+ public class BirdPooperDebugDisplay : MonoBehaviour
+ {
+ [Header("References")]
+ [SerializeField] private BirdPlayerController birdController;
+ [SerializeField] private TextMeshProUGUI debugText;
+
+ [Header("Settings")]
+ [SerializeField] private bool showDebugDisplay = true;
+ [SerializeField] private float updateInterval = 0.1f; // Update display every 0.1 seconds
+
+ private float _nextUpdateTime;
+ private int _frameCount;
+ private float _minDeltaTime = float.MaxValue;
+ private float _maxDeltaTime = 0f;
+ private float _avgDeltaTime;
+ private float _deltaTimeSum;
+
+ private void Start()
+ {
+ if (debugText == null)
+ {
+ Debug.LogError("[BirdPooperDebugDisplay] Debug text reference not assigned!");
+ enabled = false;
+ return;
+ }
+
+ if (birdController == null)
+ {
+ birdController = FindFirstObjectByType();
+ if (birdController == null)
+ {
+ Debug.LogError("[BirdPooperDebugDisplay] Could not find BirdPlayerController!");
+ enabled = false;
+ return;
+ }
+ }
+
+ debugText.gameObject.SetActive(showDebugDisplay);
+ _nextUpdateTime = Time.time + updateInterval;
+ }
+
+ private void Update()
+ {
+ if (!showDebugDisplay || debugText == null)
+ return;
+
+ // Track deltaTime statistics
+ _frameCount++;
+ _deltaTimeSum += Time.deltaTime;
+ if (Time.deltaTime < _minDeltaTime) _minDeltaTime = Time.deltaTime;
+ if (Time.deltaTime > _maxDeltaTime) _maxDeltaTime = Time.deltaTime;
+ _avgDeltaTime = _deltaTimeSum / _frameCount;
+
+ // Update display at specified interval
+ if (Time.time >= _nextUpdateTime)
+ {
+ UpdateDebugDisplay();
+ _nextUpdateTime = Time.time + updateInterval;
+ }
+ }
+
+ private void UpdateDebugDisplay()
+ {
+ if (debugText == null) return;
+
+ // Get settings from the bird controller via reflection (hacky but works for debug)
+ var settingsField = typeof(BirdPlayerController).GetField("_settings",
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ var settings = settingsField?.GetValue(birdController) as Core.Settings.IBirdPooperSettings;
+
+ var velocityField = typeof(BirdPlayerController).GetField("_verticalVelocity",
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ float velocity = velocityField != null ? (float)velocityField.GetValue(birdController) : 0f;
+
+ var rbField = typeof(BirdPlayerController).GetField("_rb",
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ var rb = rbField?.GetValue(birdController) as Rigidbody2D;
+
+ // Calculate actual FPS
+ float fps = _avgDeltaTime > 0 ? 1f / _avgDeltaTime : 0f;
+
+ // Build debug string
+ string debugInfo = "BIRD POOPER DEBUG\n";
+ debugInfo += "───────────\n";
+ debugInfo += $"PLATFORM: {Application.platform}\n";
+ debugInfo += $"TIME SCALE: {Time.timeScale:F2}\n";
+ debugInfo += $"TARGET FPS: {Application.targetFrameRate}\n";
+ debugInfo += $"ACTUAL FPS: {fps:F1}\n";
+ debugInfo += "───────────\n";
+ debugInfo += $"ΔTime: {Time.deltaTime:F4}s\n";
+ debugInfo += $"ΔTime Min: {_minDeltaTime:F4}s\n";
+ debugInfo += $"ΔTime Max: {_maxDeltaTime:F4}s\n";
+ debugInfo += $"ΔTime Avg: {_avgDeltaTime:F4}s\n";
+ debugInfo += $"Fixed ΔT: {Time.fixedDeltaTime:F4}s\n";
+ debugInfo += "───────────\n";
+
+ if (settings != null)
+ {
+ debugInfo += $"Gravity: {settings.Gravity}\n";
+ debugInfo += $"Flap Force: {settings.FlapForce}\n";
+ debugInfo += $"Max Fall: {settings.MaxFallSpeed}\n";
+ debugInfo += "───────────\n";
+ }
+
+ debugInfo += $"Velocity: {velocity:F2}\n";
+ if (rb != null)
+ {
+ debugInfo += $"Position Y: {rb.position.y:F2}\n";
+ }
+ debugInfo += $"Dead: {birdController.IsDead}\n";
+
+ debugText.text = debugInfo;
+ }
+
+ ///
+ /// Toggle debug display visibility - can be called from UI button
+ ///
+ public void ToggleDisplay()
+ {
+ showDebugDisplay = !showDebugDisplay;
+ if (debugText != null)
+ {
+ debugText.gameObject.SetActive(showDebugDisplay);
+ }
+ }
+ }
+}
+
diff --git a/Assets/Scripts/Minigames/BirdPooper/BirdPooperDebugDisplay.cs.meta b/Assets/Scripts/Minigames/BirdPooper/BirdPooperDebugDisplay.cs.meta
new file mode 100644
index 00000000..825863a6
--- /dev/null
+++ b/Assets/Scripts/Minigames/BirdPooper/BirdPooperDebugDisplay.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 28d9dbe8309a4e1e8529953f5b400c0e
+timeCreated: 1766062170
\ No newline at end of file
diff --git a/Assets/Settings/BirdPooperSettings.asset b/Assets/Settings/BirdPooperSettings.asset
index 43b25ff0..6e0edae5 100644
--- a/Assets/Settings/BirdPooperSettings.asset
+++ b/Assets/Settings/BirdPooperSettings.asset
@@ -12,8 +12,8 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: e7d3f5b948b3454681fa573071bee978, type: 3}
m_Name: BirdPooperSettings
m_EditorClassIdentifier: AppleHillsScripts::Core.Settings.BirdPooperSettings
- gravity: 110
- flapForce: 55
+ gravity: 75
+ flapForce: 25
maxFallSpeed: 120
minY: -15
maxY: 15