Search Unity

[SOLVED] What is Wrong with My Math??

Discussion in 'Scripting' started by ChrisIsAwesome, Jul 30, 2017.

  1. ChrisIsAwesome

    ChrisIsAwesome

    Joined:
    Mar 18, 2017
    Posts:
    181
    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:

    Code (CSharp):
    1. void Update ()
    2. {
    3.      // Update time
    4.      timeMinutes = Time.timeSinceLevelLoad / 60;
    5.      timeSeconds = Time.timceSinceLevelLoad % 60;
    6.      timeCentiseconds = (Time.timeSinceLevelLoad * 100) % 100;
    7. }
    8.  
    9. void LevelComplete ()
    10. {
    11.      // If endTime is better than bestTime...
    12.      if (timeMinutes < bestTimeMinutes)
    13.      {
    14.           // Set bestTime to equal endTime
    15.           PlayerPrefs.SetString ("bestTime", string.Format ("{0}:{1}.{2}", timeMinutes.ToString ("00"), timeSeconds.ToString ("00"), timeCentiseconds.ToString ("00")));
    16.  
    17.           // Update bestTimeMinutes
    18.           PlayerPrefs.SetFloat ("bestTimeMinutes", timeMinutes);
    19.      }
    20. }
    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
     
  2. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    4,602
    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.
     
  3. ChrisIsAwesome

    ChrisIsAwesome

    Joined:
    Mar 18, 2017
    Posts:
    181
    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:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.UI;
    5. using UnityEngine.SceneManagement;
    6.  
    7. /* PURPOSE: To control the actions of the infobar */
    8.  
    9. public class infobarControl : MonoBehaviour
    10. {
    11.     /* GameObject & Component Variables */
    12.     public GameObject ball;
    13.     public GameObject flag;
    14.  
    15.     public Text ballText;
    16.     public Text coinsText;
    17.     public Text timeText;
    18.  
    19.     /* Gameplay Variables */
    20.     public int ballCount;
    21.     public int coinsTotal;
    22.     public int currentLevel;
    23.     public float timeHours;
    24.     public float timeMinutes;
    25.     public float timeSeconds;
    26.     public float timeCentiseconds;
    27.     public float bestTimeMinutes;
    28.  
    29.     public bool levelComplete;
    30.  
    31.     void Awake ()
    32.     {
    33.         // Set starting variables
    34.         coinsTotal = PlayerPrefs.GetInt ("coinsTotal");
    35.         currentLevel = SceneManager.GetActiveScene ().buildIndex;
    36.         timeText.text = "00:00";
    37.        
    38.         bestTimeMinutes = PlayerPrefs.GetFloat ("bestTimeMinutes", 99999999999999);
    39.  
    40.         // Call DetermineBallCount function
    41.         SetBallCount ();
    42.     }
    43.  
    44.     void Update ()
    45.     {
    46.         // Is level complete?
    47.         levelComplete = flag.GetComponent <flagControl>().levelComplete;
    48.  
    49.         // Set coin value & update text
    50.         coinsTotal = PlayerPrefs.GetInt ("coinsTotal");
    51.        
    52.         coinsText.text = coinsTotal.ToString ();
    53.  
    54.         // If level is not completed yet...
    55.         if (!levelComplete)
    56.         {
    57.             // Update time
    58.             timeHours = (Time.timeSinceLevelLoad / 60) / 60;
    59.             timeMinutes = (Time.timeSinceLevelLoad / 60);
    60.             timeSeconds = Time.timeSinceLevelLoad % 60;
    61.             timeCentiseconds = (Time.timeSinceLevelLoad * 100) % 100;
    62.  
    63.             // Set time
    64.             timeText.text = string.Format ("{0}:{1}", timeMinutes.ToString ("00"), timeSeconds.ToString ("00"));
    65.         }
    66.     }
    67.  
    68.     void SetBallCount ()
    69.     {
    70.         // Do currentLevel switch statement
    71.         switch (currentLevel)
    72.         {
    73.             // if on level 1...
    74.             case 0:
    75.  
    76.                 // Set ballCount & update text
    77.                 ballCount = 1;
    78.  
    79.                 ballText.text = "X" + ballCount.ToString ();
    80.  
    81.                 break;
    82.         }
    83.     }
    84.  
    85.     void MinusBallCount ()
    86.     {
    87.         // Subtract 1 from ballCount & update text
    88.         ballCount = ballCount - 1;
    89.  
    90.         ballText.text = "X" + ballCount.ToString ();
    91.     }
    92.  
    93.     void LevelComplete ()
    94.     {
    95.         // Set text for use on results panel
    96.         PlayerPrefs.SetString ("endTime", string.Format ("{0}:{1}.{2}", timeMinutes.ToString ("00"), timeSeconds.ToString ("00"), timeCentiseconds.ToString ("00")));
    97.  
    98.         // If endTime is better than bestTime
    99.         if (timeMinutes < bestTimeMinutes)
    100.         {
    101.             // Set bestTime to equal endTime
    102.             PlayerPrefs.SetString ("bestTime", string.Format ("{0}:{1}.{2}", timeMinutes.ToString ("00"), timeSeconds.ToString ("00"), timeCentiseconds.ToString ("00")));
    103.  
    104.             // Update bestTimeCentiseconds
    105.             PlayerPrefs.SetFloat ("bestTimeMinutes", timeMinutes);
    106.  
    107.             // Debug
    108.             print ("New Best Time!");
    109.         }
    110.     }
    111. }
     
  4. cstooch

    cstooch

    Joined:
    Apr 16, 2014
    Posts:
    354
    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.
     
    Last edited: Jul 30, 2017
    ChrisIsAwesome likes this.
  5. larku

    larku

    Joined:
    Mar 14, 2013
    Posts:
    1,411
    Here's what I use:

    Code (csharp):
    1. public static string formatTime(float time)
    2. {
    3.     TimeSpan t = TimeSpan.FromSeconds(time);
    4.     return string.Format("{0,1:0}:{1,2:00}", t.Minutes, t.Seconds);
    5. }
    6.  
    7. public static string formatTime2(float time)
    8. {
    9.     TimeSpan t = TimeSpan.FromSeconds(time);
    10.     return string.Format("{0,1:0}:{1,2:00}.{2,2:00}", t.Minutes, t.Seconds, t.Milliseconds / 10);
    11. }
    Perhaps useful...
     
  6. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    4,602
    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.
     
    ChrisIsAwesome likes this.
  7. ChrisIsAwesome

    ChrisIsAwesome

    Joined:
    Mar 18, 2017
    Posts:
    181
    SOLUTION!

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

    Code (CSharp):
    1. public float timeMinutes;
    2. public float timeSeconds;
    3. public float timeCenti;
    4. public float totalTime;
    5. public float totalTimeBest;
    6.  
    7. void Awake ()
    8. {
    9.      // Set totalTimeBest value
    10.      totalTimeBest = PlayerPrefs.GetFloat ("totalTimeBest", Mathf.Infinity);
    11. }
    12.  
    13. void Update ()
    14. {
    15.      // If level is not complete...
    16.      if (!levelComplete)
    17.      {
    18.           // Update time
    19.           timeMinutes = Mathf.FloorToInt ((Time.timeSinceLevelLoad / 60));
    20.           timeSeconds = Mathf.FloorToInt (Time.timeSinceLevelLoad);
    21.           timeCenti = Mathf.FloorToInt ((Time.timeSinceLevelLoad * 100) % 100);
    22.           totalTime = Time.timeSinceLevelLoad;
    23.      }
    24. }
    25.  
    26. void LevelComplete ()
    27. {
    28.      // If totalTime is less than totalTimeBest...
    29.      {
    30.           // Update string here
    31.  
    32.           // Update totalTimeBest
    33.           PlayerPrefs.SetFloat ("totalTimeBest", totalTime);
    34.      }
    35. }
    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 :p