Time.DeltaTime not correctly incrimenting timer?

So… I may just be stupid, but the simplest part of my little game here seems to be malfunctioning and I do not know why.

In short… In my GameController script (tracks everything) I have

void Start(){
float timer = 0.0f;
}

void Update(){
timer += Time.deltaTime;
}

Now the game displays the time it took in seconds at the end by simply setting the value of a text field to “timer.ToString();”, but it’s always way more than the actual time since the scene was loaded.

If my stopwatch times it at, say, 12 seconds - the timer reads over 20. I used my stopwatch to wait exactly 30 seconds, the timer read 120 seconds. I tried to find some kind of consistency to indicate the issue but there doesn’t seem to be any… seems to just be fairly randomly “adding” time as it goes along. I’ve looked into the phenomenon of time drift but I doubt that’s my problem.

EDIT: Alrighty, I’ll post the full code (At least everything that contains this variable. It’s gonna be long lol…

 public GameObject scrollingBackground;
private GameObject tile1, tile2;
private float moveSpeed, tileHeight, horizontalMoveSpeed, distanceTraveled;
public GameObject joystick;
public float maxShipSpeed, horizontalMovementMultiplier = 1.0f;
public GameObject ship;
public GameObject laserPrefab;
public Text speedText, distanceText, scoreText;
private float score;
private float shipHealth, shieldHealth;
public Image healthBar, shieldBar;
public float asteroidDamage;
public float shieldRechargeTimer, shieldRechargeRatePerSecond;
private float timerForShields;
private bool shieldIsRecharging = false;
public GameObject shipDeathExplosion;
private bool shipAlive = true;
public GameObject dataTracker;
-->    private float gameTime;
private static DataManagerScript dataManager;

// Use this for initialization
void Start () {
    dataManager = DataManagerScript.dataManager;
    distanceTraveled = 0;
    tile1 = scrollingBackground.transform.GetChild(0).gameObject;
    tile2 = scrollingBackground.transform.GetChild(1).gameObject;
    tileHeight = tile1.GetComponent<Renderer>().bounds.size.y;
    moveSpeed = 0.0f;
    score = 0;
    shipHealth = 100;
    shieldHealth = 100;
    asteroidDamage = (asteroidDamage == 0) ? 10.0f : asteroidDamage;
    shieldRechargeTimer = (shieldRechargeTimer == 0) ? 5.0f : shieldRechargeTimer;
    shieldRechargeRatePerSecond = (shieldRechargeRatePerSecond == 0) ? 5.0f : shieldRechargeRatePerSecond;
    --> gameTime = 0;
}

// Update is called once per frame
void Update () {

    //Timer
    --> gameTime += Time.deltaTime;

    //HEALTH AND SHIELDS
    //SHIELD RECHARGE
    timerForShields += Time.deltaTime;
    if (timerForShields > shieldRechargeTimer)
    {
        timerForShields = 0;
        shieldIsRecharging = true;
    }
    if (shieldHealth < 100)
    {
        shieldHealth += (shieldIsRecharging) ? shieldRechargeRatePerSecond * Time.deltaTime : 0f;
    }
    shieldHealth = (shieldHealth > 100.0f) ? 100 : shieldHealth;

    //SHIELD AND HEALTH BARS
    shieldHealth = (shieldHealth < 0) ? 0.0f : shieldHealth;
    shipHealth = (shipHealth < 0) ? 0 : shipHealth;
    if (shipAlive)
    {
        if (shipHealth <= 0)
        {
            Destroy(ship.GetComponent<MeshRenderer>());
            Instantiate(shipDeathExplosion, ship.transform.position, new Quaternion(0, 0, 0, 0));
            shipAlive = false;
            
            endGameOnDeath();
        }
    }
        shieldBar.rectTransform.localScale = new Vector3(shieldHealth / 100f, 1, 1);
        healthBar.rectTransform.localScale = new Vector3(shipHealth / 100f, 1, 1);
    

    //END HEALTH AND SHIELDS

    //If Joystick Is Down, Increase Movement Speed
    if (joystick.GetComponent<JoystickScript>().isJoystickDown())
    {
        moveSpeed += joystick.GetComponent<JoystickScript>().Vertical() * Time.deltaTime;
        
    }
    //NO GOING BACKWARDS!
    if(moveSpeed < 0)
    {
        moveSpeed = 0;
    }
    // LIMIT MAX SPEED
    moveSpeed = (moveSpeed > maxShipSpeed) ? maxShipSpeed : moveSpeed;

    // End Increase Movement Speed

    //Horizontal Movement
    if (joystick.GetComponent<JoystickScript>().isJoystickDown())
    {
        horizontalMoveSpeed = joystick.GetComponent<JoystickScript>().Horizontal();
        if (horizontalMoveSpeed > 0)
        {
            if (ship.transform.position.x < 7.5)
            {
                ship.transform.Translate(Vector3.right * horizontalMoveSpeed * horizontalMovementMultiplier * Time.deltaTime);
            }
        }else if(horizontalMoveSpeed < 0)
        {
            if (ship.transform.position.x > -7.5)
            {
                ship.transform.Translate(Vector3.left * horizontalMoveSpeed * horizontalMovementMultiplier * Time.deltaTime * -1);
            }
        }
      }

    //SCROLLING BACKGROUND
    tile1.transform.Translate(Vector2.down * Time.deltaTime * moveSpeed);
    tile2.transform.Translate(Vector2.down * Time.deltaTime * moveSpeed);
     if(tile1.transform.localPosition.y <= -15)
    {
        
         tile1.transform.localPosition = new Vector3(Random.Range(-10.0f, 10.0f) , tile1.transform.localPosition.y + tileHeight, tile1.transform.localPosition.z);
    }
    if (tile2.transform.localPosition.y <= -15)
    {
        tile2.transform.localPosition = new Vector3(Random.Range(-10.0f, 10.0f), tile2.transform.localPosition.y + tileHeight, tile2.transform.localPosition.z);
    }
    //END SCROLLING BACKGROUND

    distanceTraveled += moveSpeed * Time.deltaTime;

   
    //UI STUFF

    speedText.text = "Speed: " + moveSpeed.ToString("N2");
    distanceText.text = "Distance: " + Mathf.Round(distanceTraveled);
    scoreText.text = score.ToString();
}

Here is “endGameOnDeath()” that is called when the ship’s health is <= 0. PreviousGamesLibrary is just a serializable class that holds the score, distance and time of each game which is subsequently stored in a list of PreviousGamesLibrary’s to be saved/loaded.

private void endGameOnDeath()
{
    
    dataManager.saveNewGame(new PreviousGamesLibrary(score, distanceTraveled, gameTime));

    Invoke("backToMenu", 1);
}

As you can see I use quite a bit of Time.DeltaTime, including for a delay in my ship shield’s recharging - which I also timed and also works flawlessly.

The only static anything in this entire project is dataManager which is a single-tin structure class for managing persistent data (I’m not going to pretend I know exactly what that means, I followed a tutorial lol) but that’s the only thing I have declared static in the entire project.

It is quite strange. This might be a really weird bug with unity (highly unlikely), it might be some problems with getting time from your PC. You can also have the variable altered from other components. Maybe the timer is static!?

I see one problem is that the timer is declared in start (float timer = 0).

You can try moving to FixedUpdate and use Time.fixedDeltaTime instead.

To not have any problems. Just do something like startTime = Time.time in Start function, and when you update the UI get the time as Time.time - startTime

I wouldn’t use Time.deltaTime to measure time any longer than, like, 1 second, because floats do not have enough digits/figures to be accurately added over hundreds of frames. You’re bound to be losing or gaining something due to rounding. Edit: I thought wrong. Ignore that paragraph. Edit 2: If you are running a server with high update frequency (not limited to 60 fps), then perhaps that paragraph may help, so I have kept it in this answer.

I would use Time.time or Time.unscaledTime instead. Or even Time.realtimeSinceStartup. For example,

float timeAtStart;
float timer;
void Start() {
    timeAtStart = Time.time;
    timer = 0;
}
void Update() {
    timer = Time.time - timeAtStart;
}

// at end of game
(Time.time - timeAtStart).ToString();
// or just
timer.ToString();

Note that this is untested.

Just want to say that I also had this problem, and the issue WAS what ElijahShadbolt mentioned.
You should unstroke that paragraph lol, because that is what actually helped me resolve this.

The thing is that I had this issue on a Unity Server build (linux), so since there are no graphics, the frame rate was something crazy like 50000, which did make the Time.deltaTime extremely small for a float.
And as a result, the time was moving much slower than supposed.

I solved this issue with :

Application.targetFrameRate = 60;

In the Start function of my main manager.