I have a problem with my game. It is divided in a two states: GameState.MainMenu and GameState.Playing.
I want to draw on screen the timer when I playing. I use gameTime.TotalGameTime.Minutesand
gameTime.TotalGameTime.Seconds variable. But when I clicked on button Play Now, and my game switched state from GameState.MainMenu to GameState.Playing., the timer dosent start from 0. It starts with the time, which elapsed when I spend in MainMenu. I try create next variable to count time, which I spent in MainMenu, and I try to subtract from the first variable, but the displaying time is not properly.
My substracting time:
minutesPlaying = gameTime.TotalGameTime.Minutes; ;
secondsPlaying = gameTime.TotalGameTime.Seconds;
switch (currentGameState)
{
case GameState.MainMenu:
minutesMenu = gameTime.TotalGameTime.Minutes;
secondsMenu = gameTime.TotalGameTime.Seconds;
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
break;
case GameState.Playing:
minutesTotal = minutesPlaying - minutesMenu;
secondsTotal = secondsPlaying - secondsMenu;
break;
}
Invoke my method:
timer.Update(minutesTotal, secondsTotal);
Update method:
public void Update(int min, int sec)
{
string seconds, minutes;
seconds = sec.ToString();
minutes = min.ToString();
if (sec <= 9) seconds = "0" + seconds;
if (min <= 9) minutes = "0" + minutes;
nowString = minutes + ":" + seconds;
}
Thanks for answer :)
I would use a 'StopWatch' to 'Start' and 'Pause' the tracking of time spent in your playing state. When the playing state is entered start the 'StopWatch' and then pause it when leaving that state.
Your on the right track, but I think you should have a time passed variable instead of having to subtract it from the original gameTime, this way if you go back to the menu, then back into the game, your method wont be broken. Basicly it just takes a TimeSpan at 0 and adds gameTime to it as time passes, giving you the amount of time spent in the level.
First off add a new TimeSpan
public TimeSpan TimePassed;
Now in your Update() method inside case GameState.Playing: you will need to incriment the timer.
TimePassed -= gameTime.ElapsedGameTime;
You can also reset the timer to Zero if you need to make a new level (If that applies, Ex: Replaying a level will need a timer reset)
Now to render the time in your Draw() method.
string timeString = TimePassed.Minutes.ToString("00") + ":" + TimePassed.Seconds.ToString("00");
spriteBatch.DrawString(FONT,timeString,POSITION,Color.White);
And there you go!
You should modify time only in Playing state, I have modified the code to achieve it, whithout too much changes... PlayTime keeps the total time elapsed in the playing state.
TimeSpan Elapsed ;
switch (currentGameState)
{
case GameState.MainMenu:
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
Elapsed = TimeSpan.Zero;
break;
case GameState.Playing:
Elapsed = gametime.ElapsedGameTime;
break;
}
Invoke my method:
timer.Update(Elapsed);
Update method:
TimeSpan PlayTime = TimeSpan.Zero;
public void Update(TimeSpan Elapsed)
{
PlayTime.Add(Elapsed);
string seconds, minutes;
seconds = PlayTime.Seconds.ToString();
minutes = PlayTime.Minutes.ToString();
if (sec <= 9) seconds = "0" + seconds;
if (min <= 9) minutes = "0" + minutes;
nowString = minutes + ":" + seconds;
}
Try this:
Before the game starts you don't need to keep track of the time, so I have set minutesPlaying and secondsPlaying to 0 in GameState.MainMenu. Don't initialize minutesPlaying and secondsPlaying within the main Update() function.
minutesPlaying = 0;
secondsPlaying = 0;
switch (currentGameState)
{
case GameState.MainMenu:
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
break;
case GameState.Playing:
secondsPlaying += gameTime.ElapsedGameTime.Seconds;
if (secondsPlaying >= 60)
{
minutesPlaying++;
secondsPlaying = 0;
}
break;
}
When you display the time, just display minutesPlaying and secondsPlaying. You don't need any extra variables.
If that doesn't work, you can always keep track of the milliseconds (secondsPlaying += gameTime.ElapsedGameTime.Milliseconds) that pass when the game starts and divide the value by 1000 to display the seconds.
Related
I am making a game and I added lives and score in it. Everything is ok but something I want to add is that if the score is multiple of 15 then lives increase by 1.
The problem that I am having here is that the lives are increasing per frame and not per second. 60 frames per second, so it is increasing lives by 60 instead of 1.
The part that is working it is in 'update'
public class RobotDodge
{
private Player _Player;
private Window _GameWindow;
private List<Robot> _Robots;
//private int _Lives;
private decimal _Score, _Lives;
private DateTime _Time, currentTime;
public bool Quit{
get{
return _Player.Quit;
}
}
// making our game constructor..
public RobotDodge(){
_GameWindow=new Window("Robot Dodge", 800, 800);
_Player=new Player(_GameWindow);
_Robots = new List<Robot>();
_Lives = _Player._Lives; // player lives are 5.
_Score = 0;
//_Counter = 0;
currentTime = DateTime.Now;
_Time = DateTime.Now;
}
// method that returns our new Robot object..
public Robot RandomRobot(){
float r = SplashKit.Rnd();
Boxy _Boxy=new Boxy(_GameWindow, _Player);
Roundy _Roundy=new Roundy(_GameWindow, _Player);
Boxy_v2 _Boxy_v2=new Boxy_v2(_GameWindow, _Player);
// setting up the probability to return the robot type..
if(r < 0.3){
return _Boxy;
}else if(r > 0.3 && r < 0.6){
return _Roundy;
}else{
return _Boxy_v2;
}
}
public void DrawGame(){
// clearing the window..
_GameWindow.Clear(Color.White);
//draw our robots..
for (int i = 0; i < _Robots.Count; i++){
_Robots[i].Draw();
SplashKit.DrawCircle(Color.Blue, _Robots[i].CollisionCircle);
}
// drawing our player..
_Player.DrawPlayer();
// Number of Lives..
SplashKit.DrawText($"Lives : {_Lives}", Color.Black, "Ariel", 14, 20, 20);
SplashKit.DrawText($"Score : {_Score}", Color.Black, "Ariel", 14, 20, 50);
_GameWindow.Refresh(60);
}
// update method..
public void Update(){
for (int i = 0; i < _Robots.Count; i++){
_Robots[i].UpdateRobot();
}
if (SplashKit.Rnd() < 0.02){
_Robots.Add(RandomRobot());
}
CheckCollisions();
// increase _Time by 1 second..
_Time = _Time.AddSeconds(1);
// increase score by 1 every second..
// the below condition will always be true as the difference between times is always 1 second..
if (_Time.Second - currentTime.Second == 1){
_Score += 1;
}
if (_Score % 15 == 0){
_Lives += 1;
}
}
}
This should resolve. I had to add an update for the current time variable at the top of the loop. I was under the impression you were updating that somewhere else in the code aside from your Robot constructor and I did not want to skew your activity. So add to the first line within the update method.
protected DateTime currentTime = DateTime.Now;
// default to a minimum value
private DateTime _lastTime = DateTime.MinValue;
public void Update()
{
// UPDATE THE TIME HERE based on NOW
currentTime = DateTime.Now;
// just FYI, shortcut readability to loop vs indexing loop.
// An array is an enumerable you can loop through directly
foreach (var oneRobot in _Robots)
oneRobot.UpdateRobot();
// vs what you had
// for (int i = 0; i < _Robots.Count; i++)
//{
// _Robots[i].UpdateRobot();
//}
if (SplashKit.Rnd() < 0.02)
{
_Robots.Add(RandomRobot());
}
CheckCollisions();
// if the first time ever in, just set the last time to whatever current IS and get out.
// you would never increase as score or lives on the first instance as it is basically 0
if (_lastTime == DateTime.MinValue)
{
_lastTime = currentTime;
return;
}
// subtracting one date/time
// creates a "TimeSpan" object which represents the difference between two date/time fields.
// if no full second has completed yet, get out.
if ((currentTime - _lastTime).Seconds < 0)
return;
// a second HAS completed since the last measurement. Update Score and Lives
_Score++;
if (_Score % 15 == 0)
_Lives++;
// NOW, you can adjust, but if its a 1.3 second between cycles, you would be constantly
// getting farther and farther from actual seconds. So what I am proposing is to
// take the last second and just add an absolute one second to it, so when the next
// real Second cycle is complete, it should properly hit again.
_lastTime = _lastTime.AddSeconds(1);
}
I am trying to create a countdown that includes hours.
private int time = 3660;
public MainWindow()
{
var vm = new TimerViewModel();
InitializeComponent();
// get display setting - 2 means extended
int displayType = Screen.AllScreens.Length;
// set the windows datacontext
DataContext = vm;
// set up the timedispatcher
dt.Interval = new TimeSpan(0, 0, 1);
dt.Tick += Timer_Tick;
}
private void Timer_Tick(object sender, EventArgs e)
{
switch(time)
{
case int x when x > 10 && x <= 20:
TimerPreview.Foreground = Brushes.Orange;
time--;
break;
.....................
default:
TimerPreview.Foreground = Brushes.LimeGreen;
time--;
break;
}
TimerPreview.Content = string.Format("00:{0:00}:{1:00}", time / 60, time % 60);
}
I cannot work out how to get the countdown working correctly with hours. It works great with minutes and seconds.
TimerPreview.Content = string.Format("{0:00}:{1:00}:{2:00}", time ???, time ??? 60, time % 60);
I have tried a number of combinations but have failed to find a solution. What am I missing? Many thanks.
Use 3600 (the number of seconds in an hour), and use the modulus operator on the minutes just like you're doing on the seconds (since you want 60 minutes to appear to roll over into a new hour):
TimerPreview.Content =
string.Format("{0:00}:{1:00}:{2:00}", time / 3600, (time / 60) % 60, time % 60);
// 320 -> 00:05:20
// 7199 -> 01:59:59
// 7201 -> 02:00:01
Another (arguably more readable) option would be to use a TimeSpan to handle the formatting:
TimerPreview.Content = TimeSpan.FromSeconds(time).ToString(#"hh\:mm\:ss");
Result when time is 3660:
01:01:00
EDIT: Thanks to #GrantWinney for pointing out that the default string format for a TimeSpan is the same as above, unless the timespan is greater than a day, in which case it includes days as well. So you could just do:
TimerPreview.Content = TimeSpan.FromSeconds(time).ToString();
What i wanted to do is that if i check if counter == 10 then the progressBar will jump by 10's untill 100.
If i will make if counter == 20 then the progressBar should jump by 20's untill 100.
private void NewsUpdate()
{
counter += 1;
progressBar1.Value = (int)Math.Round((counter / 10f) * 100);
label9.Text = counter.ToString();
label9.Visible = true;
if (counter == 10)
{
Extractions();
counter = 0;
progressBar1.Value = 0;
}
}
Im calling this method in a timer tick event the timer1 interval is set to 1000ms
What happen now is that the progrsssBar1 move by 10's getting to 90% after 9 times it's moving back to 0 to value 0. Wht it's not getting to 100% to the end ?
Do this instead:
PprogressBar1.Maximum = YourMaximumValue; // like for example 1,000,000
and then increment by 1
Counter += 1;
ProgressBar1.Value = Counter;
Your formula in your question results in a decimal which is always less than 100 (Like 98.55).
When the counter equals 10, the progress is 100%. However, you're setting the progressBar1.Value = 0; so it never reaches 100% before the UI can update.
I have this method that i call it from a timer tick:
private void NewsUpdate()
{
counter += 1;
progressBar1.Value = counter * 10;
progressBar1.Value = counter;
label9.Text = counter.ToString();
label9.Visible = true;
if (counter == 10)
{
client.Encoding = System.Text.Encoding.GetEncoding(1255);
page = client.DownloadString("http://rotter.net/scoopscache.html");
TextExtractor.ExtractDateTime(page, newText, dateTime);
StreamWriter w = new StreamWriter(#"d:\rotterhtml\rotterscoops.html");
w.Write(page);
w.Close();
TextExtractor.ExtractText(#"d:\rotterhtml\rotterscoops.html", newText, dateTime);
combindedString = string.Join(Environment.NewLine, newText);
counter = 0;
}
}
The progressBar1 is set from 0 to 100.
I want that if I make:
if (counter == 10)
Then the progressBar1 will move to the end after 10 seconds.
If I set it to 50 then it's 50 seconds so the progressBar1 should move 50 seconds until the end.
The timer1 in the designer is set to 1000ms
For example now the way it is:
counter += 1;
progressBar1.Value = counter * 10;
progressBar1.Value = counter;
label9.Text = counter.ToString();
label9.Visible = true;
if (counter == 10)
It will count to 10 the progressBar1 will move by 10 and then will return to the beginning.
I want to that if I change it to 10 50 or 33 the progressBar1 will know to move until the end according to the seconds counted.
If its 33 then count 33 and move the progressBar1 to the end after 33 seconds.
What i want to do is that if i set the IF == 10 then the progressBar will Increment 10 seconds * 10 steps Increment so in 10 seconds the progressBar will get to 100 to the end. Then over again ( this timer is update it should keep running all the time i don't want to stop it i just check if 10 seconds passed make update. And i also want that the progressBar will Increment according to the limit i set in this case 10 untill the end. If its == 10 then the progressBar should Increment each second by 10. And if i set it to == 50 for example then the progressBar should Increment by 2 each s
You want a counter with n steps, where n is the number of seconds to completion.
There are two ways to approach this:
(Preferred) Set the Maximum property of the ProgressBar. This will make everything automatic!
counter++;
progressBar1.Maximum = 33; //This could be set in the designer, or on init
progressBar1.Value = counter;
Calculate the interval yourself. Do this by dividing the counter by the total number of seconds, then multiplying by the range (100 in this case).
counter++;
progressBar1.Value = (int)Math.Round((counter / 33f) * 100);
All of your other code sounds fine based on your comment.
I want to display in label9 a timer counting: Hours Minutes Seconds Milliseconds
Timer1 interval set to 1000
I'm calling this method from the timer1 tick event:
private void NewsUpdate()
{
newText = new List<string>();
counter += 1;
TimeSpan t = TimeSpan.FromSeconds(counter);
string time = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms",
t.Hours,
t.Minutes,
t.Seconds,
t.Milliseconds);
progressBar1.Value = counter;
label9.Text = time;
}
I see the seconds moving counting but the milliseconds stay still on 000 why the milliseconds are not working ?
Set the Interval of the timer to 1 and create TimeSpan object from milliseconds.
Update ur code as follows
timer1.Interval = 1;
private void NewsUpdate()
{
counter += 1;
TimeSpan t = TimeSpan.FromMilliseconds(counter);
string time = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D4}ms",
t.Hours,
t.Minutes,
t.Seconds,
t.Milliseconds);
progressBar1.Value = Convert.ToInt32(counter / 1000);
label9.Text = time;
}
Because you set the timespan directly from the number of whole seconds. Any timespan from an integer number of seconds will always have 0 milliseconds.
What you probably want is to either set your timespan to Date.Now(), or record the current time at timer start and subtract that from the current time on each timer tick.