How to make a timer that ticks down in c# monogame - c#

So I'm trying to make a timer that will ticks down from 30 seconds to 0 in monogame but I don't understand how I can do it. I'm very new to programing. I'm trying to put it into my enum so when the timer hit 0 it will change state from play to gameover.
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed ||
Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
switch (currentGameState)
{
case Gamestate.Start:
timeSinceLastFrame += gameTime.ElapsedGameTime.TotalSeconds;
if (timeSinceLastFrame >= timeBetweenFrame)
{
timeSinceLastFrame -= timeBetweenFrame;
currentFrame.X++;
if(currentFrame.X >= sheetSize.X)
{
currentFrame.X = 0;
currentFrame.Y++;
if (currentFrame.Y>=sheetSize.Y)
{
currentFrame.Y = 0;
}
}
}
posStone.Y = posStone.Y + 1;
if (Keyboard.GetState().IsKeyDown(Keys.Enter))
{
currentGameState = Gamestate.Play;
}
break;
case Gamestate.Play:
break;
case Gamestate.GameOver:
break;
}
}

Method 1
This approach I used in a game where I was storing a future DateTime representing the world game time with a future starting point and updated on every Unity physics frame in my Monobehaviour script. However the general idea can be used in your scenario. If you don't need to store the game time between saves then this might be overkill. Read Method 2 in that case.
You store an end time and a reference time (game time) for the game's current time. You'd want to store both as a class property.
DateTime referenceTime;
DateTime endTime;
Right before you set currentGameState to Gamestate.Play you would want to set these values
referenceTime = DateTime.Now; //Would be assigned from a global GameTime class object or your own custom game time
endTime = referenceTime.AddSeconds(30);
Then you would update your reference time on every physics frame. I used Millisecond instead of Seconds for accuracy (GameTime Documentation) since in most game engines I've worked with frames updates tend to happen in a fraction of a second. This would be placed under Gamestate.Play case branch
referenceTime.AddMilliseconds(gameTime.ElapsedGameTime.TotalMilliseconds);
Then you also would need to do the check in the same case branch (probably after the last code snippet).
if (referenceTime > endTime) {
currentGameState = Gamestate.GameOver;
}
Method 2
GameTime class has an alternative member totalGameTime which is also a System.TimeSpan struct value (GameTime Documentation, TimeSpan Documentation). So this means another option is you can used the total number of seconds from the beginning as the game time. The reference value would be again stored as a class member it is now an int this time since we will store the number of seconds since the game started.
int referenceTime;
You would set the reference time right before you switch currentGameState to Gamestate.Play . There are other ways of doing this but this is probably the simplest.
referenceTime = gameTime.totalGameTime.TotalSeconds;
Then you would add a check in your Gamestate.Play case statement as such. If you plan on repeatedly using gameTime.totalGameTime.TotalSeconds then you might want to store it in another variable for legibility.
if (gameTime.totalGameTime.TotalSeconds > referenceTime + 30) {
currentGameState = Gamestate.GameOver;
}

Have you looked into System.Timer class?
An example of the class is below so you can copy and paste into LinqPad to run and experiment with.
void Main()
{
/* What this does is raise an event every 5 seconds, according to value set in the interval
*/
var timer = new System.Timers.Timer(5000);
timer.Elapsed += OnTimedEvent;
timer.AutoReset = true; // Whether or not the event should be repeated
timer.Enabled = true; // Starts the timer
}
void OnTimedEvent(object sender, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0:HH:mm:ss.fff}",
e.SignalTime);
}

Related

Monogame C# Timer (do something for 15 seconds every 3 seconds)

I am trying to create a timer, which, for example, every 3 seconds during eg 15 seconds will perform an action.
I tried to use gameTime.ElapsedGameTime.TotalSeconds and loop, but unfortunately it doesn't work.
I have an Attack () function that reduces player statistics when an enemy attacks it. I would like that in case of one particular enemy, this function for a specified period of time would subtract player's HP, eg for every 3 seconds. I guess it should be done in the Update function to access gameTime, unfortunately, I have no idea how to do it.
public override Stats Attack()
{
attack = true;
return new Stats(0, -stats.Damage, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}
public override void Update(GameTime gameTime)
{
spriteDirection = Vector2.Zero; // reset input
Move(Direction); // gets the state of my keyborad
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds; // make movement framerate independant
spriteDirection *= Speed; // add hero's speed to movement
position += (spriteDirection * deltaTime); // adding deltaTime to stabilize movement
totalPosition = new Vector2((int)((BottomBoundingBox.Center.X) / 32.0f), (int)((BottomBoundingBox.Center.Y) / 32.0f));
base.Update(gameTime);
}
I will make it simple, so you need to modify my code to achieve your desire result.
My best guess is that you want to have a special effect when your monsters hit your player.
First, you need to check if the monster actually hits the player (if collision is detected):
if (collision)//if it's true
{
// Apply your special effect if it is better than
// the one currently affecting the target :
if (player.PoisonModifier <= poisonModifier) {
player.PoisonModifier = poisonModifier;
player.ModifierDuration = modifierDuration;
}
//player.setColor(Color.Blue);//change color to blue
player.hitPoints -= Poision.Damage;//or enemy.PoisonDamage or whatever you define here
hit.Expire();//this can be for the arrow or bullet from your enemy or simply just a normal hit
}
In your Player class, you need:
public float ModifierDuration {
get {
return modifierDuration;
}
set {
modifierDuration = value;
modiferCurrentTime = 0;
}
}
Then in Update method of Player class:
// If the modifier has finished,
if (modiferCurrentTime > modifierDuration) {
// reset the modifier.
//stop losing HP code is here
modiferCurrentTime = 0;//set the time to zero
setColor(Color.White);//set back the color of your player
}
count += gameTime.ElapsedGameTime.TotalSeconds;//timer for actions every 3s
if (posionModifier != 0 && modiferCurrentTime <= modifierDuration) {
// Modify the hp of the enemy.
player.setHP(player.getCurrentHP() - posionDamage);
//Or change it to every 3s
//if (count > 3) {
// count = 0;
//DoSubtractHP(player);
//}
// Update the modifier timer.
modiferCurrentTime += (float) gameTime.ElapsedGameTime.TotalSeconds;
setColor(Color.Blue);//change the color to match the special effect
}
Hope this helps!
You need to store the start time, or the last time that the action was carried out. Then during each update compare the elapsed time to the stored time. If 3 seconds have passed then perform the action, store the current time and repeat the process.
I do not know monogame, but if I were doing this in one of my C# applications, I would use a timer, and pass in anything that the timer would need to modify.
There is good info here https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer?view=netframework-4.8 and I stole a bit of code from here and modified it as an example to demonstrate my idea. I extended the System.Timer to allow it to run for a duration and stop itself. You can set the frequency and duration and forget about it. Assuming that you are able to update this information from a timer.
class Program
{
private static FixedDurationTimer aTimer;
static void Main(string[] args)
{
// Create a timer and set a two second interval.
aTimer = new FixedDurationTimer();
aTimer.Interval = 2000;
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += OnTimedEvent;
// Start the timer
aTimer.StartWithDuration(TimeSpan.FromSeconds(15));
Console.WriteLine("Press the Enter key to exit the program at any time... ");
Console.ReadLine();
}
private static void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
{
FixedDurationTimer timer = source as FixedDurationTimer;
if (timer.Enabled)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
}
public class FixedDurationTimer : System.Timers.Timer
{
public TimeSpan Duration { get; set; }
private Stopwatch _stopwatch;
public void StartWithDuration(TimeSpan duration)
{
Duration = duration;
_stopwatch = new Stopwatch();
Start();
_stopwatch.Start();
}
public FixedDurationTimer()
{
Elapsed += StopWhenDurationIsReached;
}
private void StopWhenDurationIsReached(object sender, ElapsedEventArgs e)
{
if (_stopwatch != null && Duration != null)
{
if (_stopwatch.Elapsed > Duration)
{
Console.WriteLine("Duration has been met, stopping");
Stop();
}
}
}
}
}
You could see examples of how to pass objects into the timer here (#JaredPar's example) How do I pass an object into a timer event?
string theString = ...;
timer.Elapsed += (sender, e) => MyElapsedMethod(sender, e, theString);
static void MyElapsedMethod(object sender, ElapsedEventArgs e, string theString) {
...
}
One way to do this would be to use coroutines. MonoGame does not have built-in support for them like other game engines, but they are not too complicated to implement yourself. You need some knowledge of the yield keyword and enumerators to understand them, but once abstracted away they make your game code way easier to write and understand.
Here's an example of what your gameplay logic would look using a Coroutine system like the one described below:
public void Attack(Enemy enemyAttacking)
{
if (enemyAttacking.Type == "OneParticularEnemy")
{
StartCoroutine(RunDamageOverTimeAttack());
}
}
// This coroutine starts a second coroutine that applies damage over time, it
// then waits 15 seconds before terminating the second coroutine.
public IEnumerator RunDamageOverTimeAttack()
{
var cr = StartCoroutine(ApplyDamageOverTime());
yield return 15000; // in milleseconds (ms), i.e. 15000 ms is 15 seconds
cr.IsFinished = true;
}
// This coroutine applies the damage every 3 seconds until the coroutine is finished
public IEnumerator ApplyDamageOverTime()
{
while (true)
{
ApplyDamageToPlayer();
yield return 3000;
}
}
The code reads very close to the way you described the actual problem you're trying to solve. Now for the coroutine system...
The StartCouroutine method creates a Coroutine class instance and stores it. During the Update step of the game loop you iterate through the coroutines and update them, providing gameTime to calculate when the next step of the method should run. Each step executes the code in the routine until a yield is found OR until the method ends naturally. Once the coroutine is finished you clear them out. This logic looks something like this:
private List<Coroutine> coroutines = new List<Coroutine>();
public Coroutine StartCoroutine(IEnumerator routine)
{
var cr = new Coroutine(routine);
couroutines.Add(cr);
return cr;
}
public void UpdateCoroutines(GameTime gameTime)
{
// copied in case list is modified during coroutine updates
var coroutinesToUpdate = coroutines.ToArray();
foreach (coroutine in coroutinesToUpdate)
coroutine.Update(gameTime);
coroutines.RemoveAll(c => c.IsFinished);
}
public void Update(GameTime gameTime)
{
// normal update logic that would invoke Attack(), then...
UpdateCoroutines(gameTime);
}
A Coroutine class is responsible for tracking the time remaining between steps of the routine, and tracking when the routine is finished. It looks something like this:
public class Coroutine
{
private IEnumerator routine;
private double? wait;
public Coroutine(IEnumerator routine)
{
this.routine = routine;
}
public bool IsFinished { get; set; }
public void Update(GameTime gameTime)
{
if (IsFinished) return;
if (wait.HasValue)
{
var timeRemaining = wait.Value - gameTime.ElapsedGameTime.TotalMilliseconds;
wait = timeRemaining < 0 ? null : timeRemaining;
// If wait has a value we still have time to burn before the
// the next increment, so we return here.
if (wait.HasValue) return;
}
if (!routine.MoveNext())
{
IsFinished= true;
}
else
{
wait = routine.Current as double?;
}
}
}
This may seem considerably more complex than other solutions provided here, and it may be overkill, but Coroutines allow you to forgo tracking a bunch of state in tracking variables, making complex scenarios easier to follow and cleaner to read. For example, here's a arrow spawning strategy I used Coroutines for in Ludum Dare 37. It spawns 3 arrows 600 milleseconds apart with a 3 second wait between them: https://github.com/srakowski/LD37/blob/477cf515d599eba7c4b55c3f57952865d894f741/src/LD37/GameObjects/BurstArrowSpawnBehavior.cs
If you'd like more social proof of the value of Coroutines take a look at Unity. Unity is one of the more popular game engines, and it has Coroutine support. They describe a scenario where it is useful in their documentation: https://docs.unity3d.com/Manual/Coroutines.html.
I use this for my game :
Public Async Function DelayTask(Time As Double) As Threading.Tasks.Task
Await Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time))
End Function
Converted to C# :
public async System.Threading.Tasks.Task DelayTask(double Time)
{
await System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(Time));
}
You would use it like this in an Async Function :
Await DelayTask(1.5);
The number is in seconds, you can change this by changing the TimeSpan.whateverformat.
Considering that you'll have various things that affect your stats maybe you're better off at having an update subroutine in your Stats class that will check a list of effects that are scheduled to update after one point in time.
This would be better for performance than having each effect relying on its own thread.

Unity - coroutines got inaccurate with timing, want to get around [duplicate]

This question already has answers here:
Coroutines and while loop
(3 answers)
Closed 6 years ago.
I wish to move an object over time using coroutines. I wanted to get an object from point A to point B over say 2 seconds. To achieve this I used the following code:
IEnumerator _MoveObjectBySpeed()
{
while (TheCoroutine_CanRun)
{
if(myObject.transform.position.y <= UpperBoundary.position.y)
{
myObject.transform.position = new Vector3(myObject.transform.position.x,
myObject.transform.position.y + Step, myObject.transform.position.z);
}
yield return new WaitForSeconds(_smoothness);
}
TheCoroutine_CanRun = true;
moveAlreadyStarted = false;
}
and the Step is calculated like
private void CalculateSpeed()
{
Step = _smoothness * allDistance / timeToReachTop;
}
where allDistance is the distance between the bottom and the top boundary.
_smoothness is a fix value. The thing is, the bigger I get this value, the more accurate the time gets to get from bottom to up. Note that a small value here means smoother movement. This smoothness is the time the coroutine waits in between moving the myObject.
The time is measured like this:
void FixedUpdate()
{
DEBUG_TIMER();
}
#region DEBUG TIME
public float timer = 0.0f;
bool allowed = false;
public void DEBUG_TIMER()
{
if (Input.GetButtonDown("Jump"))
{
StartTimer();
}
if (myObject.transform.position.y >= UpperBoundary.position.y)
{
StopTimer();
Debug.Log(timer.ToString());
//timer = 0.0f;
}
if (allowed)
{
timer += Time.fixedDeltaTime;
}
}
void StartTimer()
{
timer = 0;
allowed = true;
}
void StopTimer()
{
allowed = false;
}
#endregion
The results were:
When I wanted the object to reach the top under 1 second and set the _smoothness to 0.01, the time the myObject took to get to the top was 1.67 seconds. When _smoothness was 0.2s, the time to actually reach the top was 1.04s.
So why is this so inaccurate and how to make it work fine?
This smoothness is the time the coroutine waits in between moving the myObject
The mistake you're making is assuming that a co-routine waits the perfect time before executing. Rather, it probably executes on the next frame after the timeout has finished.
Assuming you want smooth motion, you want to move the object every frame (e.g. in Update or co-routine that uses 'yield return null').
Note: Each frame may take a different duration (consider 144fps vs 15fps), and you can discover this in Time.deltaTime . https://docs.unity3d.com/520/Documentation/ScriptReference/Time-deltaTime.html

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!

Cooldown timer for an incremental game

I'm trying to make a game in unity (which uses c#) and what I am trying to accomplish is almost identical to what is done in the game Adventure capitalist. When you click on one of the companies, there is a "cooldown" timer. I put that in quotations because you don't get the money until after the timer has finished. I have looked at the other suggested questions and have managed to create the code below
public UnityEngine.UI.Text showCurrency;
public int money = 0;
public int moneyPerClick = 1;
public float timeToCollect = 3.0F;
private float timeStamp;
private bool buttonClicked;
void Start()
{
timeStamp = Time.time + timeToCollect;
}
void Update()
{
showCurrency.text = "Money: " + money;
if(buttonClicked && timeStamp > 0.0F)
{
timeStamp -= Time.time;
}
if (timeStamp == 0.0F)
{
money += moneyPerClick;
}
}
public bool Clicked()
{
buttonClicked = true;
return buttonClicked;
}
I currently get 1 error but that started happening after I added the showCurrency.text = "Money: " + money; part. So that needs to be fixed.
The code, as far as I can tell, it not working. I don't have the cooldown effect working with the image fill (which will be a problem for another day) So I can't actually see if the timer is counting down, but I guess I could have a Debug.Log and have a system.out line to test that. The other thing that isn't working is I'm not getting the new money amount to show up on screen.
This code is a beginners best guess at how it would be layed out and it is where I'm at. If it looks like I am using the methods wrong, that's probably because I am. Any further information to at least point me in the right direction would be greatly appreciated.
Unity's Update() method gets called every frame. So if you have the game set to 30 FPS, then Update() will get called 30 times every second.
Firstly, timeStamp -= Time.time subtracts the current time from your stored time every single frame (it gets to be a realllly small number really fast). As you have it, try changing your second if statement to an inequality check instead of checking for equality:
if (timeStamp <= 0.0F)
Alternatively, your Update() function could be simplified to something like this:
void Update()
showCurrency.text = "Money: " + getMoney();/*Make money into a property with a getter and setter, so that it will be easily changeable at run-time*/
if(buttonClicked && (Time.time >= timeStamp))
{
timeStamp = (Time.time + timeToCollect);
setMoney(getMoney() + moneyPerClick);
buttonClicked = false; /*Reset your button to be clickable again*/
/*You could also setup your setMoney() like setMoney(moneyPerClick), but that's not what you asked ;b*/
}
}

XNA Elapsed time doesn't update

I am trying to implement a simple counter in my XNA game. Thought this would be simple enough. I have the following code:
elapsed = gameTime.ElapsedGameTime.TotalMilliseconds;
timer -= (int)elapsed;
if (timer <= 0)
{
timer = 10; //Reset Timer
}
But elapsed never changes from 0.0. Am I missing something obvious here? I suspect I am. I have gameTime declared at the top and initialised as usual.
As asked, here is a bit more code:
public class Game1 : Microsoft.Xna.Framework.Game
{
private GameTime zombieTime;
public Game1()
{
zombieTime = new GameTime();
// Other (unrelated) stuff here
}
protected void AddZombie()
{
elapsed = zombieTime.ElapsedGameTime.TotalMilliseconds;
timer -= (int)elapsed;
if (timer <= 0)
{
timer = 10; //Reset Timer
Zombie zombie = new Zombie(ScreenWidth, ScreenHeight, random);
zombie.LoadContent(this.Content, "ZombieSprites/ZombieLeft1");
zombies.Insert(0, zombie);
}
}
protected void Update()
{
AddZombie();
// Other game update stuff here
}
}
I am sorry, I believed the original code snippet would have been enough. I read some pages online where people posted examples of a timer and used the method I have used above. I understand some of the comments made here about the update going fast enough so that elapsed time will always be 0.
You're not using the correct GameTime. zombieTime is never updated by anything so it will always be zero'd out. The GameTime you want to use is passed into the Update() function already for you.
The correct way to do it would be like this:
protected void AddZombie(GameTime gameTime)
{
float elapsed = gameTime.ElapsedGameTime.TotalMilliseconds;
timer -= (int)elapsed;
if (timer <= 0)
{
timer = 10; //Reset Timer
// Rest of stuff goes here
}
}
protected void Update(GameTime gameTime)
{
AddZombie(gameTime);
}
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.gametime_members.aspx
Elapsed game time is the time since the LAST update. TOTAL game time in the cumulative game time...
So, unless you're doing a lot of work you're not showing, you're gonna be taking no time at all to update, so a value of 0 is quite sensible
try shoving a sleep statement in there and see if elapsed time goes up.

Categories