308 lines
8.0 KiB
C#
308 lines
8.0 KiB
C#
using UnityEngine;
|
|
using UnityEngine.UI;
|
|
using UnityEngine.SceneManagement;
|
|
using TMPro;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
public class TaskTimer : MonoBehaviour
|
|
{
|
|
[Header("Timer Settings")]
|
|
[Tooltip("Total time for the task in seconds")]
|
|
public float taskDuration = 300f; // 5 minutes default
|
|
|
|
[Tooltip("Start timer automatically on scene load")]
|
|
public bool autoStart = true;
|
|
|
|
[Tooltip("Show warning when time is low (in seconds)")]
|
|
public float warningThreshold = 30f;
|
|
|
|
[Header("UDP Control Settings")]
|
|
[SerializeField] private int udpPort = 5555;
|
|
[SerializeField] private bool enableUDPControl = true;
|
|
[Tooltip("If enabled, timer will only start via UDP command (ignores autoStart)")]
|
|
[SerializeField] private bool waitForUDPStart = false;
|
|
|
|
[Header("UI References")]
|
|
[Tooltip("TextMeshPro component to display timer (recommended for VR)")]
|
|
public TextMeshProUGUI timerTextTMP;
|
|
|
|
[Tooltip("Legacy UI Text component (if not using TextMeshPro)")]
|
|
public Text timerTextLegacy;
|
|
|
|
[Header("Visual Settings")]
|
|
[Tooltip("Normal color for timer text")]
|
|
public Color normalColor = Color.white;
|
|
|
|
[Tooltip("Warning color when time is running low")]
|
|
public Color warningColor = Color.red;
|
|
|
|
[Tooltip("Format: 'MM:SS' or 'HH:MM:SS'")]
|
|
public bool showHours = false;
|
|
|
|
private float timeRemaining;
|
|
private bool isRunning;
|
|
|
|
// UDP infrastructure
|
|
private UdpClient udpClient;
|
|
private Thread udpListenerThread;
|
|
private bool isListening = false;
|
|
private string pendingCommand = null;
|
|
private readonly object commandLock = new object();
|
|
|
|
private void Start()
|
|
{
|
|
timeRemaining = taskDuration;
|
|
|
|
if (enableUDPControl)
|
|
{
|
|
StartUDPListener();
|
|
}
|
|
|
|
// Only auto-start if not waiting for UDP command
|
|
if (autoStart && !waitForUDPStart)
|
|
{
|
|
StartTimer();
|
|
}
|
|
|
|
UpdateTimerDisplay();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
// Check for pending UDP commands (must be done on main thread)
|
|
lock (commandLock)
|
|
{
|
|
if (!string.IsNullOrEmpty(pendingCommand))
|
|
{
|
|
ProcessTimerCommand(pendingCommand);
|
|
pendingCommand = null;
|
|
}
|
|
}
|
|
|
|
if (!isRunning)
|
|
return;
|
|
|
|
timeRemaining -= Time.deltaTime;
|
|
|
|
// Check if time is up
|
|
if (timeRemaining <= 0)
|
|
{
|
|
timeRemaining = 0;
|
|
OnTimerExpired();
|
|
}
|
|
|
|
UpdateTimerDisplay();
|
|
}
|
|
|
|
public void StartTimer()
|
|
{
|
|
isRunning = true;
|
|
Debug.Log($"TaskTimer: Timer started for {taskDuration} seconds");
|
|
}
|
|
|
|
public void PauseTimer()
|
|
{
|
|
isRunning = false;
|
|
Debug.Log("TaskTimer: Timer paused");
|
|
}
|
|
|
|
public void ResumeTimer()
|
|
{
|
|
isRunning = true;
|
|
Debug.Log("TaskTimer: Timer resumed");
|
|
}
|
|
|
|
public void ResetTimer()
|
|
{
|
|
timeRemaining = taskDuration;
|
|
UpdateTimerDisplay();
|
|
Debug.Log("TaskTimer: Timer reset");
|
|
}
|
|
|
|
public void AddTime(float seconds)
|
|
{
|
|
timeRemaining += seconds;
|
|
Debug.Log($"TaskTimer: Added {seconds} seconds. New time: {timeRemaining}");
|
|
}
|
|
|
|
private void UpdateTimerDisplay()
|
|
{
|
|
string timeText = FormatTime(timeRemaining);
|
|
|
|
// Update TextMeshPro
|
|
if (timerTextTMP != null)
|
|
{
|
|
timerTextTMP.text = timeText;
|
|
timerTextTMP.color = timeRemaining <= warningThreshold ? warningColor : normalColor;
|
|
}
|
|
|
|
// Update Legacy Text
|
|
if (timerTextLegacy != null)
|
|
{
|
|
timerTextLegacy.text = timeText;
|
|
timerTextLegacy.color = timeRemaining <= warningThreshold ? warningColor : normalColor;
|
|
}
|
|
}
|
|
|
|
private string FormatTime(float time)
|
|
{
|
|
int hours = Mathf.FloorToInt(time / 3600f);
|
|
int minutes = Mathf.FloorToInt((time % 3600f) / 60f);
|
|
int seconds = Mathf.FloorToInt(time % 60f);
|
|
|
|
if (showHours)
|
|
{
|
|
return string.Format("{0:00}:{1:00}:{2:00}", hours, minutes, seconds);
|
|
}
|
|
else
|
|
{
|
|
int totalMinutes = Mathf.FloorToInt(time / 60f);
|
|
return string.Format("{0:00}:{1:00}", totalMinutes, seconds);
|
|
}
|
|
}
|
|
|
|
private void OnTimerExpired()
|
|
{
|
|
isRunning = false;
|
|
Debug.Log("TaskTimer: Time expired! Returning to Lobby...");
|
|
|
|
// Load Lobby scene
|
|
SceneManager.LoadScene("Lobby");
|
|
}
|
|
|
|
// Public methods for external control
|
|
public float GetTimeRemaining()
|
|
{
|
|
return timeRemaining;
|
|
}
|
|
|
|
public bool IsRunning()
|
|
{
|
|
return isRunning;
|
|
}
|
|
|
|
public float GetProgress()
|
|
{
|
|
return 1f - (timeRemaining / taskDuration);
|
|
}
|
|
|
|
// UDP Control Methods
|
|
private void StartUDPListener()
|
|
{
|
|
try
|
|
{
|
|
isListening = true;
|
|
udpClient = new UdpClient(udpPort);
|
|
udpListenerThread = new Thread(new ThreadStart(ListenForUDPCommands));
|
|
udpListenerThread.IsBackground = true;
|
|
udpListenerThread.Start();
|
|
Debug.Log($"TaskTimer: UDP Listener started on port {udpPort}");
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogError($"TaskTimer: Failed to start UDP listener: {e.Message}");
|
|
}
|
|
}
|
|
|
|
private void ListenForUDPCommands()
|
|
{
|
|
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, udpPort);
|
|
|
|
while (isListening)
|
|
{
|
|
try
|
|
{
|
|
byte[] data = udpClient.Receive(ref remoteEndPoint);
|
|
string message = Encoding.UTF8.GetString(data);
|
|
ProcessUDPCommand(message);
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
if (isListening)
|
|
{
|
|
Debug.LogError($"TaskTimer: UDP receive error: {e.Message}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ProcessUDPCommand(string message)
|
|
{
|
|
Debug.Log($"TaskTimer: Received UDP command: {message}");
|
|
|
|
if (string.IsNullOrEmpty(message))
|
|
return;
|
|
|
|
// Parse command format: TIMER:COMMAND (e.g., TIMER:START, TIMER:PAUSE, TIMER:RESET, TIMER:RESUME)
|
|
string[] parts = message.Split(':');
|
|
if (parts.Length == 2 && parts[0].Trim().ToUpper() == "TIMER")
|
|
{
|
|
string command = parts[1].Trim().ToUpper();
|
|
lock (commandLock)
|
|
{
|
|
pendingCommand = command;
|
|
}
|
|
Debug.Log($"TaskTimer: Scheduled command: {command}");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"TaskTimer: Invalid UDP command format: {message}. Expected format: TIMER:COMMAND (e.g., TIMER:START)");
|
|
}
|
|
}
|
|
|
|
private void ProcessTimerCommand(string command)
|
|
{
|
|
switch (command)
|
|
{
|
|
case "START":
|
|
StartTimer();
|
|
break;
|
|
case "PAUSE":
|
|
PauseTimer();
|
|
break;
|
|
case "RESUME":
|
|
ResumeTimer();
|
|
break;
|
|
case "RESET":
|
|
ResetTimer();
|
|
break;
|
|
default:
|
|
Debug.LogWarning($"TaskTimer: Unknown command: {command}. Valid commands: START, PAUSE, RESUME, RESET");
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
StopUDPListener();
|
|
}
|
|
|
|
private void OnApplicationQuit()
|
|
{
|
|
StopUDPListener();
|
|
}
|
|
|
|
private void StopUDPListener()
|
|
{
|
|
isListening = false;
|
|
|
|
if (udpClient != null)
|
|
{
|
|
udpClient.Close();
|
|
udpClient = null;
|
|
}
|
|
|
|
if (udpListenerThread != null && udpListenerThread.IsAlive)
|
|
{
|
|
udpListenerThread.Abort();
|
|
udpListenerThread = null;
|
|
}
|
|
|
|
Debug.Log("TaskTimer: UDP Listener stopped");
|
|
}
|
|
}
|
|
|