135 lines
4.4 KiB
C#
135 lines
4.4 KiB
C#
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using UnityEngine;
|
|
using Zenject;
|
|
using FlappyBird.Core;
|
|
|
|
namespace FlappyBird.UI
|
|
{
|
|
public class GameUIView : MonoBehaviour
|
|
{
|
|
[Header("Score Display")]
|
|
[SerializeField] private Component currentScoreText;
|
|
[SerializeField] private Component bestScoreText;
|
|
[SerializeField] private RectTransform popupContainer;
|
|
[SerializeField] private float scorePopScale = 1.4f;
|
|
[SerializeField] private float scorePopDuration = 0.1f;
|
|
|
|
[Header("Screens")]
|
|
[SerializeField] private CanvasGroup tapToStartGroup;
|
|
[SerializeField] private CanvasGroup gameOverGroup;
|
|
[SerializeField] private float fadeSpeed = 5f;
|
|
|
|
[Header("Game Over Details")]
|
|
[SerializeField] private Component gameOverScoreText;
|
|
[SerializeField] private Component gameOverBestScoreText;
|
|
|
|
|
|
private IGameUIController _controller;
|
|
private RectTransform _currentScoreRect;
|
|
private Vector3 _currentScoreBaseScale;
|
|
private float _scorePopTimer;
|
|
private List<ScorePopupView> _activePopups = new List<ScorePopupView>();
|
|
public Transform PopupTransform => transform;
|
|
public RectTransform PopupContainer => popupContainer != null ? popupContainer : (transform as RectTransform);
|
|
|
|
[Inject]
|
|
public void Construct(IGameUIController controller)
|
|
{
|
|
_controller = controller;
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
if (currentScoreText != null)
|
|
{
|
|
_currentScoreRect = currentScoreText.transform as RectTransform;
|
|
_currentScoreBaseScale = _currentScoreRect.localScale;
|
|
}
|
|
}
|
|
|
|
private void Start() => _controller.Bind(this);
|
|
|
|
private void Update()
|
|
{
|
|
UpdateScorePop();
|
|
UpdateScreenFades();
|
|
UpdateMenuPulse();
|
|
HandlePopupCleanup();
|
|
}
|
|
|
|
public void ShowScoreGain()
|
|
{
|
|
_scorePopTimer = scorePopDuration;
|
|
}
|
|
|
|
public void SetCurrentScore(int score)
|
|
{
|
|
SetText(currentScoreText, score.ToString());
|
|
SetText(gameOverScoreText, score.ToString());
|
|
}
|
|
|
|
public void SetBestScore(int bestScore)
|
|
{
|
|
SetText(bestScoreText, $"BEST: {bestScore}");
|
|
SetText(gameOverBestScoreText, bestScore.ToString());
|
|
}
|
|
|
|
private void UpdateScorePop()
|
|
{
|
|
if (_scorePopTimer <= 0) return;
|
|
|
|
_scorePopTimer -= Time.unscaledDeltaTime;
|
|
// Use an animation curve feel: pop out fast, shrink back
|
|
float t = _scorePopTimer / scorePopDuration;
|
|
float scale = Mathf.Lerp(1f, scorePopScale, t);
|
|
_currentScoreRect.localScale = _currentScoreBaseScale * scale;
|
|
}
|
|
|
|
private void UpdateScreenFades()
|
|
{
|
|
float step = fadeSpeed * Time.unscaledDeltaTime;
|
|
// Smoothly fade groups based on their active state handled in Controller
|
|
tapToStartGroup.alpha = Mathf.MoveTowards(tapToStartGroup.alpha, tapToStartGroup.interactable ? 1 : 0, step);
|
|
gameOverGroup.alpha = Mathf.MoveTowards(gameOverGroup.alpha, gameOverGroup.interactable ? 1 : 0, step);
|
|
}
|
|
|
|
private void UpdateMenuPulse()
|
|
{
|
|
if (tapToStartGroup.interactable)
|
|
{
|
|
float pulse = 1f + Mathf.Sin(Time.unscaledTime * 4f) * 0.05f;
|
|
tapToStartGroup.transform.localScale = Vector3.one * pulse;
|
|
}
|
|
}
|
|
|
|
public void SetTapToStartVisible(bool visible)
|
|
{
|
|
tapToStartGroup.interactable = visible;
|
|
tapToStartGroup.blocksRaycasts = visible;
|
|
}
|
|
|
|
public void SetGameOverVisible(bool visible)
|
|
{
|
|
gameOverGroup.interactable = visible;
|
|
gameOverGroup.blocksRaycasts = visible;
|
|
}
|
|
|
|
public void SetCurrentScoreVisible(bool visible)
|
|
{
|
|
currentScoreText.gameObject.SetActive(visible);
|
|
}
|
|
|
|
private void HandlePopupCleanup()
|
|
{
|
|
// Logic to return expired popups to pool would go here via the Controller
|
|
}
|
|
|
|
private static void SetText(Component target, string value)
|
|
{
|
|
if (target == null) return;
|
|
var prop = target.GetType().GetProperty("text");
|
|
prop?.SetValue(target, value, null);
|
|
}
|
|
}
|
|
} |