Making a timer in Unity - c#

I have a problem in Unity. I need a countdown in my project. But this countdown will count 2 times. For example, 3 seconds a job will be done, 2 seconds another job will be done and these works will continue.

Couroutines (and more Coroutines)
Coroutines do exactly the thing you want to do.
private IEnumerator Countdown2() {
while(true) {
yield return new WaitForSeconds(2); //wait 2 seconds
//do thing
}
}
private IEnumerator Countdown3() {
while(true) {
yield return new WaitForSeconds(3); //wait 3 seconds
//do other thing
}
}
You then start them off by calling in your Start() method (or where ever):
void Start() {
StartCoroutine(Countdown2());
StartCoroutine(Countdown3());
}
Note that both countdown methods will do their thing forever unless one of three things happens:
StopCoroutine(...) is called, passing in the reference returned by StartCoroutine
The countdown function itself returns (which will not happen unless it breaks out of the infinite while(true) loop)
The coundown function itself calls yield break
Also note that in the event both coroutines should resume at the same time (e.g. at 6 seconds) coroutine 2 will execute first (as it was started first), unless some other effect intervenes (e.g. one of the loops has another yield instruction, one of the loops is terminated, etc).

Based in the example of use you gave where an object first turn right, then turns left:
consider an object. When the program start, turn right for 3 seconds,
after turn left for 1 second. they will repeat continuously. two
counters will follow each other.
Below I give you two timers which are executed sequentially: first one will last 3 seconds and the second one other one 2 seconds. Once one counter finish the other one will starts, this will be repeated in an infinite loop.
float counterTask1 = 3f;
float counterTask2 = 2f;
float timer;
bool chooseTask;
void Start(){
//Initialize timer with value 0
timer = 0;
chooseTask = 1;
}
void Update ()
{
// Add the time since Update was last called to the timer.
timer += Time.deltaTime;
// This will trigger an action every 2 seconds
if(chooseTask && timer >= counterTask1)
{
timer -= counterTask1;
chooseTask = 0;
#Do Task 1 Here
}else if(!chooseTask && timer >= counterTask2)
{
timer -= counterTask2;
chooseTask = 1;
#Do Task 2 Here
}
}
I am not sure if this is what you were looking for. In any case there are lot of questions already asked about timers in Unity. Check some of them:
https://stackoverflow.com/search?q=timer+in+Unity

Related

In while loops list compare of coroutine not work sometimes - C# Unity3D Rhythm Game

I'm going to make a rhythm game.
music playing time is variable which change by music playing in millisecond
List is a float list which have the time(float second) I filled , let me to access it and use for compare with music playing time to instance object .
when list time = music time then instant object.
but sometimes will missing(not found value in list but value is really exist in list).
Here is my assumption.
I think List.indexOf performance are not good and thread have some delay.
Music playing time change every millisecond , a little delay from unity or others will cause missing(doesn't entering if statement)
I don't know if it's correct.
Could any one help me.
Here is my code.
IEnumerator Execute(MethodDelegate Start,MethodDelegate Stop)
{
while (true) {
int res = result.IndexOf ((float)System.Math.Round (GameObject.Find ("Music").GetComponent<AudioSource> ().time, DigitalAdjust)-TimeAdjust);
if (res!=-1) {
if (res == result.Count-2) {
Stop.Invoke ();
print ("CoroutineStop");
StopCoroutine (_Execute);
}
//execute
num=Positoin[res];
print (res);
Start.Invoke();
}
yield return null;
}
}
Thanks.
Chances are that you are correct. You might miss some if statement because you don't match the millisecond exactly. Here are some things that could help:
It you game reaches 60 FPS (the usual rate for a smooth rendering), each frame will take around 16 milliseconds. If you have events that must trigger at exact milliseconds you will miss some because your Execute function calls are separated by around 16ms (a coroutine is called once per frame).
A solution to this is remember the last time the Execute function was called and check everything in between:
private float _lastTime;
IEnumerator Execute(MethodDelegate Start,MethodDelegate Stop)
{
while (true) {
// here your code must check all the event between _lastTime and Time.time
var lastTimeMilliseconds = (int)(_lastTime*1000);
var currentTimeMilliseconds = (int)(Time.time*1000);
for(int time = lastTimeMilliseconds+1; time <= currentTimeMillisedons; time++)
{
// let's say last frame was at time 1000ms
// This frame occurs at time 1016ms
// we have to check your list for events that have occured since last frame (at time 1001, 1002, 1003, ...)
// so we have a for loop starting at 1001 until 1016 and check the list
int res = result.IndexOf ((float)System.Math.Round (time, DigitalAdjust)-TimeAdjust);
if (res!=-1)
{
if (res == result.Count-2)
{
Stop.Invoke ();
print ("CoroutineStop");
StopCoroutine (_Execute);
}
//execute
num=Positoin[res];
print (res);
Start.Invoke();
}
}
// At the end, remember the time:
_lastTime = Time.time;
yield return null;
}
}
Check the Time class, you also have access to Time.deltaTime to know the time elapsed between two frames, if that helps.
EDIT:
As you requested in comment, I added some bit of code from your example to explain better this idea. Note that I don't know what your variables do so you will have to adapt. For instance, Time.time gives that time since app start. You will likely need to adapt this use the time since you started the audio
Another important thing:
GameObject.Find must look in all the objects. It is really slow and shouldn't be used every frame
GetComponent looks for all your scripts and is slow as well. It shouldn't be used every frame.
Instead, do this:
private AudioSource _audioSource;
private void Start()
{
_audioSource = GameObject.Find ("Music").GetComponent<AudioSource> ();
}
This will retrieve the source only once. Then in your code you simply call
_audioSource.time;

Unity2D: How to resume timer even after application has quit

basically I have a countdown timer with in my game. What I basically want to do is continue my timer playing even if I close out of my application and play it again, I want the timer to continue counting down. Kind of like clash of clan when the counter is still work when the app is closed. For example: if I exit the game and the timer is on 1:30 (1 minute, 30 seconds). Then if I restart the game 30 seconds later, the timer should show 1:00 (1 minute, 0 seconds) Or if I close the game 30 seconds later, the timer should show 1:00 (1 minute, 0 seconds)
So far this is as far as I've got:
public class TimeManager: MonoBehaviour {
public Text timer;
float minutes = 5;
float seconds = 0;
float miliseconds = 0;
public int curHealth;
public int maxHealth = 3;
void Start ()
{
curHealth = maxHealth;
}
void Awake ()
{
if (PlayerPrefs.HasKey("TimeOnExit"))
{
var x = DateTime.Now - DateTime.Parse(PlayerPrefs.GetString("TimeOnExit"));
PlayerPrefs.DeleteKey("TimeOnExit");
}
}
void Update(){
if(miliseconds <= 0){
if(seconds <= 0){
minutes--;
seconds = 59;
}
else if(seconds >= 0){
seconds--;
}
miliseconds = 100;
}
miliseconds -= Time.deltaTime * 100;
//Debug.Log(string.Format("{0}:{1}:{2}", minutes, seconds, (int)miliseconds));
timer.text = string.Format("{0}:{1}", minutes, seconds, (int)miliseconds);
}
private void OnApplicationQuit()
{
PlayerPrefs.SetString("TimeOnExit", DateTime.Now.ToShortTimeString());
}
}
TimeOnExit is good but you also need to store either the target time or remaining time of each countdown timer (in case you have more than one - e.g., multiple Woodcutters producing Wood).
Then, in the startup code for the game, you need to run through your game loop once per second that the user was absent. If a timer would have triggered an event, you trigger that event in this loop (Woodcutter adds 1 Wood to Barn).
If you've ever seen a game with a progress bar at startup, there's a good chance that's part of what's going on.
Depending on your game and how long the user was gone, you might need to simulate multiple iterations of the same timer (Woodcutter adds 1 Wood to Barn and then starts working on the next Wood - over and over until the time is caught up).
Finally, you would need to re-instantiate the actual timers that existed and need to continue during live play. Be sure to figure out where they are within the 5-minute loop. If all of the timers are off by a few seconds, nobody will notice but if a restart means they all line up, that will seem strange.
Once you get sophisticated, there are techniques that are faster than a second-by-second (or whatever time period makes sense for your game) simulation of the time that went by but that's a good place to start.
Here's a hint: If you store an array of the target times and what event should trigger, it could be easy enough to cycle through the ones that are in the past and trigger them in order. Be sure to insert new target times into the array for repeating events.
Good Luck!

Unity coroutine does not work

I don't think I fully understand coroutines, it doesn't work the way I wanted, so I need help.
I've got a memory game (Simon like) that consist of 4 squares that randomly switches on and off. After square switches on/off it should take a little break, before switching next button, which my program does not seem to do so. For the switching process I use blink*Colorname coroutine which is following:
foreach (int color in pattern)
{
switch (color)
{
case 0:
StartCoroutine (blinkGreen (blinkSeconds));
break;
case 1:
StartCoroutine (blinkRed (blinkSeconds));
break;
default:
break;
}
}
//to do: pause function between button blinks
IEnumerator blinkGreen (float seconds)
{
greenImg.color = Color.white;
yield return new WaitForSeconds (seconds);
greenImg.color = Color.green;
}
I've tried using waitforseconds at 2 places to achieve my goal: First, at blink*Color as following:
IEnumerator blinkGreen (float seconds)
{
greenImg.color = Color.white;
yield return new WaitForSeconds (seconds);
greenImg.color = Color.green;
yield return new WaitForSeconds (seconds);
}
Second, after the the loop, under //to do: pause function between button blinks by calling another coroutine:
StartCoroutine(waitfornexttransition(5.0f));
IEnumerator waitfornexttransition (float second)
{
yield return new WaitForSeconds (second);
}
Am I missing something? All suggestions and helps are appreciated. Thanks!
Unity does not wait for the completion of the started coroutine to continue code execution. If you type (psuedocode):
print "1"
start coroutine that prints "2" after a second
print "3"
the output will be: "1", "3", "2".
If you put all your code in one coroutine, everything will run sequentially as expected.
Well, since it's all in a foreach loop anyway and you're starting a new couroutine for each iteration, then basically all the coroutines are starting at the same time within the same frame. They will then all run concurrently regardless of what the rest of your code is doing, blinking all lights simultaneously.
If you want your code to wait for a certain amount of time between executions, then make a timer and only blink the lights when the timer hits 0. A basic example would be:
private float timer;
void Start()
{
timer = 2f; // Waiting 2 seconds between blinks
}
void Update()
{
timer -= Time.deltaTime; // Consistently decrement timer independently of frame rate
if (timer <= 0)
{
blinkLight();
timer = 2f;
}
}
void blinkLight()
{
// Light blinking logic goes here.
}
Your foreach loop should also be inside a coroutine and you can put a yield return new WaitForSeconds (seconds) before each iteration.

C# : How couroutine stop the loop

I want to understand the concept of coroutine i don't know why code stop at when it print 1,2,3.But in this code loop should run 30 times and print value 1 to 30.
public class NewCore : MonoBehaviour
{
void Start ()
{
StartCoroutine (MyCoroutine (0.52f));
StartCoroutine (CoroutineKiller (2f));
}
IEnumerator MyCoroutine (float delay)
{
int value = 0;
while (value < 30)
{
yield return new WaitForSeconds (delay);//wait
value ++;
print (value);
}
StartCoroutine (MyCoroutine (delay));
}
IEnumerator CoroutineKiller (float delay)
{
yield return new WaitForSeconds (delay);
StopAllCoroutines ();
}
}
You are printing values from 1 - 30 with delay of 0.52sec, but after 2 seconds you stop doing so (you call StopAllCoroutines). Which is why you are only seeing 1, 2 and 3 printed out.
Try to comment out StartCoroutine (CoroutineKiller (2f));, or give it more delay to see how it controls the flow stop.
Coroutines are similar to threads - although not really - but yes in the sense that calling StartCoroutine does not block the code.
So when you're doing this:
void Start ()
{
StartCoroutine (MyCoroutine (0.52f));
StartCoroutine (CoroutineKiller (2f));
}
It will actually start both coroutines and execute them step by step, side by side, on each Update call.
If you wish to execute the coroutines one after another, try the following:
void Start()
{
StartCoroutine(F());
}
IEnumerator F()
{
// by yielding the Coroutine, it will block the execution of
// this coroutine (F) until MyCoroutine finishes
yield return StartCoroutine(MyCoroutine(1.52f));
// This is no longer doing much, really.
yield return StartCoroutine(Killer(2f));
print("done");
}
You can read more about the Execution Order which also includes information about coroutines and how the yield system actually works.
It seems you using StopCoroutine(MyCoroutine()); in CoroutineKiller and it stops coroutine after 2 seconds. (your parameter is 2f. And I think you used it for WaitForSeconds). So your MyCoroutine function working just 3 delay ( 0.52 + 0.52 + 0.52 =1.56) and before fourth delay ends, you stop this coroutine and you get just 1,2,3 printed.

Why doesn't my program wait like I need it to?

Ok pretty much what I am trying to do is have my program wait a predetermined amount of times then move the character to another spot on the grid (which is notated by the "panel_x" and "panel_y" variables). Instead it waits and then moves the character around every frame...Im not sure what I am doing wrong. I believe I need a coroutine, but I may be wrong.
//How I am calling the coroutine
void Update()
{
if(HP != 0)
{
StartCoroutine(Wait());
}
}
//The Coroutine I need to run to move my character
//around...I need this to run until the character's
//hp reaches 0.
IEnumerator Wait()
{
while(true)
{
//I need it to wait...
yield return new WaitForSeconds(3);
//Then move the character to another
//grid...
panel_x = Random.Range(1, 4);
panel_y = Random.Range(1, 4);
}
}
Update runs on every frame. What's happening here is that you're calling Wait() on every frame which will run multiple instances in an infinite loop.
I believe what you're trying to do is change the x and y values every 3 seconds.
If so, try something like this.
float timer = 0.0f;
void Update()
{
if (HP != 0)
{
timer += Time.deltaTime;
if (timer >= 3)
{
panel_x = Random.Range(1, 4);
panel_y = Random.Range(1, 4);
timer = 0;
}
}
}
Time.deltaTime returns the time passed between frames so it can be used as a timer by summing it every frame. When 3 seconds has passed, we reset the timer to 0 and run our method.
Did you modify that HP ever after? If HP is not 0, then the StartCoroutine(Wait()) will be called every time Update() is executed. That is why you get the weird result.
Your function doesn't seem to take any parameters, so I can provide you other method like Invoke as an alternative.
Invoke(x, y) executes a function after a certain amount of seconds delay. In your case for example:
void Update(){
if(HP != 0)
Invoke("MoveChar", 3); //execute MoveChar() after 3 seconds
}
MoveChar(){
panel_x = Random.Range(1,4);
panel_y = Random.Range(1,4);
}
Just for your info, there is also InvokeRepeating(x, y, z) which maybe you won't need in this case.
It repeats every z seconds after function x being called with y delay.
void Start(){
//execute MoveChar() after 3 seconds and call the
//function again after 0.5 seconds repeatedly
InvokeRepeating("MoveChar", 3, 0.5);
}
MoveChar(){
panel_x = Random.Range(1,4);
panel_y = Random.Range(1,4);
}

Categories