Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Complete countdown timer?

Discussion in 'Scripting' started by Jack.H, Apr 28, 2012.

  1. Jack.H

    Jack.H

    Joined:
    Nov 29, 2011
    Posts:
    48
    For my game I am currently using a countdown script that I found in a tutorial. The code is as follows:
    Code (csharp):
    1.  
    2. // the textfield to update the time to
    3. private var textfield:GUIText;
    4.  
    5. // time variables
    6. public var allowedTime:int = 90;
    7. private var currentTime = allowedTime;
    8.  
    9. function Awake()
    10. {
    11. // retrieve the GUIText Component and set the text
    12. textfield = GetComponent(GUIText);
    13.  
    14. UpdateTimerText();
    15.  
    16. // start the timer ticking
    17. TimerTick();
    18. }
    19.  
    20. function TimerTick()
    21. {
    22. // while there are seconds left
    23. while(currentTime > 0)
    24. {
    25. // wait for 1 second
    26. yield WaitForSeconds(1);
    27.  
    28. // reduce the time
    29. currentTime--;
    30.  
    31. UpdateTimerText();
    32. }
    33.  
    34. // game over
    35.  
    36. }
    37.  
    However the code shown above is incomplete and doesn't do anything when it's gets to zero. What would I add to the code so that the level would start again? I know it would be something along the lines of application.LoadLevel(1) but other than that I have no idea where to even start.

    Can anyone help me?
     
  2. RANDOM++

    RANDOM++

    Joined:
    Mar 10, 2012
    Posts:
    68
    I don't see any code that specifies what should happen when time gets at 0, so how you expect something to happen if you don't declare it. For as far I can see, the script only subtracts 1 from the currentTime (every second) as long as the currentTime is greater than 0.
     
    Last edited: Apr 28, 2012
  3. RANDOM++

    RANDOM++

    Joined:
    Mar 10, 2012
    Posts:
    68
    psuedo code:

    if (currentTime <= 0)
    {
    Application.LoadLevel(1);
    }

    In fact its not pseudo hehe, it should work as long as currentTime reaches 0.
     
    Last edited: Apr 29, 2012
  4. Jack.H

    Jack.H

    Joined:
    Nov 29, 2011
    Posts:
    48
    Sorry I'm a complete newbie when it comes to scripting. So I'd just add that on the end of the script I have?
     
  5. RANDOM++

    RANDOM++

    Joined:
    Mar 10, 2012
    Posts:
    68
    You could put that piece of code in your Update function, like I said it should work as long as currentTime actually reaches 0. To check this, you could write a code like this:

    if (currentTime <= 0)
    {
    Debug.Log("current time is 0");
    }

    So basically it's the same condition, only the code that gets executed is different. But this is very easy and useful to check the value of your variables.

    By the way. Use Awake to initialize variables, use something like function Update to actually run code and call functions to be executed. Update gets called every frame, Awake just once at creation of the object (so whenever you press play or instantiate the game object).

    Edit: I actually wrote a little script but it doesn't do what I expect it to do. I tried something similar before and it works but this time the function gets called every frame and the yield statement gets ignored. This is what I did:

    Code (csharp):
    1. public var currentTime : int = 90;
    2.  
    3.  
    4.  
    5. function Update ()
    6.  
    7. {
    8.  
    9.  
    10.  
    11.     CountDown();
    12.  
    13.  
    14.  
    15.     if (currentTime <= 0)
    16.  
    17.     {
    18.  
    19.    
    20.  
    21.         Debug.Log("Time is up my friend");
    22.  
    23.    
    24.  
    25.     }
    26.  
    27.  
    28.  
    29. }
    30.  
    31.  
    32.  
    33.  
    34.  
    35. function CountDown()
    36.  
    37. {
    38.  
    39.  
    40.  
    41.     currentTime = currentTime - 1;
    42.  
    43.     yield WaitForSeconds(1);
    44.  
    45.  
    46.  
    47. }
     
    Last edited: Apr 29, 2012
  6. Bluntweapon

    Bluntweapon

    Joined:
    Feb 24, 2012
    Posts:
    158
    Ok I'll just jack the thread then since RANDOM++ asked for help.

    There's nothing wrong with this code (other than formatting issues, but that's just personal bias, ignore me).

    The function will iterate through the 'while' loop until currentTime is no longer greater than 0, at which point the loop will no longer run. Since code gets executed sequentially, add something after the "game over" comment and it should execute at such a time.

    In code
    Code (csharp):
    1.  
    2. function TimerTick()
    3.    {
    4.       // while there are seconds left
    5.       while(currentTime > 0)
    6.       {
    7.          // wait for 1 second
    8.          yield WaitForSeconds(1);
    9.  
    10.          // reduce the time
    11.          currentTime--;
    12.  
    13.          UpdateTimerText();
    14.       }
    15.  
    16.       // game over <--------------This is where you do stuff
    17.       Application.LoadLevel(0); //Or whichever level you want to load
    18. }
    19.  
     
  7. Bluntweapon

    Bluntweapon

    Joined:
    Feb 24, 2012
    Posts:
    158
    The problem with this particular code is that first of all, the yield statement is after whatever function you wanted to do, so it looks like the program is ignoring the yield statement.

    Second, you're calling CountDown() every frame, which means every frame it counts down the time by 1, then waits for one second. Even if you would flip the instructions, that still fires off the function every frame, so you'd have one second of nothing, and then every frame currentTime would count down by 1. Coroutines are executed at the same time (I assume in separate threads) after all.

    Instead, if we were to go along with your train of thought, we'd have to do something like this
    Code (csharp):
    1.  
    2. public var currentTime : int = 90;
    3.  
    4. function Start(){
    5.      CountDown();
    6. }
    7.  
    8. function CountDown()
    9. {
    10.     while( true ){
    11.         currentTime = currentTime - 1;
    12.  
    13.         yield WaitForSeconds(1);
    14.     }
    15. }
    16.  
    17. function Update(){
    18.     if (currentTime <= 0)
    19.     {
    20.         Debug.Log("Time is up my friend");
    21.     }
    22. }
    23.  
    Alternatively, there's a very nice function to repeatedly call a function for us in Unity

    Code (csharp):
    1.  
    2. public var currentTime : int = 90;
    3.  
    4. function Start(){
    5.     InvokeRepeating( "CountDown", 0, 1 );
    6. }
    7.  
    8. function Update ()
    9. {
    10.     if (currentTime <= 0)
    11.     {
    12.         CancelInvoke( "CountDown" );
    13.         Debug.Log("Time is up my friend");
    14.     }
    15.  
    16. }
    17.  
    18. function CountDown()
    19. {
    20.     currentTime = currentTime - 1;
    21. }
     
    Last edited: Apr 29, 2012
  8. Eric5h5

    Eric5h5

    Volunteer Moderator Moderator

    Joined:
    Jul 19, 2006
    Posts:
    32,401
    Well, no, you're right...it's objectively harder to read and should be fixed.

    No, coroutines aren't threads; all code including coroutines is run in the main thread unless you explicitly create threads yourself with the System.Threading functions. But yes, what's actually happening is that a new instance of CountDown is being launched every frame in Update. The results are no different from just having

    Code (csharp):
    1. function Update () {
    2.     currentTime = currentTime - 1;
    3. }
    except less efficient.

    Almost. The Update function should be removed; there's no reason to check the timer every frame when you're already decrementing it in the CountDown function and can just check it there. Also InvokeRepeating has an issue where in some cases it's called twice in the same frame if the initial timer is 0, so I always use something like .001 instead. Although in this case it should probably be 1 anyway.

    Code (csharp):
    1. public var currentTime : int = 90;
    2.  
    3. function Start(){
    4.     InvokeRepeating( "CountDown", 1, 1 );
    5. }
    6.  
    7. function CountDown()
    8. {
    9.     if (--currentTime == 0)
    10.     {
    11.         Debug.Log("Time is up my friend");
    12.         CancelInvoke( "CountDown" );
    13.     }
    14. }
    --Eric
     
  9. Bluntweapon

    Bluntweapon

    Joined:
    Feb 24, 2012
    Posts:
    158
    Did not know that. Cool, thanks for letting me know.
     
  10. RANDOM++

    RANDOM++

    Joined:
    Mar 10, 2012
    Posts:
    68
    I'd like to thank the both of you for your excellent help. I was confused why this wouldn't work, and in some other case I did almost the same in the update function and the coroutine function got executed perfectly. So I'm still not sure what's the reason for that.

    Now I use this piece of code and yeah it does do exactly what you want it to do

    Code (csharp):
    1. public var currentTime : int = 90;
    2.      
    3.     function Start(){
    4.         InvokeRepeating( "CountDown", 1, 1 );
    5.     }
    6.      
    7.     function CountDown()
    8.     {
    9.         if (--currentTime == 0)
    10.         {
    11.             Debug.Log("Time is up my friend");
    12.             CancelInvoke( "CountDown" );
    13.         }
    14.     }
    But just for my understanding, the InvokeRepeating takes 3 parameters? The function to invoke, the endpoint where the InvokeRepeating function quits, and the time interval?

    And the CountDown function itself, I understand it's constantly checking if currentTime is equal to 0, I think I also understand why you wrote --currentTime. Everytime this function gets called (and the InvokeRepeating function says every second) currentTime gets subtracted by 1. Pretty cool it can be this easy, I wouldn't have come up with this myself :D

    Thanks thanks thanks

    @ Jack, the code provided by Eric works, you just need to change the Debug.Log statement to a Application.LoadLevel. I hope this also works out for you I did my best for you but I'm sorry if I lack some knowledge. At least I learned something after all this :)
     
  11. Bluntweapon

    Bluntweapon

    Joined:
    Feb 24, 2012
    Posts:
    158
    The function, the time between now and the first instance of the invoke, and interval between invoke thereafter

    InvokeRepeating doesn't quit by itself. You'd have to call CancelInvoke somewhere in the same script to stop invoking
     
  12. RANDOM++

    RANDOM++

    Joined:
    Mar 10, 2012
    Posts:
    68
    But ofcourse how could I be this stupid that was pretty obvious but still thanks for pointing out I just would forget something like that :D

    I'm sure I will find tons of uses for these functions because the projects I'm working on rely heavily on time intervals, I cherish every line I have to write less to achieve the same functionality.