so I've just made a simple snake game in WinForms and I've only got one problem left. The idea is that I'm gonna implement a power-up that speeds up the player for 10 seconds. I've tried using thread. sleep method to create a timer but that sleeps the entire program. I've also tried playing with the task.delay method but without success.
Am I going about this problem the wrong way or am I just missing something? Do you have any tips to get me on the right path? I'm quite new to this so this might be an easy fix.
var watch = new Stopwatch();
watch.Start();
using (var task = Task.Delay(10000))
{
speedUp = true;
task.Wait();
}
watch.Stop();
speedUp = false;
This is my attempt at the delay. But it seems to pause the entire program for 10 seconds before it continues.
Thanks
You may want to check the past time by looking at a clock within your game loop. The property Environment.TickCount provides you with the milliseconds since the last boot or you could do it with DateTime or Timespan. Now instead of sleeping you could check if 10'000 milliseconds have past since the activation of the power-up. This way nothing will get blocked and collecting multiple power-ups within 10 seconds won't screw up the deactivation process.
Related
Is there a way to run the Timer so that it starts at exact full second?
stateTimer = new Timer(someCallback, null, 0, 1000);
This one will start right away, repeating each second, but the problem is it will start exactly when I run the program which may result in 13:14:15.230 start.
I would like 13:14:15.000 start. Something like:
stateTimer = new Timer(someCallback, null, DateTime.Now.Date, 1000);
Is that possible?
EDIT:
After doing the console log:
Console.WriteLine($"Time: {DateTime.Now.ToString("HH:mm:ss.fff")}");
I've noticed that 1 second interval actually gets incremented by more than one second (about 1,02 each iteration) so after 50 iterations one second is skipped. I have solved my problem by making timer run each 800ms instead. Not ideal solution, but it works, and second never gets skipped (and I have no problem in triggering the same second twice).
stateTimer = new Timer(someCallback, null, 0, 800);
No that is not possible. That would require you to exactly know when the CPU thinks it is a good idea to start to execute your timer code. Even if you would Thread.Sleep for the remaining time, it would still not mean the code is executed at the very millisecond you want to.
Instead, if you just want to do this for formatting, you can use a custom date/time format to do that.
I had to do something similar in a program that displayed the time and displayed the number of minutes and seconds until the user could do something.
The original code used a 1000 ms timer. This had the problems described by the original poster. I created a thread. The thread had a loop:
private ManualResetEvent myThreadRequestExit = new ManualResetEvent(false);
while (!myThreadRequestExit.WaitOne(1000 - DateTime.UtcNow.Millisecond))
{
// Do the one-second work here
}
The loop will exit and the thread will terminate when MyThreadRequestExit is set.
Although it may fire a few milliseconds after the start of the second it is close enough that the user perceives the timer as ticking when it should and it does not lose seconds as long as the work can be done in less than a second.
Just add this line before you start your timer. Sweet and simple :)
Thread.Sleep(1000 - DateTime.Now.Millisecond);
I write a c# program to count down 5 seconds. What I do is:
new Task(() =>
{
try
{
this.Invoke((Action)delegate()
{
label1.Text = "5"; // 4, 3, 2..etc
});
}
Thread.Sleep(1000);
}
catch
{
// form could be disposed
break;
}
}
}).Start();
This works on my PC, however, when I copied the program to a window tablet, the Thread.Sleep(1000) gives more than 1 second. In other words, it takes more than 5 seconds (in fact more than 10 seconds) to update the label from 5,4,3,2,1. Alternatively, it takes too long to update label1.Text = "5", etc? It does not make sense to me. I wonder what is wrong?
Sleep will wait at least that long before activating the thread again, but it can always be longer than that. After the one second time has passed the thread becomes eligible to execute by the CPU scheduler, and the scheduler is able to run it whenever it wants to. If it's particularly busy, and/or is using a scheduling algorithm that doesn't focus on quickly allowing newly active threads to run, it could be some time.
Servy's answer is correct. For more details, please read the documentation:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686298.aspx
which states:
Note that a ready thread is not guaranteed to run immediately. Consequently, the thread may not run until some time after the sleep interval elapses. For more information, see Scheduling Priorities.
The priority documentation is here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms685100.aspx
More generally though: you are doing something deeply wrong. Do not ever use Sleep like this. If you want to wait some amount of time then use a timer or use a Delay task. Never sleep a thread like this outside of test code. The right way to write your code is probably something like
for (int countdown = 5; countdown > 0; countdown -= 1)
{
label1.Text = countdown.ToString();
await Task.Delay(1000);
}
Or, make a class that has a timer and a counter, start the timer to tick a few times a second, compare the current time to the time when you last updated the label, and if more than a second has elapsed, update the label.
I am currently trying to write an application that runs the same code exactly 100 times a second. I have done some testing using the built-in timers of the .NET framework. I've tested the System.Threading.Timer class, the System.Windows.Forms.Timer class and the System.Timers.Timer class. None of them seem to be accurate enough for what I am trying to do.
I've found out about PerformanceCounters and am currently trying to implement such in my application.
However, I am having a bit of trouble with my program taking up a whole core of my CPU when idling.
I only need it to be active 100 times a second. My loop looks like this:
long nextTick, nextMeasure;
QueryPerformanceCounter(out start);
nextTick = start + countsPerTick;
nextMeasure = start + performanceFrequency;
long currentCount;
while (true)
{
QueryPerformanceCounter(out currentCount);
if (currentCount >= nextMeasure)
{
Debug.Print("Ticks this second: " + tickCount);
tickCount = 0;
nextMeasure += performanceFrequency;
}
if (currentCount >= nextTick)
{
Calculations();
tickCount++;
nextTick += countsPerTick;
}
}
As you can see, most of the time the program will be waiting to run Calculations() again by running through the while loop constantly. Is there a way to stop this from happening? I don't want to slow the computers my program will be run on down.
System.Thread.Thread.Sleep unfortunately is also pretty "inaccurate", but I would be okay with using it if there is no other solution.
What I am basically asking is this: Is there a way to make an infinite loop less CPU-intensive? Is there any other way of accurately waiting for a specific amount of time?
As I'm sure you're aware, Windows is not a real-time O/S, so there can never be any guarantee that your code will run as often as you want.
Having said that, the most efficient in terms of yielding to other threads is probably to use Thread.Sleep() as the timer. If you want higher accuracy than the default you can issue a timeBeginPeriod with the desired resolution down to a millisecond. The function must be DLLImported from winmm.dll.
timeBeginPeriod(1) together with a normal timer or Thread.Sleep should work decently.
Note that this has a global effect. There are claims that it increases power consumption, since it forces the windows timer to run more often, shortening the CPU sleeping periods. This means you should generally avoid it. But if you need highly accurate timing, it's certainly a better choice than a busy-wait.
I have a timer. But it starts a second late.I mean, i am clocking a time according to the timer. But the time in the clock, which updates through timer, is one second less then the actual time elapsed. The timer starts off a second late. How to set my timer to start at right time to show actual time? this is the code inside tick event:
if (currentState == play)
{
m_StatusLabel.Text = String.Format("Playing {0} ", format(timeCounter));
timeCounter++;
}
Here timeCounter is updating with each second.
Standard System.Windows.Forms timers give no guarantees about 'metronome quality'. For that, you would need either a Systems.Threading.Timer or a System.Timer.
Ref: Comparing the Timer Classes in the .NET Framework Class Library
There are no timers available that will gaurantee true-time accuracy, not even System.Threading.Timer can do that. For example, when you ask Threading.Timer to fire in 30ms, it might actually take 40ms or 200ms. Or if you ask for 1 second, it might fire in 1.01 seconds. In won't take long before your time is inaccurate.
The reason for this is that the timer makes the callback thread schedulable, but it still takes time for the thread scheduler to actually call the thread. Various operations the system is performing can delay that callback.
A Windows timer makes only one guarantee: it won't fire before the timeout interval elapses. Specifically, a thread will never receive a timer event while there are messages in its message queue.
Consequently you can't use timers to implement a clock. If you want to update a status message to show how long something has been playing, then you could try this: record the start time, and when your timer ticks get the current time, subtract the start time, and dislay the difference. Your status message won't be updated exactly every second, but when it is updated it will be correct.
Show a number 1 greater:
if (currentState == play)
m_StatusLabel.Text = String.Format("Playing {0} ",
format(++timeCounter) );
NOTE
Please consider the reason of those who suggested displaying the elapsed time as current time - start time. That is the way this is usually done. It is more accurate; a timer can be irregular on a busy system. It is still possible to update the displayed value every second. Pseudo code here for now(). This requires acquiring current time and performing a time difference.
if (currentState == play)
m_StatusLabel.Text = String.Format("Playing {0} ",
format( (now() - start)/1000 );
See Also:
1. DateTime.Subtraction
2. DateTime.Now
I'm currently using the following code (C#):
private static void PlayLoop(string filename)
{
Audio player = new Audio(filename);
player.Play();
while (player.Playing)
{
if (player.CurrentPosition >= player.Duration)
{
player.SeekCurrentPosition(0, SeekPositionFlags.AbsolutePositioning);
}
System.Threading.Thread.Sleep(100);
}
}
This code works, and the file I'm playing is looping. But, obviously, there is a small gap between each playback.
I tried reducing the Thread.Sleep it to 10 or 5, but the gap remains.
I also tried removing it completely, but then the CPU usage raises to 100% and there's still a small gap.
Is there any (simple) way to make playback in DirectX gapless? It's not a big deal since it's only a personal project, but if I'm doing something foolish or otherwise completely wrong, I'd love to know.
Thanks in advance.
The problem with your approach is that you have a thread spinning, checking the position of the player. The longer you increase your sleep, the less CPU it drains but the later it will notice the end of the clip. The shorter the sleep, the quicker it will notice but the more CPU your checker thread consumes.
I can see nothing in the docs to tell the clip to loop. The only suggestion I have is to use the Audio.Ending event to start the clip playing again. This will remove the need for your separate monitoring thread but I am not sure whether this will be quick enough to eliminate the gap.
You will also want to check to ensure your audio clip does not begin or end with a period of silence.
Since you know the duration of the song, just use a timer to regularly call you "rewind" method and give the timer an interval sligthly shorter than the real song duration.
private static void PlayLoop(string filename)
{
Audio player = new Audio(filename);
player.Play();
//System.Timers.Timer (i reduce the length by 100 ms to try to avoid blank)
var timer = new Timer((player.Duration - TimeSpan.FromMilliseconds(100)).TotalMilliseconds());
timer.Elapsed += (sender, arg) =>
{
player.SeekCurrentPosition(0, SeekPositionFlags.AbsolutePositioning);
};
timer.Start();
}
Of course, you'll need to stop the timer so make it an instance member instead of a local variable of your method, so you can disable it with a Stop() method.
I hope this will help you.
Manitra