rabidus-test/Assets/SCSM/SciFiShipController/Demos/Scripts/OnScreenTimer.cs

309 lines
12 KiB
C#
Raw Permalink Normal View History

2023-07-24 16:38:13 +03:00
using UnityEngine;
using UnityEngine.UI;
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
namespace SciFiShipController
{
/// <summary>
/// On-screen timer.
/// NOTE: This will inpact Garbage Collection (GC) as the on-screen text is updated
/// with stringbuilder.
/// </summary>
public class OnScreenTimer : MonoBehaviour
{
#region Public Variables and Properties
public bool initialiseOnAwake = true;
public float dayLength = 86400f; // Day length in seconds
public Color screenClockTextColour = new Color(240f / 255f, 240f / 255f, 240f / 255f, 33f / 255f);
public bool useScreenClockSeconds = true;
// Screen clock defaults
public Color GetDefaultScreenClockTextColour { get { return new Color(240f / 255f, 240f / 255f, 240f / 255f, 33f / 255f); } }
#endregion
#region Private variables
private bool isInitialised = false;
private Canvas timerCanvas = null;
private float dayTimer = 0f;
private float screenClockUpdateTimer = 0f;
private float screenClockUpdateInterval = 60f; // # seconds between updates
private float simTimeRealTimeRatio = 1f; // How many seconds pass in simulated time to every real time second?
private RectTransform screenClockPanel;
private Text screenClockText;
#endregion
#region Initialisation Methods
// Use this for initialization
void Awake()
{
if (initialiseOnAwake) { Initialise(); }
}
/// <summary>
/// WARNING: This will affect GC as it does string comparisons
/// </summary>
public void Initialise()
{
isInitialised = false;
ValidateOnScreenTimer();
// How many seconds pass in simulated time to every real time second?
simTimeRealTimeRatio = 86400 / dayLength;
// Determine how often to update the clock
screenClockUpdateInterval = useScreenClockSeconds ? 1f : 60f;
if (screenClockText != null)
{
screenClockText.text = CurrentTimeString(useScreenClockSeconds);
UpdateScreenClockColour();
isInitialised = true;
}
}
#endregion
#region Update Methods
// Update is called once per frame
void Update()
{
dayTimer += Time.deltaTime;
if (isInitialised && screenClockText != null)
{
// Clock displays hours and minutes. So need no to update every frame
screenClockUpdateTimer += Time.deltaTime * simTimeRealTimeRatio;
if (screenClockUpdateTimer > screenClockUpdateInterval)
{
screenClockUpdateTimer = 0f;
screenClockText.text = CurrentTimeString(useScreenClockSeconds);
}
}
}
#endregion
#region Private Methods
private void ValidateOnScreenTimer()
{
if (timerCanvas == null)
{
// Attempt to find the canvas
GameObject timerCanvasGO = null;
Canvas[] canvasArray = FindObjectsOfType<Canvas>();
// ArrayUtility.Find only works in editor
int numCanvas = canvasArray == null ? 0 : canvasArray.Length;
for (int cvsIdx = 0; cvsIdx < numCanvas; cvsIdx++)
{
if (canvasArray[cvsIdx].name == "OnScreenTimerCanvas") { timerCanvas = canvasArray[cvsIdx]; break; }
}
// If OnScreenTimer canvas doesn't exist, create it
if (timerCanvas == null)
{
timerCanvasGO = new GameObject("OnScreenTimerCanvas");
timerCanvasGO.layer = 5;
timerCanvasGO.AddComponent<Canvas>();
timerCanvas = timerCanvasGO.GetComponent<Canvas>();
timerCanvas.renderMode = RenderMode.ScreenSpaceOverlay;
timerCanvasGO.AddComponent<UnityEngine.UI.CanvasScaler>();
timerCanvasGO.AddComponent<UnityEngine.UI.GraphicRaycaster>();
}
}
if (timerCanvas != null && screenClockPanel == null)
{
if (screenClockPanel == null)
{
RectTransform[] rectTransforms = timerCanvas.GetComponentsInChildren<RectTransform>(true);
if (rectTransforms != null)
{
foreach (RectTransform rt in rectTransforms)
{
if (rt.name == "ClockPanel")
{
screenClockPanel = rt;
break;
}
}
}
// If the clock panel doesn't exist, create it
if (screenClockPanel == null)
{
// Add the clock panel as a child of the Canvas
GameObject screenClockPanelGameObject = new GameObject("ClockPanel");
if (screenClockPanelGameObject != null)
{
screenClockPanelGameObject.transform.parent = timerCanvas.transform;
screenClockPanel = screenClockPanelGameObject.AddComponent<RectTransform>();
// Configure clock Panel rect transform
if (screenClockPanel != null)
{
// Place the panel in the lower right corner
screenClockPanel.anchorMin = new Vector2(0.8f, 0f);
screenClockPanel.anchorMax = new Vector2(0.97f, 0.15f);
screenClockPanel.sizeDelta = Vector2.zero;
screenClockPanel.anchoredPosition = Vector2.zero;
screenClockPanel.localScale = Vector3.one;
}
// Add the UI Text component as a child of the ClockPanel
GameObject screenClockTextGameObject = new GameObject("ClockText");
if (screenClockTextGameObject != null)
{
screenClockTextGameObject.transform.parent = screenClockPanelGameObject.transform;
// Add and configure the clock Text RectTransform
RectTransform screenClockTextRectTranform = screenClockTextGameObject.AddComponent<RectTransform>();
if (screenClockTextRectTranform != null)
{
screenClockTextRectTranform.anchorMin = Vector2.zero;
screenClockTextRectTranform.anchorMax = Vector2.one;
screenClockTextRectTranform.sizeDelta = Vector2.zero;
screenClockTextRectTranform.anchoredPosition = Vector2.zero;
screenClockTextRectTranform.localScale = Vector3.one;
// Add and configure the clock Text
screenClockText = screenClockTextGameObject.AddComponent<Text>();
if (screenClockText != null)
{
// Configure the UI Text
screenClockText.font = SSCUtils.GetDefaultFont();
screenClockText.raycastTarget = false;
screenClockText.resizeTextForBestFit = true;
screenClockText.alignment = TextAnchor.MiddleRight;
screenClockText.text = "00:00";
}
}
}
}
}
if (screenClockPanel != null && screenClockText == null)
{
screenClockText = timerCanvas.GetComponentInChildren<Text>(true);
}
}
}
}
/// <summary>
/// Returns a formatted time string in HH:MM or HH:MM:SS
/// </summary>
/// <param name="includeSeconds"></param>
/// <returns></returns>
private string CurrentTimeString(bool includeSeconds = false)
{
float timeOfDay = (CurrentTime() / dayLength) * 24f;
float minutes = (timeOfDay % 1f) * 60f;
if (includeSeconds)
{
int minsInt = (int)System.Math.Floor(minutes);
return FormatTime((int)System.Math.Floor(timeOfDay), minsInt, (int)((minutes - minsInt) * 60f));
}
else
{
return FormatTime((int)System.Math.Floor(timeOfDay), (int)System.Math.Floor(minutes));
}
}
/// <summary>
/// Returns the time given the hours, minutes, and seconds
/// Displays -- if the values exceed their limits.
/// For the sake of performance, assumes all values are +ve.
/// </summary>
/// <param name="hours"></param>
/// <param name="minutes"></param>
/// <param name="secs"></param>
/// <returns></returns>
private string FormatTime(int hours, int minutes, int secs)
{
System.Text.StringBuilder sf = new System.Text.StringBuilder(15);
// Deal with most common scenarios first
if (hours == 0) { sf.Append("00"); }
else if (hours < 10) { sf.Append("0"); sf.Append(hours); }
else if (hours < 99) { sf.Append(hours); }
else { sf.Append("--"); }
sf.Append(":");
if (minutes > 59) { sf.Append("--"); }
else if (minutes > 9) { sf.Append(minutes); }
else { sf.Append("0"); sf.Append(minutes); }
sf.Append(":");
if (secs > 59) { sf.Append("--"); }
else if (secs > 9) { sf.Append(secs); }
else { sf.Append("0"); sf.Append(secs); }
return sf.ToString();
}
/// <summary>
/// Returns the time given the hours and minutes
/// Displays -- if the values exceed their limits.
/// For the sake of performance, assumes all values are +ve.
/// </summary>
/// <param name="hours"></param>
/// <param name="minutes"></param>
/// <returns></returns>
private string FormatTime(int hours, int minutes)
{
System.Text.StringBuilder sf = new System.Text.StringBuilder(6);
if (hours == 0) { sf.Append("00"); }
else if (hours < 10) { sf.Append("0"); sf.Append(hours); }
else if (hours < 99) { sf.Append(hours); }
else { sf.Append("--"); }
sf.Append(":");
if (minutes > 59) { sf.Append("--"); }
else if (minutes > 9) { sf.Append(minutes); }
else { sf.Append("0"); sf.Append(minutes); }
return sf.ToString();
}
#endregion
#region Public Methods
/// <summary>
/// Returns the time in seconds from the start of the current day
/// </summary>
/// <returns>The time.</returns>
public float CurrentTime()
{
return dayTimer % dayLength;
}
public void UpdateScreenClockColour()
{
if (screenClockText != null)
{
try
{
screenClockText.color = screenClockTextColour;
}
catch (System.Exception ex)
{
#if UNITY_EDITOR
Debug.LogWarning("OnScreenTimer.UpdateScreenClockColour " + ex.Message);
#else
if (ex != null) { }
#endif
}
}
}
#endregion
}
}