2026-04-29 10:13:09 +07:00
|
|
|
using UnityEngine;
|
2026-05-11 21:35:39 +07:00
|
|
|
using TMPro;
|
|
|
|
|
using UnityEngine.UI;
|
|
|
|
|
using System.Collections;
|
2026-04-29 10:13:09 +07:00
|
|
|
|
|
|
|
|
public class ScoreManager : MonoBehaviour
|
|
|
|
|
{
|
2026-05-11 21:35:39 +07:00
|
|
|
[Header("UI References")]
|
|
|
|
|
public TextMeshProUGUI scoreText;
|
|
|
|
|
public TextMeshProUGUI timerText;
|
|
|
|
|
public TextMeshProUGUI winStatusText;
|
|
|
|
|
public GameObject distanceCanvas;
|
|
|
|
|
public TextMeshProUGUI distanceText;
|
|
|
|
|
|
|
|
|
|
[Header("Game Settings")]
|
|
|
|
|
public float gameDuration = 300f;
|
|
|
|
|
public int targetScore = 50;
|
|
|
|
|
|
2026-04-29 10:13:09 +07:00
|
|
|
private int currentScore = 0;
|
2026-05-11 21:35:39 +07:00
|
|
|
private float timeRemaining;
|
|
|
|
|
private bool isGameOver = false;
|
2026-04-29 10:13:09 +07:00
|
|
|
|
2026-05-04 14:52:40 +07:00
|
|
|
void Start()
|
|
|
|
|
{
|
|
|
|
|
currentScore = 0;
|
2026-05-11 21:35:39 +07:00
|
|
|
timeRemaining = gameDuration;
|
|
|
|
|
isGameOver = false;
|
|
|
|
|
|
2026-05-04 14:52:40 +07:00
|
|
|
UpdateScoreUI();
|
2026-05-11 21:35:39 +07:00
|
|
|
if (distanceCanvas != null) distanceCanvas.SetActive(false);
|
|
|
|
|
if (winStatusText != null) winStatusText.text = "";
|
2026-05-04 14:52:40 +07:00
|
|
|
|
2026-05-11 21:35:39 +07:00
|
|
|
Debug.Log("<color=green>ScoreManager đã khởi tạo thành công!</color>");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Update()
|
|
|
|
|
{
|
|
|
|
|
if (isGameOver) return;
|
|
|
|
|
|
|
|
|
|
if (timeRemaining > 0)
|
|
|
|
|
{
|
|
|
|
|
timeRemaining -= Time.deltaTime;
|
|
|
|
|
UpdateTimerUI();
|
|
|
|
|
|
|
|
|
|
if (currentScore >= targetScore)
|
|
|
|
|
{
|
|
|
|
|
WinGame();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GameOver();
|
|
|
|
|
}
|
2026-05-04 14:52:40 +07:00
|
|
|
}
|
|
|
|
|
|
2026-04-29 10:13:09 +07:00
|
|
|
private void OnTriggerEnter(Collider other)
|
|
|
|
|
{
|
2026-05-11 21:35:39 +07:00
|
|
|
Debug.Log("Va chạm Trigger với: " + other.gameObject.name);
|
|
|
|
|
ProcessScore(other.gameObject);
|
2026-05-04 14:52:40 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnCollisionEnter(Collision collision)
|
|
|
|
|
{
|
2026-05-11 21:35:39 +07:00
|
|
|
Debug.Log("Va chạm Vật lý với: " + collision.gameObject.name);
|
|
|
|
|
ProcessScore(collision.gameObject);
|
2026-05-04 14:52:40 +07:00
|
|
|
}
|
|
|
|
|
|
2026-05-11 21:35:39 +07:00
|
|
|
void ProcessScore(GameObject obj)
|
2026-05-04 14:52:40 +07:00
|
|
|
{
|
2026-05-11 21:35:39 +07:00
|
|
|
if (isGameOver) return;
|
2026-05-04 14:52:40 +07:00
|
|
|
|
2026-05-11 21:35:39 +07:00
|
|
|
// Ưu tiên kiểm tra script BouncyBall
|
|
|
|
|
BouncyBall ball = obj.GetComponent<BouncyBall>();
|
|
|
|
|
|
|
|
|
|
// Nếu không có script, hoặc bóng đã được tính điểm rồi thì bỏ qua
|
|
|
|
|
if (ball == null || ball.isScored)
|
2026-04-29 10:13:09 +07:00
|
|
|
{
|
2026-05-11 21:35:39 +07:00
|
|
|
return;
|
2026-04-29 10:13:09 +07:00
|
|
|
}
|
2026-05-11 21:35:39 +07:00
|
|
|
|
|
|
|
|
// Đánh dấu đã ghi điểm ngay lập tức
|
|
|
|
|
ball.isScored = true;
|
|
|
|
|
|
|
|
|
|
// Tính khoảng cách
|
|
|
|
|
float distance = Vector3.Distance(ball.shotPosition, transform.position);
|
|
|
|
|
int points = CalculatePoints(distance);
|
|
|
|
|
|
|
|
|
|
currentScore += points;
|
|
|
|
|
UpdateScoreUI();
|
|
|
|
|
|
|
|
|
|
// Hiển thị khoảng cách trên rổ
|
|
|
|
|
if (distanceCanvas != null)
|
|
|
|
|
{
|
|
|
|
|
StopAllCoroutines(); // Dừng các lần hiện trước đó để tránh chồng chéo
|
|
|
|
|
StartCoroutine(ShowDistanceUI(distance));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Debug.LogWarning("distanceCanvas chưa được gán trong Inspector!");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Debug.Log($"<color=cyan>GHI ĐIỂM: {points}pt | Khoảng cách ném: {distance:F2}m | Tổng điểm: {currentScore}</color>");
|
|
|
|
|
|
|
|
|
|
// Vô hiệu hóa script hoặc quả bóng để không tính điểm lại
|
|
|
|
|
ball.enabled = false;
|
|
|
|
|
Destroy(obj, 0.5f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int CalculatePoints(float distance)
|
|
|
|
|
{
|
|
|
|
|
// Điều chỉnh lại logic: Nếu trong AR khoảng cách tính bằng Unity Unit có thể rất nhỏ
|
|
|
|
|
// Bạn có thể cần nhân distance với một hệ số nếu tỉ lệ scale của bạn khác 1:1 mét
|
|
|
|
|
if (distance >= 10f) return 3;
|
|
|
|
|
if (distance >= 5f) return 2;
|
|
|
|
|
if (distance >= 3f) return 1;
|
|
|
|
|
return 1;
|
2026-04-29 10:13:09 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UpdateScoreUI()
|
|
|
|
|
{
|
2026-05-11 21:35:39 +07:00
|
|
|
if (scoreText != null) scoreText.text = "Score: " + currentScore;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void UpdateTimerUI()
|
|
|
|
|
{
|
|
|
|
|
if (timerText != null)
|
2026-05-04 14:52:40 +07:00
|
|
|
{
|
2026-05-11 21:35:39 +07:00
|
|
|
int minutes = Mathf.FloorToInt(timeRemaining / 60);
|
|
|
|
|
int seconds = Mathf.FloorToInt(timeRemaining % 60);
|
|
|
|
|
timerText.text = string.Format("Time: {0:00}:{1:00}", minutes, seconds);
|
2026-05-04 14:52:40 +07:00
|
|
|
}
|
2026-05-11 21:35:39 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IEnumerator ShowDistanceUI(float distance)
|
|
|
|
|
{
|
|
|
|
|
if (distanceText != null) distanceText.text = $"{distance:F1}m";
|
|
|
|
|
distanceCanvas.SetActive(true);
|
|
|
|
|
yield return new WaitForSeconds(2.5f);
|
|
|
|
|
distanceCanvas.SetActive(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WinGame()
|
|
|
|
|
{
|
|
|
|
|
isGameOver = true;
|
|
|
|
|
if (winStatusText != null)
|
|
|
|
|
{
|
|
|
|
|
winStatusText.text = "YOU WIN!";
|
|
|
|
|
winStatusText.color = Color.green;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GameOver()
|
|
|
|
|
{
|
|
|
|
|
isGameOver = true;
|
|
|
|
|
if (winStatusText != null)
|
2026-05-04 14:52:40 +07:00
|
|
|
{
|
2026-05-11 21:35:39 +07:00
|
|
|
winStatusText.text = "GAME OVER";
|
|
|
|
|
winStatusText.color = Color.red;
|
2026-05-04 14:52:40 +07:00
|
|
|
}
|
2026-04-29 10:13:09 +07:00
|
|
|
}
|
|
|
|
|
}
|