Homebound / Scripts / GameOverlay.cs
GameOverlay.cs
Raw
using System;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class GameOverlay : MonoBehaviour, IDDataPersistence
{
    public static GameOverlay Instance { get; private set; }

    [Header("Canvas Attributes")]
    [SerializeField] TextMeshProUGUI levelTitleText;        // Reference to the title of the level text
    [SerializeField] Image timerImage;                      // Reference to the timer that ticks down for the time trial
    [SerializeField] Image completedTrialImage;             // Reference for the medal Image
    [SerializeField] TextMeshProUGUI timeToCompleteTrial;   // Reference to how much time is left for the trial to be completed
    [SerializeField] bool hasDollyCamera = true;            // Reference for if the current level has a dolly camera, if so the start timer won't start automatically, otherwise will start

    // Reference
    bool trialCompleted = false; // Reference to be called upon loading and saving if the image for the medal is activated
    bool startTimer = false;     // Reference for the trigger to start the timer to make sure when the level is fully ready to start

    // Components
    LevelSO[] levelRefs; // Reference for the collection of levels for the Game Overlay script to cycle through
    LevelSO levelRef; // Reference for the Level SO for the current Level
    float timer; // Time to tick down
    bool paused;

    /// <summary>
    /// Starts the game overlay's timer
    /// </summary>
    public void StartTimer() => startTimer = true;

    void Awake()
    {
        Instance = this;
    }

    // Start is called before the first frame update
    void Start()
    {
        try
        {
            LoadData(DataPersistenceManager.Instance.gameData);
        }
        catch (NullReferenceException ex) when (DataPersistenceManager.Instance.gameData == null)
        {
            throw new NullReferenceException("Game Data not initialized, loading overlay data has failed! Continuing as if level has not been completed...", ex);
        }
        
        timerImage.fillAmount = 1f; // Sets the timer image state to full amount
        timeToCompleteTrial.text = levelRef.levelTimeTrial.ToString(); // Sets the text inside the timer for the time to complete the trial from the level reference 
        
        timer = levelRef.levelTimeTrial; // Sets the time for the trial to be completed

        levelTitleText.text = levelRef.levelName; // Level title is set to the name of the current Level SO name

        // If the reference for the trial have already been completed returns true then the medal image show up next to the timer
        // otherwise it does not show up
        if (trialCompleted)
            completedTrialImage.enabled = true;
        else 
            completedTrialImage.enabled = false;

        if (!hasDollyCamera) StartTimer();
    }

    // Update is called once per frame
    void Update()
    {
        if (!MainMenu.IsPaused && startTimer)
            TimeTrialTimer();
    }

    /// <summary>
    /// Timer for the Time Trial that will continously tick down if the timer value is positive
    /// </summary>
    void TimeTrialTimer()
    {
        
        // When the timer is positive it will continously count down based on delta time and decrement the timer image 
        if (timer > 0f && !paused)
        {
            timer -= Time.deltaTime;
            timerImage.fillAmount = timer / levelRef.levelTimeTrial;
            timeToCompleteTrial.text = Mathf.CeilToInt(timer).ToString();
        }
        // This else if statement makes sure that the fill amount will always end up at zero if the timer value doesn't properly align with the fill amount
        else if (timer <= 0f && timerImage.fillAmount > 0f)
        {
            timerImage.fillAmount = 0f;
            timeToCompleteTrial.text = "0";
        }
    }

    public bool StopTimer()
    {
        bool output = false;
        if (timer > 0f)
        {
            paused = true;
            trialCompleted = true;
            completedTrialImage.enabled = true;
            output = true;
        }

        SaveData(ref DataPersistenceManager.Instance.gameData);
        return output;
    }

    public void LoadData(GameData data)
    {
        levelRefs = Resources.LoadAll<LevelSO>("Levels/");

        foreach (KeyValuePair<string, bool> levelName in data.completedTrials)
        {
            if (levelName.Key == SceneManager.GetActiveScene().name)
            {
                for (int i = 0; i < levelRefs.Length; i++)
                {
                    if (levelRefs[i].levelName == levelName.Key)
                    {
                        levelRef = levelRefs[i];
                        break;
                    }
                }
                trialCompleted = levelName.Value;
                break;
            }
        }
    }

    public void SaveData(ref GameData data)
    {
        data.completedTrials.Remove(levelRef.levelName);     // Removes the level reference
        data.levelTimesCompleted.Remove(levelRef.levelName); // Removes the level reference for the time aspect

        data.completedTrials.Add(levelRef.levelName, trialCompleted); // Then re-adds the level with the current trial state
        data.levelTimesCompleted.Add(levelRef.levelName, timer);      // Then re-adds the level with the time that is left for the current level      
    }
}