Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Timer and save the best time in each game level

Discussion in 'Scripting' started by Deleted User, Feb 25, 2021.

  1. Deleted User

    Deleted User

    Guest

    i have a timer which as soon as game runs , will start the level time.

    there is also another ui text which will only show the lowest time as best time for user. so they know the most lowest time for them to finish that level is that.

    the very first time each level is played , the best time is not available. because user has not still played there.

    here is the timer script which is attached to an empty gameobject in level 1. this script is also attached to game pause panel and gameover panel which are deactivated at the start of the level.

    at the moment , timer does not work when level is started, but one time opening and closing the pause panel makes the timer working.

    i hope someone help me to complete the game timer problem and also learn from it. thanks by the way,

    level timer class:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. public class LevelTimer : MonoBehaviour
    5. {
    6.     [SerializeField]
    7.     private Text gameTimerText;
    8.  
    9.     [SerializeField]
    10.     private Text gameBestTimeText;
    11.  
    12.     public float startTime;
    13.  
    14.     private string minutes;
    15.     private string seconds;
    16.  
    17.     public bool wonGame = false;
    18.  
    19.  
    20.     private void Start()
    21.     {
    22.         startTime = Time.time;
    23.         gameBestTimeText.color = Color.yellow;
    24.  
    25.     }
    26.  
    27.     private void Update()
    28.     {
    29.         float t = Time.time - startTime;
    30.  
    31.         minutes = ((int)t / 60).ToString();
    32.         seconds = (t % 60).ToString("f0");
    33.         gameTimerText.text = minutes + " : " + seconds;
    34.  
    35.         if (wonGame)
    36.         {
    37.             Debug.Log("won");
    38.             gameTimerText.text = minutes + " : " + seconds;
    39.         }
    40.     }
    41.  
    42.     public void ShowGameTimer()
    43.     {
    44.         Debug.Log("game over");
    45.  
    46.         gameTimerText.text = minutes + " : " + seconds;
    47.     }
    48.  
    49. }
    50.  
     
  2. pantang

    pantang

    Joined:
    Sep 1, 2016
    Posts:
    217
    gamerTimerText.text = TimeSpan.FromSeconds(time.time - starttime).ToString("mm:ss")

    thats a easier way to get your time into a string, as to why your loop isn't working I do not know, but you dont need todo the same thing on won, is the GUI active?

    maybe add a few Debug.Log("have we set gamerText" + gameTimerText.text);
     
  3. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    Start and Update do not run unless the GameObject it is attached to is active. You say you have 3 instances of this script in your game. I don't see why you would want more than 1 instance. The instances you have attached to the deactivated pause and gameover panels won't set their start times if the GameObjects aren't active.

    I'm wondering if you think that the variables in the script are shared between the 3 instances? They are not. They are all separate from each other. So the instance of the script attached to the otherwise empty GameObject in the scene, may be working fine, but when you try to use the instances attached to the 2 panels, they hold entirely separate values for everything. My guess is you should remove the instances attached to the panels, and instead reference the instance attached to the empty GameObject.
     
    Deleted User likes this.
  4. BenniKo

    BenniKo

    Joined:
    Mar 24, 2015
    Posts:
    100
    You attached this script to multiple game objects? I would only do that for the timer script. One instance should be enough.

    Also I think your timer is not running because you attach it to an deactivated gameobject.
    start() and update() will not execute on deactivated game objects.
     
    Last edited: Feb 25, 2021
    Deleted User and Joe-Censored like this.
  5. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    I would design it so the pause and gameover scripts talk to the timer script. You can set a reference to the Timer script by adding a public field, then dragging it there in the inspector.

    Code (csharp):
    1. public LevelTimer TimerScript;
    If the pause and gameover scripts need to write to the UI Text themselves, I'd replace ShowGameTimer() with a method that returns a string instead. The pause and gameover scripts can then just call that method on the Timer script, and put it into their own Text UI they have a reference to.
     
    Deleted User likes this.
  6. Joe-Censored

    Joe-Censored

    Joined:
    Mar 26, 2013
    Posts:
    11,847
    So your code shows the last run through, but not the best. For that you'd add another float to save the best result for Timer. You'd probably make it a static variable if your game ends up destroying this instance of LevelTimer. Then when you calculate a final result at the end of a game, you compare the current Timer to the saved bestTimer, and if lower you replace bestTimer with the value from Timer.

    One issue is a float will default to 0f, and 0f will always be lower than whatever run through time a player gets, so you'll either need to detect that 0f isn't a real result for bestTimer so always overwrite if 0, or default bestTimer to an extremely high value at the start where the first run by even a terrible player will always be lower.

    If you need this to persist between launches of the game, you'd save bestTimer to PlayerPrefs, but that's its own topic and there's already a lot of threads on how to use that.
     
    Deleted User likes this.
  7. Deleted User

    Deleted User

    Guest

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. public class LevelTimer : MonoBehaviour
    5. {
    6.     public Text gameTimerText;
    7.  
    8.     public float Timer;
    9.  
    10.     // float for the best time
    11.     public float bestTime;
    12.  
    13.  
    14.  
    15.     private void Update()
    16.     {
    17.         Timer += Time.deltaTime;
    18.  
    19.         int minutes = Mathf.FloorToInt(Timer / 60f);
    20.         int seconds = Mathf.FloorToInt(Timer % 60f);
    21.  
    22.         gameTimerText.text = minutes.ToString("00") + " : " + seconds.ToString("00");
    23.     }
    24. }
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.UI;
    3.  
    4. public class GameOverInfo : MonoBehaviour
    5. {
    6.     private LevelTimer levelTimerInstance;
    7.  
    8.     [SerializeField]
    9.     private Text gameTimerText;
    10.  
    11.     [SerializeField]
    12.     private Text gameBestTimeText;
    13.  
    14.  
    15.  
    16.     private void Start()
    17.     {
    18.         levelTimerInstance = GameObject.Find("LevelTimer").GetComponent<LevelTimer>();
    19.         gameBestTimeText.color = Color.yellow;
    20.  
    21.      
    22.  
    23.     }
    24.  
    25.     private void Update()
    26.     {
    27.         gameTimerText.text = levelTimerInstance.gameTimerText.text;
    28.  
    29.         gameBestTimeText.text = levelTimerInstance.gameTimerText.text;
    30.  
    31.  
    32.     }
    33.  
    34.  
    35.  
    36. }
    37.  
    as sson as game is over , i see the current timer time as the best time.

    when user plays the game again, how to do a comparison and see if timer is lower than last timer , then change the value of the best time?

    here is what i did for that:

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.SceneManagement;
    3. using TMPro;
    4.  
    5. public class GameManager : MonoBehaviour
    6. {
    7.     private LevelTimer levelTimerInstance;
    8.  
    9.     public GameObject[] players;
    10.  
    11.     [SerializeField]
    12.     private TextMeshProUGUI moneyAmountText;
    13.  
    14.     [SerializeField]
    15.     private Transform spawnPoint;
    16.  
    17.     public GameObject gameOverPanel;
    18.  
    19.  
    20.  
    21.  
    22.  
    23.     private void Start()
    24.     {
    25.         levelTimerInstance = GameObject.Find("LevelTimer").GetComponent<LevelTimer>();
    26.  
    27.         SpawnPlayer();
    28.     }
    29.  
    30.     private void Update()
    31.     {
    32.         moneyAmountText.text = GlobalValues.money.ToString();
    33.  
    34.  
    35.         if (gameOverPanel.activeSelf == true)
    36.         {
    37.             Debug.Log("game over is active from gamemanager");
    38.  
    39.             if (levelTimerInstance.Timer < levelTimerInstance.bestTime)
    40.             {
    41.                 levelTimerInstance.bestTime = levelTimerInstance.Timer;
    42.             }
    43.         }
    44.  
    45.     }
    but this only always shows the last timer as the best time.
    then how to add comparison to the last timer time?

    if it is not understandable i can add more to that/

    please help . i am here for a week now.
    give some code please..
     
  8. Deleted User

    Deleted User

    Guest

    this is the only place in GameManager for game over:

    Code (CSharp):
    1.     private void Update()
    2.     {
    3.         moneyAmountText.text = GlobalValues.money.ToString();
    4.  
    5.         // game over
    6.         if (gameOver)
    7.         {
    8.             Debug.Log("game over from gamemanager");
    9.  
    10.             gameOverPanel.SetActive(true);
    11.  
    12.             if (levelTimerInstance.Timer < levelTimerInstance.bestTime)
    13.             {
    14.                 levelTimerInstance.bestTime = levelTimerInstance.Timer;
    15.             }
    16.         }
    17.  
    18.     }
    now inside this if condition, i need:

    if this is the first time game runs , best time is the current level timer time.

    if this is the second time or above, the lowest time is the best time.

    this is very confusing. i hope someone can help. thanks.
     
  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,336
    This is only confusing because you're not packaging it up in a well-designed API. The above is a LOT of code to go sprinkling around your game wherever times might be involved.

    I suggest perhaps wrapping it up as an API that can do semantic things like "record this time if lower" and internally it will check if you have already any best time recorded, and if you don't, well, that first time by definition is the best time.

    Here are some super-simple examples, one for highest score (higher is better), one for lower time (lower is better):

    Tracking high scores low times single-entry leaderboard:

    For scores: https://pastebin.com/VmngEK05

    Usage:

    Code (csharp):
    1. TheBest.RecordScoreIfHigher( lastGamePlayScore);
    For times: https://pastebin.com/A7GC76uQ

    Usage:

    Code (csharp):
    1. TheBest.RecordTimeIfLower( lastGamePlayTime);
    To retrieve the best score or time, use one of these:

    Code (csharp):
    1. int bestScore = TheBest.BestScore;
    Code (csharp):
    1. float bestTime = TheBest.BestTime;
     
    Deleted User likes this.
  10. Deleted User

    Deleted User

    Guest


    Code (CSharp):
    1.  private void Update()
    2.     {
    3.         moneyAmountText.text = GlobalValues.money.ToString();
    4.  
    5.         // game over
    6.         if (gameOver)
    7.         {
    8.             //Debug.Log("game over from gamemanager");
    9.             TheBest.RecordTimeIfLower(lastGamePlayTime);
    10.             float bestTime = TheBest.BestTime;
    11.  
    12.  
    13.             gameOverPanel.SetActive(true);
    14.  
    15.             if (levelTimerInstance.Timer < levelTimerInstance.bestTime)
    16.             {
    17.                 levelTimerInstance.bestTime = levelTimerInstance.Timer;
    18.             }
    19.         }
    20.  
    21.     }
    there are some errors above.

    TheBest and lastGamePlayTime dont exist. what do i do?

    i have created a new class and put this in unity project:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public static partial class Timer
    6. {
    7.     // for times: lower is better
    8.     private static string s_LowestTime = "LowestTime";
    9.  
    10.     public static bool HaveBestTime
    11.     {
    12.         get
    13.         {
    14.             return PlayerPrefs.HasKey(s_LowestTime);
    15.         }
    16.     }
    17.  
    18.     public static float BestTime
    19.     {
    20.         get
    21.         {
    22.             return PlayerPrefs.GetFloat(s_LowestTime, 0);
    23.         }
    24.         private set     // make this public if you trust yourself
    25.         {
    26.             PlayerPrefs.SetFloat(s_LowestTime, value);
    27.         }
    28.     }
    29.  
    30.     public static bool RecordTimeIfLower(float time)
    31.     {
    32.         if (!HaveBestTime || (time < BestTime))
    33.         {
    34.             BestTime = time;
    35.             return true;
    36.         }
    37.         return false;
    38.     }
    39.  
    40.     public static void ClearBestTime()
    41.     {
    42.         if (HaveBestTime)
    43.         {
    44.             PlayerPrefs.DeleteKey(s_LowestTime);
    45.         }
    46.     }
    47. }
    48.  
    then i think i use it in gamemanager where game over runs. but this is also problem. how to fix it ? thanks.
     
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,336
    Of course it doesn't exist. You renamed it!

    My original code:

    Code (csharp):
    1. public static partial class TheBest
    My class name was TheBest. You can leave it that way or you can rename it, I don't care.

    Just beware that if you rename an identifier and call it Timer, you better be sure you are referring to it as Timer, because it sure won't exist anymore as TheBest!
     
    Deleted User likes this.
  12. Deleted User

    Deleted User

    Guest

    thanks. i hope you are not unhappy i did rename your class. and now in this line :

    Timer.RecordTimeIfLower(lastGamePlayTime);

    it says lastGamePlayTime does not exist. where is it if i missed it?
    where actually in your class and solution timer counts?
    where is the lowest time registered? via that playerprefs?

    and is inside gamemanager in gameover bool the referencing the correct way of calling - saving the lowest time in my codes now?

    i hope you help me to find this out. thanks.
     
  13. Deleted User

    Deleted User

    Guest

    so now errors are gone, but time is not saved and the lowest time is not also shown. here is the code:

    Code (CSharp):
    1.   private void Update()
    2.     {
    3.         moneyAmountText.text = GlobalValues.money.ToString();
    4.  
    5.         // game over
    6.         if (gameOver)
    7.         {
    8.             Timer.RecordTimeIfLower(levelTimerInstance.Timer);
    9.  
    10.             float bestTime = Timer.BestTime;
    11.  
    12.  
    13.             gameOverPanel.SetActive(true);
    14.  
    15.             if (levelTimerInstance.Timer < levelTimerInstance.bestTime)
    16.             {
    17.                 levelTimerInstance.bestTime = levelTimerInstance.Timer;
    18.             }
    19.         }
    20.  
    21.     }
    Code (CSharp):
    1. public class LevelTimer : MonoBehaviour
    2. {
    3.     public Text gameTimerText;
    4.  
    5.     public float Timer;
    6.  
    7.     // float for the best time
    8.     public float bestTime;
    9.  
    10.  
    11.  
    12.     private void Update()
    13.     {
    14.         Timer += Time.deltaTime;
    15.  
    16.         var ts = TimeSpan.FromSeconds(Timer);
    17.  
    18.         int timeInSecondsInt = (int)Timer;
    19.         int minutes = timeInSecondsInt / 60;
    20.         int seconds = timeInSecondsInt - (minutes * 60);
    21.  
    22.         gameTimerText.text = minutes.ToString("D2") + ":" + seconds.ToString("D2");
    23.     }
    24. }
    i find your scripting very useful, however do you show me how to use it?
     
  14. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,336
    JeffDUnity3D likes this.