Unity2D: How to resume timer even after application has quit - c#

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!

Related

Making a timer in Unity

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

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;

Decrease variable over a specific amount of time

So when my character gets hit by the enemies fire breath, I want to create the feel of the character being set on fire. So while the character is on fire I want him to lose a specific amount of health for a specific amount of time.
For example; lets say he is on fire for 3 seconds and I want to make him lose 30 health for being on fire, how would I evenly distribute losing 30 health for 3 seconds? I dont want the 30 damage to be applied instantly to the health, I want it to slowly tick away at the players health so that at the 3 second mark 30 damage has been dealt.
The game is being made with c#.
Thanks.
This is just like moving Gameobject over time or doing something over time. The only difference is that you have to use Mathf.Lerp instead of Vector3.Lerp. You also need to calculate the end value by subtracting the value you want to lose over time from the current value of the player's life. You pass this into the b or second parameter of the Mathf.Lerp function.
bool isRunning = false;
IEnumerator loseLifeOvertime(float currentLife, float lifeToLose, float duration)
{
//Make sure there is only one instance of this function running
if (isRunning)
{
yield break; ///exit if this is still running
}
isRunning = true;
float counter = 0;
//Get the current life of the player
float startLife = currentLife;
//Calculate how much to lose
float endLife = currentLife - lifeToLose;
//Stores the new player life
float newPlayerLife = currentLife;
while (counter < duration)
{
counter += Time.deltaTime;
newPlayerLife = Mathf.Lerp(startLife, endLife, counter / duration);
Debug.Log("Current Life: " + newPlayerLife);
yield return null;
}
//The latest life is stored in newPlayerLife variable
//yourLife = newPlayerLife; //????
isRunning = false;
}
Usage:
Let's say that player's life is 50 and we want to remove 2 from it within 3 seconds. The new player's life should be 48 after 3 seconds.
StartCoroutine(loseLifeOvertime(50, 2, 3));
Note that the player's life is stored in the newPlayerLife variable. At the end of the coroutine function, you will have to manually assign your player's life with the value from the newPlayerLife variable.
I suppose, what you are looking for is a Coroutine. Check out here and here for the documentation. It will allow you to do your custom health reducing actions separately from update function. Using coroutines you can make something happening by ticks, and you can determine how much time the tick is.
You could use couroutines. Something like this:
void OnHitByFire()
{
StartCoroutine(DoFireDamage(5f, 4, 10f));
}
IEnumerator DoFireDamage(float damageDuration, int damageCount, float damageAmount)
{
int currentCount = 0;
while (currentCount < damageCount)
{
HP -= damageAmount;
yield return new WaitForSeconds(damageDuration);
currentCount++;
}
}
So this is what I ended up doing. It causes the character on fire to lose 30 health and you can see the health ticking down instead of it happening over intervals.
IEnumerator OnFire()
{
bool burning = true;
float timer = 0;
while (burning)
{
yield return new WaitForSeconds(0.1f);
hp -= 1;
timer += 0.1f;
if (timer >= 3)
{
burning = false;
}
}
}

C# GDI Game Loop (FPS Counter)

I'm currently making a 2D Game Engine using C#, GDI and have setup a simple frame cap. The game can only render 60fps.
As far as I know there's no issue with the code however, I would just like a cleaner way of rendering 60fps and no more.
Here's my code, any help would be great
public void Run()
{
window.Show();
window.Focus();
Initialize();
isRunning = true;
canRender = true;
timer = new Stopwatch();
timer.Start();
// the amount of milliseconds needed to pass before rendering next frame
double frameCapCounter = 16.666666;
while (isRunning)
{
Application.DoEvents();
if (window.Focused)
{
if (timer.ElapsedMilliseconds >= frameCapCounter)
{
canRender = true;
frames += 1; // update amount of frames
frameCapCounter += 16.666666; // increment counter
}
else
{
canRender = false;
}
// this is used to check if a second has passed, and if so
// we set the fps variable to the amount of frames rendered
// and reset all variables.
if (timer.ElapsedMilliseconds >= 1000)
{
fps = frames;
frames = 0;
frameCapCounter = 0;
timer.Restart();
}
Update();
LateUpdate();
if (canRender)
Render();
else
{
Thread.Sleep(1);
}
}
}
}
In your scenario, instead of keeping track of time elapsed, frames rendered, etc. etc., you could simply sleep the number of milliseconds to cap your FPS, example:
public void Run()
{
window.Show();
window.Focus();
Initialize();
isRunning = true;
while (isRunning)
{
if (window.Focused)
{
Update();
LateUpdate();
Render();
Thread.Sleep(16); // hard cap
}
}
}
The problem with this, however, is that up to the sleep, the rest of that code could take longer than 16 milliseconds; to this, you could do some performance measurements and do something like 10ms instead.
Another way, keeping a timer active would be something like the following:
public void Run()
{
window.Show();
window.Focus();
Initialize();
isRunning = true;
long limit = 1000 / 60;
while (isRunning)
{
if (window.Focused)
{
timer.Restart();
Update();
LateUpdate();
Render();
while (timer.ElapsedMilliseconds < limit) {
Thread.Sleep(0);
}
}
}
}
This avoids the "hard" cap while also taking into consideration the fact that all code up to the sleep could take longer than 16ms, thus not enter the "wait" loop if the requisite time has passed.
Also, a few notes to consider with your posted code:
First, if you are not using any Windows Form elements (like a PictureBox, Button, TextBox, etc.), and instead are drawing all elements your self (e.g. calling something like Graphics.DrawLine, etc.), you do not need to call Application.DoEvents; doing so causes the calling thread to wait until all other Windows Forms messages have been processed, thus slowing down the render loop. If you do indeed need to call Application.DoEvents it would be better to do it after your Render(), so as to include that function call time in your frame limit.
Lastly, when specifying a Thread.Sleep of anything less than approximately 20 milliseconds on a non-real-time Windows systems, chances are that the Sleep will actually sleep for longer due to the time slice dedicated to a thread on a Windows system. So the Thread.Sleep(1) might sleep for 1 millisecond, OR it might sleep for 6 milliseconds; and if the time cap is already at 14 milliseconds, a 6 millisecond sleep would put your total time upwards of 20 milliseconds, thus slowing your frame rate.
Specifying a sleep of 0 milliseconds relinquishes the remainder of the time slice for the calling thread to another thread of equal priority that is ready to run; if no other threads are ready, then the calling thread continues (so 0-1 milliseconds of wait, vs 6+). You could also use Thread.Yield but that acts much different than Sleep at the kernel level.
Hope that can help.

How to make visual timer go x times slower and how to stop it?

So I am making a game with visual timer that shows current playtime of the player as "MM:SS". Now I've managed to do this just fine, but I haven't yet been to make the time go slower or to stop it when I want to. I am using TimeSpan to show the time in a very similar way as shown in one of this question's answers.
public TimeSpan RunningTime{ get { return DateTime.UtcNow - _started; } }
private DateTime _started;
public void Start()
{
_started = DateTime.UtcNow;
}
and then I read the RunningTime in other script and show it for the player in the GUI. When player presses for example key "a", the game goes into slow motion mode where time and player moves 0.5 times as fast as normally. But I can't get the effect to work in the GUI time text.
Just use Time.timeScale to stop or slow down the time inside your game:
Time.timeScale = 1; // The default value.
Time.timeScale = 0; // Stop the time.
Time.timeScale = 0.5f; // Slower time.
Time.timeScale = 2f; // Faster time.
Then use Time.timeSinceLevelLoad to show the current playtime to the player.

Categories