[SOLVED] What is Wrong with My Math??

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.

Any help would be greatly appreciated!

- Chris

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)

Can you show a bit more code.

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.

1 Like

Here’s what I use:

public static string formatTime(float time)
{
    TimeSpan t = TimeSpan.FromSeconds(time);
    return string.Format("{0,1:0}:{1,2:00}", t.Minutes, t.Seconds);
}

public static string formatTime2(float time)
{
    TimeSpan t = TimeSpan.FromSeconds(time);
    return string.Format("{0,1:0}:{1,2:00}.{2,2:00}", t.Minutes, t.Seconds, t.Milliseconds / 10);
}

Perhaps useful…

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.

1 Like

SOLUTION!

Turns out I was pretty tired last night and was way over-complicating things! :sweat_smile:

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 :stuck_out_tongue: