All I’m trying to do is set a high score to be in a time format, like 00:02.05 where the first 00 is minutes, second 00 is seconds and third 00 is centiseconds (100th of a second). My code:
void Update ()
{
// Update time
timeMinutes = Time.timeSinceLevelLoad / 60;
timeSeconds = Time.timceSinceLevelLoad % 60;
timeCentiseconds = (Time.timeSinceLevelLoad * 100) % 100;
}
void LevelComplete ()
{
// If endTime is better than bestTime...
if (timeMinutes < bestTimeMinutes)
{
// Set bestTime to equal endTime
PlayerPrefs.SetString ("bestTime", string.Format ("{0}:{1}.{2}", timeMinutes.ToString ("00"), timeSeconds.ToString ("00"), timeCentiseconds.ToString ("00")));
// Update bestTimeMinutes
PlayerPrefs.SetFloat ("bestTimeMinutes", timeMinutes);
}
}
The problem is that, at random times, you’ll get a new best time even if your end time is greater than best time. This is so frustrating because I’ve done everything I can think of to fix it.
To be specific: An end time of 00:02.90 overwrote the best time that was 00:02.05. Looking at the minute comparisons, the 2.90 time took less minutes to get to than the 2.05. This is why I’m really confused.
I’d be curious to know more of the code, but as of what you have displayed. You are setting your PlayerPref float equal to the timeMinutes and not the completion time (which should include the entire time with mins, secs, etc. Or, whatever time you record when the level gets completed.
I’m not sure why it would overwrite, because from what I can see you’re only recording minutes in your player pref and you’re checking if timeMinutes is < the best time (which is also minutes)
I compare just the minutes because I tried having total time elapsed and I had the same issue with that. My guess is that the 4+ decimal places is throwing it off with rounding or something so is there any way to not have Unity do time to decimals?
Full script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
/* PURPOSE: To control the actions of the infobar */
public class infobarControl : MonoBehaviour
{
/* GameObject & Component Variables */
public GameObject ball;
public GameObject flag;
public Text ballText;
public Text coinsText;
public Text timeText;
/* Gameplay Variables */
public int ballCount;
public int coinsTotal;
public int currentLevel;
public float timeHours;
public float timeMinutes;
public float timeSeconds;
public float timeCentiseconds;
public float bestTimeMinutes;
public bool levelComplete;
void Awake ()
{
// Set starting variables
coinsTotal = PlayerPrefs.GetInt ("coinsTotal");
currentLevel = SceneManager.GetActiveScene ().buildIndex;
timeText.text = "00:00";
bestTimeMinutes = PlayerPrefs.GetFloat ("bestTimeMinutes", 99999999999999);
// Call DetermineBallCount function
SetBallCount ();
}
void Update ()
{
// Is level complete?
levelComplete = flag.GetComponent <flagControl>().levelComplete;
// Set coin value & update text
coinsTotal = PlayerPrefs.GetInt ("coinsTotal");
coinsText.text = coinsTotal.ToString ();
// If level is not completed yet...
if (!levelComplete)
{
// Update time
timeHours = (Time.timeSinceLevelLoad / 60) / 60;
timeMinutes = (Time.timeSinceLevelLoad / 60);
timeSeconds = Time.timeSinceLevelLoad % 60;
timeCentiseconds = (Time.timeSinceLevelLoad * 100) % 100;
// Set time
timeText.text = string.Format ("{0}:{1}", timeMinutes.ToString ("00"), timeSeconds.ToString ("00"));
}
}
void SetBallCount ()
{
// Do currentLevel switch statement
switch (currentLevel)
{
// if on level 1...
case 0:
// Set ballCount & update text
ballCount = 1;
ballText.text = "X" + ballCount.ToString ();
break;
}
}
void MinusBallCount ()
{
// Subtract 1 from ballCount & update text
ballCount = ballCount - 1;
ballText.text = "X" + ballCount.ToString ();
}
void LevelComplete ()
{
// Set text for use on results panel
PlayerPrefs.SetString ("endTime", string.Format ("{0}:{1}.{2}", timeMinutes.ToString ("00"), timeSeconds.ToString ("00"), timeCentiseconds.ToString ("00")));
// If endTime is better than bestTime
if (timeMinutes < bestTimeMinutes)
{
// Set bestTime to equal endTime
PlayerPrefs.SetString ("bestTime", string.Format ("{0}:{1}.{2}", timeMinutes.ToString ("00"), timeSeconds.ToString ("00"), timeCentiseconds.ToString ("00")));
// Update bestTimeCentiseconds
PlayerPrefs.SetFloat ("bestTimeMinutes", timeMinutes);
// Debug
print ("New Best Time!");
}
}
}
Hmm, looking at the code, it seems like that should work (although, I’m not sure why you don’t store everything as the unchanged float, and why you need to store the formatted time, but that’s no big deal). One question though… after your level completes, does bestTimeMinutes get reloaded for the play-through? If not, then it would be storing an older, higher time, and that would likely cause this problem, as it would think the “bestTimeMinutes” is whatever it was when this script’s awake was called.
Maybe you should do Debug.Log output, even, in your “if (timeMinutes < bestTimeMinutes)” code to spit out what the bestTimeMinutes value is, and the timeMinutes value as well, so you can see better what is going on. I don’t think it’s a rounding issue (at least to that scale), but can’t say for certain.
This is what I was trying to get at. I was thinking the total time before you do any calculations. You just need to know if the amount of time passed is less than before. Then you can do the math for the calculations to compute what you need.
Turns out I was pretty tired last night and was way over-complicating things!
public float timeMinutes;
public float timeSeconds;
public float timeCenti;
public float totalTime;
public float totalTimeBest;
void Awake ()
{
// Set totalTimeBest value
totalTimeBest = PlayerPrefs.GetFloat ("totalTimeBest", Mathf.Infinity);
}
void Update ()
{
// If level is not complete...
if (!levelComplete)
{
// Update time
timeMinutes = Mathf.FloorToInt ((Time.timeSinceLevelLoad / 60));
timeSeconds = Mathf.FloorToInt (Time.timeSinceLevelLoad);
timeCenti = Mathf.FloorToInt ((Time.timeSinceLevelLoad * 100) % 100);
totalTime = Time.timeSinceLevelLoad;
}
}
void LevelComplete ()
{
// If totalTime is less than totalTimeBest...
{
// Update string here
// Update totalTimeBest
PlayerPrefs.SetFloat ("totalTimeBest", totalTime);
}
}
So the Time.timeSinceLevelLoad returns seconds since level loaded to 7 decimal places (which is really as precise as I’d need to get, so it works for me).
I also set the totalTimeBest PlayerPref to infinity by default to ensure that no matter your time, you’ll beat it first time.
This is why you should never code when low on sleep