Coroutine onscreen countdown timer?

Ok, so i’m trying to display a countdown timer onscreen from my Coroutine’s “yield return new WaitForSeconds”. The code works but the timer is erratic, goes fast then slow then fast. I’m new to C# so I don’t know much, but if I had to guess I think the problem is it’s counting down on every frame and not in real time. I know this means i should use “Time.deltaTime”, but i’m just not sure how to impermanent it into my script.

using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class PickupManager : MonoBehaviour
{
    public GameObject _camara;
    public Image tripIcon;
    public Image shieldIcon;
    public Image speedIcon;
    public Text tripTimer;

    public float tripTime = 10f;

    private SENaturalBloomAndDirtyLens bloomScript;



    public void Start()
    {
        bloomScript = _camara.GetComponent("SENaturalBloomAndDirtyLens") as SENaturalBloomAndDirtyLens;
    }

    public void IsTripping()
    {
        StartCoroutine(Tripping());
    }

    public IEnumerator Tripping()
    {
        while (tripTime > 0f)
        {
            yield return new WaitForSeconds(1f);
            tripTimer.enabled = true;
            tripTimer.text = tripTime.ToString();
            bloomScript.bloomIntensity = 0.3f;
            bloomScript.lensDirtIntensity = 0.8f;
            tripIcon.color = new Color32(0, 33, 255, 255);
            tripTime -= 1f;
        }
        bloomScript.bloomIntensity = 0.05f;
        bloomScript.lensDirtIntensity = 0.05f;
        tripIcon.color = new Color32(0, 33, 255, 100);
        tripTimer.enabled = false;
    }

}

Are you calling IsTripping() from another script? If so, can you show the code that is calling it?
May also want to move

 yield return new WaitForSeconds(1f);

next to the ending brace in the while loop. This way, the text is updated before counting down to the next update.

Yes “IsTripping()” is called from another script.

using UnityEngine;
using System.Collections;

public class TripPickup : MonoBehaviour
{
    public GameObject tripParticleEffect;
    public PickupManager pickupManager;

    private bool isTriggered;
    private AudioSource tripSound;

    void Awake()
    {
        tripSound = gameObject.GetComponent<AudioSource>();
    }

    void Update()
    {
        if (isTriggered && !tripSound.isPlaying)
            Destroy(gameObject);

    }

    void OnTriggerEnter(Collider col)
    {
        if (col.gameObject.name == "Player")
        {
            isTriggered = true;
            pickupManager.IsTripping();
            tripSound.enabled = true;
            gameObject.GetComponent<MeshRenderer>().enabled = false;
            Instantiate(tripParticleEffect, gameObject.transform.position, Quaternion.LookRotation(Vector3.up));
        }
    }
}

I also tried moving

 yield return new WaitForSeconds(1f);

but it still counts down erratic.

EDIT:
Problem solved. It seems moving

 yield return new WaitForSeconds(1f);

right before

tripTime -= 1f;

Now it counts down like it should. Thanks for the help.

Moving it would not affect the countdown…it just results in behavior that is expected. Your previous code was waiting 1 second before even showing the first number of the countdown. It should follow that the first number should be displayed before waiting a second, then the countdown can continue.

Place a debug.log in the OnTriggerEnter event. I have a feeling that IsTripping() is called multiple times. This may explain the counter behavior. Short of this, the code looks correct. I don’t see why timer should not work as expected.

You are absolutely correct, it was getting triggered twice. The problem was

    void Update()
    {
        if (isTriggered && !tripSound.isPlaying)
            Destroy(gameObject);
    }

the delay from waiting for the audio to finish before destroying, was causing multiple collisions. So I rewrote the script using a different approach.

using UnityEngine;
using System.Collections;

//Make sure there is always an spherecollider and audiosource component on the GameObject where this script is added.
[RequireComponent(typeof(SphereCollider), typeof(AudioSource))]
public class TripPickup : MonoBehaviour
{
    public GameObject tripParticleEffect;
    public PickupManager pickupManager;
    public AudioClip tripSound;


    // Prevent audio from auto playing.
    void Start()
    {
        GetComponent<AudioSource>().playOnAwake = false;
    }

    IEnumerator OnTriggerEnter(Collider col)
    {
        if (col.gameObject.name == "Player")
        {
            //The script that we are calling it from.
            pickupManager.IsTripping();
            Instantiate(tripParticleEffect, gameObject.transform.position, Quaternion.LookRotation(Vector3.up));
            //Prevent furthur collisions & make invisible.
            GetComponent<Collider>().enabled = false;
            GetComponent<Renderer>().enabled = false;
            //Play the audio clip & wait before destroying it.
            GetComponent<AudioSource>().PlayOneShot(tripSound);
            yield return new WaitForSeconds(tripSound.length);
            //Destroy it after the audio clip is done.
            Destroy(this.gameObject);
        }
    }
}

Now it’s smooth as butter, thanks for pointing me in the right direction.

1 Like