Simple timer

Hello,

I just start a new game in which you have to score points until the timer go to zero.

So i just write that script, and it works fine.

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

public class TimeCounter : MonoBehaviour {

    public float timeFloat = 120;
    public int timeRemaining;
    public Text Timer;
    public GameObject Player;


    void Start () {
   
    }
   

    void Update () {
   
        timeFloat -= Time.deltaTime;
        int timeRemaining = (int)timeFloat;
        Timer.text = timeRemaining.ToString ();

        if (timeFloat == 0)
        {
            timeFloat = 0;
            Timer.GetComponent<AudioSource>().Play();
            Application.LoadLevel ("Quit menu");

        }
    }


}

But when it comes to zero, it makes a kind of pause and the timer increase his value, there is no sound play and no level loaded.

What do i miss?

Thanks!

Regards, Sam.

Welcome on this forums.

Use code tags when you post codes.

Your timeFloat is never equals to zero (it jumps pass it) but continue to decrease below, therefore your if-block is never executed.
Your condition should be:

if (timeFloat < 0.0f)

thanks! it works.

I would like to know how can i add a waitforseconds without courontines?

So the sound can finish to play before the next scene load.

Your best best is to put your application.loadlevel in a seperate method. Then call it with an invoke which will allow you to declare a set time before it calls the method. You can hard code the value, but you may consider getting the length of your audio file and using that as the time before invoke executes.

If you want to do a lot of stuffs that depend on time, I’d suggest to use Behaviour Tree.
Have a look at Panda BT (www.pandabehaviour.com).

Using this tool, first, you would decompose your script into small and distinct tasks:

GameController.cs

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

public class GameController : MonoBehaviour
{
    public float timeFloat = 120;
    public int timeRemaining;
    public Text Timer;
    public GameObject Player;

    new AudioSource audio;

    void Start()
    {
        audio = Timer.GetComponent<AudioSource>();
    }

    [Task]
    void WaitTimer()
    {
        timeFloat -= Time.deltaTime;
        int timeRemaining = (int)timeFloat;
        Timer.text = timeRemaining.ToString();

        if (timeFloat < 0.0f)
            Task.current.Succeed();
    }

    [Task]
    void PlaySound()
    {
        if( Task.current.isStarting )
            audio.Play();

        if (audio.isPlaying)
            Task.current.Succeed();
    }

    [Task]
    void WaitSoundCompletion()
    {
        if (!audio.isPlaying)
            Task.current.Succeed();
    }

    [Task]
    void LoadQuitMenu()
    {
        Application.LoadLevel("Quit menu");
    }

}

Then chain these tasks into a sequence using a BT script:

tree("Root")
    sequence
        WaitTimer
        PlaySound
        WaitSoundCompletion
        LoadQuitMenu

For someone just starting out trying to make a timer this is beyond overkill. Behavior Trees aren’t the answer to every single coding problem.

1 Like

this, before going to a BT he should learn about coroutines, using invoke and just doing a simple timer with the update and delta time.

I disagree. Implementing this using coroutine or plain Update function would result into more complex code and would require more coding skills.

What would be not accessible to beginner in the example I’ve suggested?

that it is adding a whole entire new system to the process, when it isnt fully needed, and it is just putting off the process of learning the basics to a later date.

Yes, it is indeed an addition of a new system into the process. But this system helps to write simpler code, which makes the live of a beginner easier.

Furthermore, the way a problem is expressed in BT terms is more close to plain english, therefore more intuitive. The OP want to do the following:
“Wait for a timer to reache zero, then play a sound, then wait for the sound to complete and finally load a menu scene”.
This is clearly a sequence of actions:

  • Wait for a timer to reach zero
  • Play a sound
  • Wait for the sound to complete
  • Load a menu scene

And here is the implementation of that sequence in a BT script:

    sequence
        WaitTimer
        PlaySound
        WaitSoundCompletion
        LoadQuitMenu

How could it be simpler than that for a beginner?

It’s like 3 lines of code. How is that more complex than learning and using an entire new library with external configuration files whatever else is involved? OP should learn the basic concepts first. Using a behavior tree just for a timer is a complete waste of time.

1 Like

It’s not just one timer, it’s one timer + playing a sound and wait until it finishes before loading another scene. I’ve proposed a solution to tackle this problem, and it’s not that hard to extend this solution to tackle more advanced problems as well. So I am not only proposing a solution for a specific timer issue, rather, I am proposing a general solution about how to handle tasks that need to be chained in a timely manner.

1 Like

it’s pretty confusing to use it.

@kaisersam
Sorry to hear that, I though it was pretty straightforward.
Where are you confused? Did you get it work?

The interesting thing about game programming is that our flow isn’t usually just line after line of code executed in order. Instead we have to start something off, let it run across multiple frames, keeping track of the state, checking for when we need to transition to the next state, and so on. The logic is spread out over time rather than all sequential like “normal” programming.

One good way of dealing with this is behavior trees. They are very powerful and there is a place for them (and my current work in progress is heavily dependent on them). But they are just a single tool in the whole toolbox.

I mentioned state before for a reason. The trick to all of this is the need for executing a specific piece of code based on the current “state”. You may have heard of State Machine which is another valuable tool in your game programming arsenal. Do some reading about those.

So you can think of state as a step in a process. We can divide your example into three distinct steps. Run The Game, Wait For Audio Completion, Quit To Menu. (we could add a couple more in there, but this will get the point across).

So lets turn your code into a simple state machine. A good way to track the current step we’re on is to use an enum, which is basically a fancy wrapper around a list of numbers that each represent a different value. Our enum will have 3 values that correspond to our three steps: RunGame, WaitForAudio, QuitToMenu.

The basic idea behind the state machine is to execute the appropriate code for the current state, then check things to see if it’s time to switch to the next state or step, then start executing the code appropriate for that step, and so on. A good way to execute code based on a certain value is to use a “switch” statement. Something else for you to read about :slight_smile:

So here’s some code that implements these ideas - it may or may not compile or run as is, but it gives you the basic idea and architecture and will hopefully get you pointed in a right direction.

class Test
{
    enum State { RunGame, WaitForAudio, QuitToMenu }

    public float RunTime = 120;
    public float AudioTime = 5;   // set this to the audio length, or set it in Start by looking up the audio length

    State state;
    float timer;


    void SetState(State newState, float newTimer)
    {
        state = newState;
        timer = newTimer;
    }


    void Start()
    {
        // set the game state to RunGame
        SetState(State.RunGame, RunTime);
    }


    void Update()
    {
        // execute a different method depending on the current game state
        // our initial state is RunGame (set in Start) so we'll be running RunGame initially
        switch (state)
        {
            case State.RunGame:
                RunGame();
                break;

            case State.WaitForAudio:
                WaitForAudio();
                break;

            case State.QuitToMenu:
                QuitToMenu();
                break;
        }
    }


    void RunGame()
    {
        // perform our actions while we're in the RunGame state
        timer -= Time.deltaTime;
        int timeRemaining = (int)timeFloat;
        Timer.text = timeRemaining.ToString();


        // check conditions for moving to the next state
        // in this case, when the timer counts down to 0 then we want to
        // start the audio and then set the state to WaitForAudio - our switch
        // statement above will then start executing the WaitForAudio method
        // next time Update runs
        if (timer <= 0)
        {
            Timer.GetComponent<AudioSource>().Play();
            SetState(State.WaitForAudio, AudioTime);
        }
    }


    void WaitForAudio()
    {
        // perform our actions while we're in the WaitForAudio state
        timer -= Time.deltaTime;

        // check conditions for moving to the next state
        // once the timer runs out we'll switch to QuitToMenu state
        // and our switch statement will start executing that next
        // time it runs
        if (timer <= 0)
        {
            SetState(State.QuitToMenu, 0);
        }
    }


    void QuitToMenu()
    {
        Application.LoadLevel("Quit menu");
    }
}

Look at the switch statement in Update. This is one of the things I really like about state machines - those 3 lines there are almost like looking at “normal” code. A state executes until it’s done, then the next one does, then the next. It’s a really good way of making distributed code like this feel a little more like normal sequential code.

Now, here’s the cool thing. Coroutines are nothing more than beautiful syntactic sugar around that builds an internal state machine for you. The compiler interprets your yield statements as state transitions and builds internal structures automatically to track the current state and transition to the next state and so on.

So for your audio playing and waiting I would recommend just using a Coroutine, which would look something like the following code. You can kind of see the state transitions, and it looks even more like normal sequential code because the compiler is hiding all of the state transition internals from you. It’s beautiful and brings a tear to your eye.

IEnumerator PlayAudioAndQuit()
{
    Timer.GetComponent<AudioSource>().Play();
    yield return new WaitForSeconds(AudioTime);
    Application.LoadLevel("Quit menu");
}

Your current code that starts the audio and loads the level would just be changed to do this…

StartCoroutine(PlayAudioAndQuit());

Coroutines aren’t always the answer either, but they’re perfect for this sort of “start something and wait for it to finish” thing.

Behavior Trees let you do all this sort of thing too, but with a lot more unnecessary overhead for simple things like this. They’re almost like having an entirely new scripting language on top of the C#. Useful and powerful, but not always the correct or most easily understood tool.

1 Like

A lot of good examples. Really it depends on what you plan to do as to what you should choose to use. It’s a good idea to learn what you can do with the base version of unity and then look into assets to see if maybe you need something that can do more in a simpler way.

I only suggest this because you could easily get an asset and then suddenly find it not supported or no longer around down the road and suddenly you’re back on here trying to figure out how to get things to work again.

@Dave-Carlile
I agree that FSM is definitely simpler to learn and to understand than BT; if one does not understand how an FSM works, there is no way one would understand how a BT works. So, if you want to learn in a progressive manner, FSM is prior to BT. But beware that this is just about learning difficulty. BT is not build upon FSM, they are different beasts not to be confused.

I would agree or disagree depending on where you are placing the overhead of BT. You would learn FSM faster than BT and FSM is easier to master. However, on the practical side, compared to BT, using FSM is less simple and more cumbersome. Because you need to explicitly define any single transitions and any modifications to the FSM requires to fiddle with those transitions. Furthermore, the states are interdependent with each others, even more when you define the transitions within the state functions. It is well known that heavily interdependent systems are fragile. On the other side, BT has no explicit transitions: the transitions are defined by the position of a node in the tree, which makes inserting/moving/deleting a node from the tree, a breeze. Also, since the execution flow is defined by the tree itself, there is no need to handle it on the C# side: you don’t have that big switch statement anymore and the transitions are simply absent. Which simplifies the code a lot.

tldr; FSM vs BT? FSM easier to learn, BT easier to use.

Again it depends on the problem you’re trying to solve. Sometimes FSM is the right/easier tool, sometimes BT is, sometimes a bunch of if…then…else statements are. For OP, a behavior tree is beyond the scope of what is needed to solve the stated problem.

And there are various ways to implement FSM as well that don’t require the state machine - you can encode the transitions in data and execute the target method using a delegate. The transitions become a simple loop and dictionary lookup. This is a step towards what the compiler is doing for you when you use a Coroutine.

I appreciate that you’ve made what is probably a good BT asset, but marketing it as the tool to solve every problem under the sun is just that, marketing.

Thanks guys i manage to do it

if (timeFloat < 0)
        {
            timeFloat = 0;
            Destroy (Player);
            GetComponent<AudioSource> ().clip = GameBuzzer;
            GetComponent<AudioSource> ().Play ();
            StartCoroutine ("waitSeconds");

        }
    }

    IEnumerator waitSeconds(){
        yield return new WaitForSeconds (9);
        Application.LoadLevel ("Quit menu");
    }

now the problem concerns the audioclip(time:7sec), it plays just a milliseconds and restart again and again, until the waitforseconds expires and the next level is loaded.

Is there a problem on how i make the audioclip play?

Of course you need to applied the right tool to solve a specific problem.

I don’t know what the OP exactly needs. I saw a problem that could be solved appropriately by BT, therefore I proposed this solution. Personally, since I’ve learned about BT, FSM is a tool of the past. I would have no reason to use it (in a game development context), since using a BT instead is far more simple to obtain the same result. But that’s a personal opinion, you could consider it as a bias, if you want to.

Anyhow an FSM is implemented, you’ll have to deal with the transitions and the state interconnections. That’s intrinsic to the very definition of an FSM. The problem is the same even with drawing FSM with pen and paper.

Of course I want to reach a maximum people. However, I am not putting BT forward because of some foul marketing strategy. I am truly and sincerely convinced that it is a better tool compared to FSM (and coroutine) to define timely logics. That’s it.