How can I get better timed events for my games? - c#

I have written an ordinary dispatcher timer method for creating a gameloop in WPF. I notice though that if it is set to a shorter interval than say 200 ms, it doesn't catch up correctly.
I printed the seconds to screen (see code below) and compared it to my wrist watch. With a setting of 500 ms, it's okay, but if I set it much lower there is a huge discrepancy. I tried a setting of 10 ms, and that meant that the time onscreen passed only 38 seconds in a (real) minute! (Note that all my game engine code was removed during testing, so it's just the timer loop that is called. Also, it doesn't matter if I run the code from VS or the exe file in the Debug folder.)
Note that the games I create run smoothly, it's just that my (standard) Each_Tick method doesn't get called at the correct times. This in turn means that my games will run faster on a faster computer.
So how do I keep correct track of time and make sure that the Each_Tick method fires at the same time, independently of the computer (or cellphone) used? That is, I would rather have a limit on the number of game objects, collision detection precision etc, but on time, rather than just going as fast as possible. Put differently, if I set the timer increment value to 50ms (which I think is reasonable as that would mean 20 times per second), I really want the game to be updated 20 times per second.
I really don't want to get into threading etc if it's possible to avoid it, as the games themselves run fine now. I just want them to play back at the same speed.
I looked up some great replies at How to control frame rate in WPF by using dispatcher timer accurately? but this still doesn't answer my question: how do I get my games to run at the same speed, regardless of (modern) computer/cell phone and whether it's WPF/UWP or perhaps something else?
Or is this impossible to do in a rather easy manner, and I should just accept that game speed depends on the computer used?
Thanks!
Code:
public void StartTimer()
{
//This variable is used to get to the controls (labels etc) of the MainWindow (WPF)
MainWindow mainWin = System.Windows.Application.Current.Windows.Cast<System.Windows.Window>().FirstOrDefault(window => window is MainWindow) as MainWindow;
time = TimeSpan.FromSeconds(0);
//What the code below does is this:
//For each 10 ms, call the different methods. Then add 10 ms to the current time.
timer = new DispatcherTimer(new TimeSpan(0, 0, 0, 0, 10), DispatcherPriority.Normal, delegate
{
if (runGame == false) return; //only go on if not in pause mode
mainWin.txtInfo.Text = time.ToString("mm\\:ss");//Shows the timer in a textbox, only showing minutes and seconds.
//Collision code etc removed during the test
time = time.Add(TimeSpan.FromMilliseconds(10)); //adds a specified time to the current time
}, System.Windows.Application.Current.Dispatcher);
}
Note that the code above was added for testing purposes. The original code (facing the same problems) looks like this:
public void StartTimer()
{
//A note on the dispatcherTimer http://www.wpf-tutorial.com/misc/dispatchertimer/
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 10); // Each every n milliseconds (set low to avoid flicker)
timer.Tick += EachTick;
timer.Start();
}
// A testing counter
int counter = 0;
// Raised every tick while the DispatcherTimer is active.
private void EachTick(object sender, object e)
{ etc

Build in timers are not very accurate. Use this code snippet. Ive used this timer many times. This timer is really accurate. Kudos to John
public class AccurateTimer
{
private delegate void TimerEventDel(int id, int msg, IntPtr user, int dw1, int dw2);
private const int TIME_PERIODIC = 1;
private const int EVENT_TYPE = TIME_PERIODIC;
[DllImport("winmm.dll")]
private static extern int timeBeginPeriod(int msec);
[DllImport("winmm.dll")]
private static extern int timeEndPeriod(int msec);
[DllImport("winmm.dll")]
private static extern int timeSetEvent(int delay, int resolution, TimerEventDel handler, IntPtr user, int eventType);
private readonly int _mTimerId;
public AccurateTimer(int delay)
{
timeBeginPeriod(1);
_mTimerId = timeSetEvent(delay, 0, TimerTick, IntPtr.Zero, EVENT_TYPE);
}
public void Stop()
{
timeEndPeriod(1);
System.Threading.Thread.Sleep(100);// Ensure callbacks are drained
}
private void TimerTick(int id, int msg, IntPtr user, int dw1, int dw2)
{
Console.WriteLine("Tick " + DateTime.Now.TimeOfDay.TotalMilliseconds);
}
}

Related

How to keep time reference during mobile window switch? (Unity) [duplicate]

This question already has answers here:
How to pass data (and references) between scenes in Unity
(6 answers)
Closed 2 years ago.
I have a game where I have a queue matchup system.
I would like to show the player how long they been in the current queue. It works well, until the player presses the menu/app overview button on their phone, which basically freezes the timer, and it only continues counting when the player switches back to full screen mode on their phone.
I tried looking for an app lifecycle method (somewhat like onApplicationPause, but it didn't work for me)
I also tried syncing the time by saving it in the db and then loading from the database actually, but Firebase puts on some delay, so it won't be exact.
How could I solve this, so it will keep counting when the user presses their app overview/menu button on their phone?
For now, I have this code which counts the user's queue time:
private void Update() {
if(startedCounting) {
timer += Time.deltaTime;
int seconds = Mathf.FloorToInt(timer % 60);
int minutes = Mathf.FloorToInt(timer / 60);
queueStatusText.text = "You are in the queue\n"
+ string.Format("{0:00}:{1:00}", minutes, seconds);
}
}
There are different aproaches, some using static class or singleton pattern. It is better to not update this time variable each time on Update() as it takes the computation time each update (if you don't need this time for anything else). Also user doesn't need to have exact time by frames so you can avoid things like adding Time.deltaTime.
I'll show you example with static class, it can hold this information. Also note that this script is only added as C# file, but you don't attach it to any GameObject :
public static class QueueTimerInformation //It is not inheriting from MonoBehavior!
{
private static DateTime dt;
private static bool isRunning = false;
//Save current DateTime when user did the action
public static void Start()
{
if(!isRunning)
{
dt = DateTime.Now;
isRunning = true;
}
}
public static void Reset()
{
isRunning = false;
}
// This gets the actual time in String value
// Usually it is better to return just `elapsedTime` and format it later
public static string GetTimeElapsed()
{
if(!isRunning) return "00:00"; //Not running, return some default
var elapsedTime = (DateTime.Now - dt);
return $"{elapsedTime:mm\\:ss}";
}
}
Usage
//On 1st time enter lobby
QueueTimerInformation.Start();
//In update method
var result = QueueTimerInformation.GetTimeElapsed();

How do I fix my timer so it counts in seconds?

I want my game's screen to have a timer on screen that shows how many seconds have passed (representing the player's point score). I am able to get the timer on screen, however, the counter freaks out and my console doesn't print the result correctly either. Any ideas?
I've tried to use timer.Elapsed however SplashKit (what i must use) does not seem to recognise that.
Sorry if this is a repeated question, I am new to programming and have searched around but couldn't find anything I could comprehend/assist.
public void Timer()
{
//begin timer and print results
timer.Start();
//write to console how many milliseconds have passed, and divide by 1000 for seconds.
Console.WriteLine($":{timer.Ticks} milliseconds have passed");
Console.WriteLine($"which is {timer.Ticks /1000} seconds");
//covert timer.Ticks to string and store into string 'score
score = Convert.ToString(timer.Ticks);
//assign font
Font Quicksand = SplashKit.LoadFont("Quicksand", "Resources\\fonts\\Quicksand-Regular.otf");
//use SplashKit to print to screen..
SplashKit.DrawText(score, Color.Black, Quicksand, 70, 700, 900);
}
+1 to Eric j's comment- all the Timer types the framework that I know of are not for providing stopwatch style "the game has been running 5 minutes" type functions directly themselves. They're classes that raise an event at some predefined interval. The actual timing of the game, if using a Timer, would be done by you by recording the start time and differencing the time now to it upon the timer elapsing it's interval:
public class Whatever{
private Timer _t = new Timer();
private DateTime _start;
public Whatever(){ //constructor
_t.Elapsed += TimerElapsed; //elapsed event handled by TimerElapsed method
_t.Interval = 1000; //fire every second
}
public void StartGame(){
_start = DateTime.UtcNow;
_t.Start();
}
private void TimerElapsed(){
Console.WriteLine("Game has been running for " + (DateTime.UtcNow - _start));
}
The timer interval merely controls how often the clock will update on screen. If you're offering game times of 10.1,10.2 seconds etc then make the timer interval less than 100 (updates more than once every 0.1 seconds) for example
It's not clear what type timer is, but the property Elapsed is probably a TimeSpan.
The total seconds including decimal amount is present in the double value:
timer.Elapsed.TotalSeconds
You can truncate that to an integer by casting it
var seconds = (int)timer.Elapsed.TotalSeconds;

Stop a looped thread method after x seconds

I'm creating a console game as simple as "I generate a random number, find it", but with many options.
My current code (without what I want here) is availlable on GitHub: https://github.com/crakmaniaque/trouvezmoi
What I want is to create a version of my game which will be timed, so the computer generates numbers, the user finds it, it generates a new one and the player have 90 seconds to find a max lot of random numbers. I can code this easily.
What I will need help is to stop the game (a thread) after 90 seconds and retrieve the number of answers founded from the thread. The Console.Title should also show time remaining. The attempt I've tried works, but the thread is not interrupted if console is asking for number input (Console.ReadLine()). But the timer is for the entire process, not only user input.
private static void timerb()
{
int t = 90;
for (int i = 0; i < 90; i++)
{
Console.Title = t + " seconds remaining";
Thread.Sleep(1000);
t--;
}
}
private static void cGame()
{
Thread t = new Thread(timerb);
t.Start();
while (t.IsAlive)
{
bool good = false;
int rnd = new Random().Next(0,10); // 0 and 10 are sample
while (!good)
{
try
{
Console.Write("Enter a number between x and y >");
int i = int.Parse(Console.ReadLine());
if (i == rnd)
{
good = true;
}
}
catch (FormatException)
{
Console.WriteLine("Invalid answer.");
}
}
}
}
I don't know much about threading and at that point I'm stuck.
Can someone help me with my problem? I'm using .NET 2.0.
Perhaps you are looking for a timer? You could register an event, that would fire after 90 seconds, that would run while the loop is happening. The documentation can be found here: Timer class MSDN documentation.
I believe the usage would be:
Timer timer = new Timer { Interval = new Timespan (0,1,30);
timer.elapsed += //function to fire to kill the app or the game
You'd need to make each console read with a timeout equal to the amount of time left in the game. That solves that issue.
Then, you need a way to signal the timerb thread to shut down when the main game loop has ended. I think the simplest way would be to end the game loop when the remaining time is <= zero. Alternatively, you could make timerb singnal the main thread to shut down when t == 0. Inter-thread communication is always complicated and error-prone, though.
You can signal the timerb thread to shut down by setting a volatile bool shutdown to true and by making timerb poll that variable and shut itself down.

Optimization: Set or Subtract?

a very simple question here.
I'm coding a multi-player game in XNA, what would be a better option here - and why?
(Line 7 is the only changed line in each option)
Option 1:
const float SyncFrequency = (1 / 30f);
float Sync;
void Sync(GameTime GT)
{
Sync += (float)GT.ElapsedGameTime.TotalSeconds;
if (Sync >= SyncFrequency) { Sync = 0; SyncAll(); }
}
void SyncAll() { /*Syncing Code Here*/ }
Option 2:
const float SyncFrequency = (1 / 30f);
float Sync;
void Sync(GameTime GT)
{
Sync += (float)GT.ElapsedGameTime.TotalSeconds;
if (Sync >= SyncFrequency) { Sync -= SyncFrequency; SyncAll(); }
}
void SyncAll() { /*Syncing Code Here*/ }
Also:
Notice I use floats:
const float SyncFrequency = (1 / 30f);
Would using extra-precision be needed at all? E.g:
const double SyncFrequency = (1 / 30d);
Mathematically it makes sense to subtract instead of resetting.
Example: Standard 60fps means updating approximately every 16.666ms. If you have a network connection that you poll on every 30ms, upon the second Update, the time passed will have been 33.333ms, and this means that if you reset, you will be back at square one again. Whereas if you subtract 30ms from the current, you're down on 3.333ms. So over the course of a few seconds, you'll have run a more accurate amount of polls on the network connection.
It all depends on the situation really. In the above mentioned instance, you might run into situation where the application will poll the network connection 33 times, and other times it may do it 32, or 34 times, due to the time from the previous runs. All this can of course be calculated.
This may lead to uneven (probably unnoticeable, but still) gameplay, if it is actually used for network polling.
However for things such as Animating, it makes sense, since you'll most likely ALWAYS want the smoothest animating your hardware can deliver.
Especially for movement, since some PCs can't update 60 times per second (either because of hardware limitations, or the software simply only supports 30 or maybe 24fps). This will also lead to uneven movement, if you don't actually use the time passed (16.666ms in 60fps) to calculate movement and the likes.
Rant ended
To further expand on Bjarke's great explanation, I coded this for my use, but feel free to use it:
public static class Timers
{
private static Dictionary<string, Timer> Array;
public static void Add(string Name, double Interval)
{
if (Array == null) Array = new Dictionary<string, Timer>();
if (!Array.ContainsKey(Name)) Array.Add(Name, new Timer(Interval));
}
public static void Remove(string Name) { if ((Array != null) && Array.ContainsKey(Name)) Array.Remove(Name); }
public static void Clear() { Array.Clear(); Array = null; }
public static void Update(GameTime GameTime)
{
if (Array != null)
foreach (Timer Timer in Array.Values)
{
if (Timer.Time >= Timer.Interval) Timer.Time -= Timer.Interval;
Timer.Time += GameTime.ElapsedGameTime.TotalSeconds;
}
}
public static bool Tick(string Name) { return ((Array != null) && Array.ContainsKey(Name) && (Array[Name].Time >= Array[Name].Interval)); }
private class Timer
{
public double Interval, Time;
public Timer(double Interval) { this.Interval = Interval; }
}
}
To add a timer:
//Parameter 1: The name of the timer for your use.
//Parameter 2: The time (of interval) for each tick.
Timers.Add("Name of your timer!", (1 / 30d));
// (1 / 30d) = 30 times a second
(add your timer in the load function of your game)
To use your timer:
In your game update method (at the start of it), call:
Timers.Update(gameTime);
Then you can use any timers that you've added like so:
if (Tick("Name of your timer!"))
{
//Code here to execute on the tick of the timer
}

A way to concatenate Console.Beep sounds

I've been playing around with some C# and specifically making sounds... because it's fun. So I've got it all working but there's something bugging me to do with Console.Beep(): it doesn't directly concatenate sounds. For example, running the code below will result in a series of 250-millisecond bursts of sound - but instead of all being run together and sounding as if they are one, they become disjointed, with a ~50ms pause in between each sound.
for(int i = 0; i < 11; i++)
{
Console.Beep(980, 250);
}
So the question is, is there any programmatic way to make the system run the sounds together? I have to say I don't really expect there to be but I figured it was worth an ask, since many other resources seem to just accept the fact that it doesn't.
You can't, that method uses kernel's functions. I will prove:
[SecuritySafeCritical]
[HostProtection(SecurityAction.LinkDemand, UI = true)]
public static void Beep(int frequency, int duration)
{
if (frequency < 37 || frequency > 32767)
{
throw new ArgumentOutOfRangeException("frequency", frequency, Environment.GetResourceString("ArgumentOutOfRange_BeepFrequency", new object[]
{
37,
32767
}));
}
if (duration <= 0)
{
throw new ArgumentOutOfRangeException("duration", duration, Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
}
Win32Native.Beep(frequency, duration);
}
This is the Console.Beep's code, it uses Win32Native.Beep to actually perform beep (Aside from the checks above it), and that method leads to:
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool Beep(int frequency, int duration);
A function that imported from kernel.
Unless you hard-code and modify your kernel, you can't. (Which I am sure you don't want to)
I can give you an alternative: http://naudio.codeplex.com/, you can instead control your sound by using this tool and giving it stream. (You can create stream that don't use file as source)

Categories

Resources