In unity3d I'm trying to read the system usage in order to see the CPU and RAM usage whilst a person is playing a VR game. I know how I can show it on the computer screen and how to keep it away from the players VR screen.
I have been looking all around Google for as far as I know. Most of them lead to the same answer which I cannot seem to get working correctly.
I have been checking out the post: https://answers.unity.com/questions/506736/measure-cpu-and-memory-load-in-code.html which leads me to the stackoverflow page: How to get the CPU Usage in C#?
In these posts they always get the problem of having the CPU usage stuck at 100%, even though this is incorrect. I have been trying the System.Threading.Thread.Sleep(1000). But this locks the entire game to 1fps due to it waiting 1 sec every second. And even with having 1fps I cannot get any other read than 100%.
In this code I'm using the Sleep thread which slows down the game to 1fps
using UnityEngine;
using System.Diagnostics;
using TMPro;
public class DebugUIManager : MonoBehaviour
{
private PerformanceCounter cpuCounter;
[SerializeField] private TMP_Text cpuCounterText;
private void Start()
{
cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
}
private void Update()
{
cpuCounterText.text = getCurrentCpuUsage();
}
private string getCurrentCpuUsage()
{
dynamic firstValue = cpuCounter.NextValue() + "% CPU";
System.Threading.Thread.Sleep(1000);
dynamic secondValue = cpuCounter.NextValue() + "% CPU";
return secondValue;
}
}
Whenever I change the
private string getCurrentCpuUsage()
{
dynamic firstValue = cpuCounter.NextValue() + "% CPU";
System.Threading.Thread.Sleep(1000);
dynamic secondValue = cpuCounter.NextValue() + "% CPU";
return secondValue;
}
to
private string getCurrentCpuUsage()
{
return cpuCounter.NextValue() + "%";
}
the game won't get dropped in fps, but it also doesn't do anything still.
I haven't been getting any error messages because it runs without issues. But I would really like to know how to get the CPU usage, so I can figure out the rest myself.
Any answers helping would be appreciated.
I tried this and also couldn't get PerformanceCounter to run correctly (always returned 100% like for you)
However I've found a good alternative here and used it to re-build your DebugUIManager on it
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using TMPro;
using UnityEngine;
public class DebugUIManager : MonoBehaviour
{
[Header("Components")]
[SerializeField] private TMP_Text cpuCounterText;
[Header("Settings")]
[Tooltip("In which interval should the CPU usage be updated?")]
[SerializeField] private float updateInterval = 1;
[Tooltip("The amount of physical CPU cores")]
[SerializeField] private int processorCount;
[Header("Output")]
public float CpuUsage;
private Thread _cpuThread;
private float _lasCpuUsage;
private void Start()
{
Application.runInBackground = true;
cpuCounterText.text = "0% CPU";
// setup the thread
_cpuThread = new Thread(UpdateCPUUsage)
{
IsBackground = true,
// we don't want that our measurement thread
// steals performance
Priority = System.Threading.ThreadPriority.BelowNormal
};
// start the cpu usage thread
_cpuThread.Start();
}
private void OnValidate()
{
// We want only the physical cores but usually
// this returns the twice as many virtual core count
//
// if this returns a wrong value for you comment this method out
// and set the value manually
processorCount = SystemInfo.processorCount / 2;
}
private void OnDestroy()
{
// Just to be sure kill the thread if this object is destroyed
_cpuThread?.Abort();
}
private void Update()
{
// for more efficiency skip if nothing has changed
if (Mathf.Approximately(_lasCpuUsage, CpuUsage)) return;
// the first two values will always be "wrong"
// until _lastCpuTime is initialized correctly
// so simply ignore values that are out of the possible range
if (CpuUsage < 0 || CpuUsage > 100) return;
// I used a float instead of int for the % so use the ToString you like for displaying it
cpuCounterText.text = CpuUsage.ToString("F1") + "% CPU";
// Update the value of _lasCpuUsage
_lasCpuUsage = CpuUsage;
}
/// <summary>
/// Runs in Thread
/// </summary>
private void UpdateCPUUsage()
{
var lastCpuTime = new TimeSpan(0);
// This is ok since this is executed in a background thread
while (true)
{
var cpuTime = new TimeSpan(0);
// Get a list of all running processes in this PC
var AllProcesses = Process.GetProcesses();
// Sum up the total processor time of all running processes
cpuTime = AllProcesses.Aggregate(cpuTime, (current, process) => current + process.TotalProcessorTime);
// get the difference between the total sum of processor times
// and the last time we called this
var newCPUTime = cpuTime - lastCpuTime;
// update the value of _lastCpuTime
lastCpuTime = cpuTime;
// The value we look for is the difference, so the processor time all processes together used
// since the last time we called this divided by the time we waited
// Then since the performance was optionally spread equally over all physical CPUs
// we also divide by the physical CPU count
CpuUsage = 100f * (float)newCPUTime.TotalSeconds / updateInterval / processorCount;
// Wait for UpdateInterval
Thread.Sleep(Mathf.RoundToInt(updateInterval * 1000));
}
}
}
Related
This question already has answers here:
High resolution timer in C#
(5 answers)
Closed 3 years ago.
I want to have a timer with about 5millisecond resolution. but the current Timer in .Net has a resolution of about 50ms.
I could not find any working solution that creates a high resolution timer, although some claim that you can do it in C#.
In regards to the information that the OP was specifically asking about the Timer class which fires events at regular intervals. I have amended this answer, with my old answer below the horizontal rule.
I tested the following code with the Timer class, and it seems like it can get at least within the 14 - 15 millisecond range on my machine. Try it out for yourself and see if you can reproduce this. So sub-50 millisecond response times are possible, but it can't get down to exactly one millisecond.
using System;
using System.Timers;
using System.Diagnostics;
public static class Test
{
public static void Main(String[] args)
{
Timer timer = new Timer();
timer.Interval = 1;
timer.Enabled = true;
Stopwatch sw = Stopwatch.StartNew();
long start = 0;
long end = sw.ElapsedMilliseconds;
timer.Elapsed += (o, e) =>
{
start = end;
end = sw.ElapsedMilliseconds;
Console.WriteLine("{0} milliseconds passed", end - start);
};
Console.ReadLine();
}
}
NB: The following is my old answer, when I thought the OP was talking about timing things. The following is merely useful information with respect to timing the duration of things, but doesn't provide any way of firing events at a regular interval. For that purpose, the Timer class is necessary.
Try using the Stopwatch class within System.Diagnostics: http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
You can query it to check if it's high resolution through it's IsHighResolution field. Also, you can check the exact resolution of the Stopwatch:
int resolution = 1E9 / Stopwatch.Frequency;
Console.WriteLine("The minimum measurable time on this system is: {0} nanoseconds", resolution);
If you're worried about where this is actually sourced, the documentation seems to imply that it actually internally calls the lower level Win32 functions:
The Stopwatch class assists the manipulation of timing-related
performance counters within managed code. Specifically, the Frequency
field and GetTimestamp method can be used in place of the unmanaged
Win32 APIs QueryPerformanceFrequency and QueryPerformanceCounter.
What about this one?
public class HiResTimer
{
private bool isPerfCounterSupported = false;
private Int64 frequency = 0;
// Windows CE native library with QueryPerformanceCounter().
private const string lib = "coredll.dll";
[DllImport(lib)]
private static extern int QueryPerformanceCounter(ref Int64 count);
[DllImport(lib)]
private static extern int QueryPerformanceFrequency(ref Int64 frequency);
public HiResTimer()
{
// Query the high-resolution timer only if it is supported.
// A returned frequency of 1000 typically indicates that it is not
// supported and is emulated by the OS using the same value that is
// returned by Environment.TickCount.
// A return value of 0 indicates that the performance counter is
// not supported.
int returnVal = QueryPerformanceFrequency(ref frequency);
if (returnVal != 0 && frequency != 1000)
{
// The performance counter is supported.
isPerfCounterSupported = true;
}
else
{
// The performance counter is not supported. Use
// Environment.TickCount instead.
frequency = 1000;
}
}
public Int64 Frequency
{
get
{
return frequency;
}
}
public Int64 Value
{
get
{
Int64 tickCount = 0;
if (isPerfCounterSupported)
{
// Get the value here if the counter is supported.
QueryPerformanceCounter(ref tickCount);
return tickCount;
}
else
{
// Otherwise, use Environment.TickCount.
return (Int64)Environment.TickCount;
}
}
}
static void Main()
{
HiResTimer timer = new HiResTimer();
// This example shows how to use the high-resolution counter to
// time an operation.
// Get counter value before the operation starts.
Int64 counterAtStart = timer.Value;
// Perform an operation that takes a measureable amount of time.
for (int count = 0; count < 10000; count++)
{
count++;
count--;
}
// Get counter value when the operation ends.
Int64 counterAtEnd = timer.Value;
// Get time elapsed in tenths of a millisecond.
Int64 timeElapsedInTicks = counterAtEnd - counterAtStart;
Int64 timeElapseInTenthsOfMilliseconds =
(timeElapsedInTicks * 10000) / timer.Frequency;
MessageBox.Show("Time Spent in operation (tenths of ms) "
+ timeElapseInTenthsOfMilliseconds +
"\nCounter Value At Start: " + counterAtStart +
"\nCounter Value At End : " + counterAtEnd +
"\nCounter Frequency : " + timer.Frequency);
}
}
I found a solution to this problem in the following blog:
http://web.archive.org/web/20110910100053/http://www.indigo79.net/archives/27#comment-255
It tells you how to use the multimedia timer to have a timer with high frequency. It is working just fine for me!!!
Here is an implementation based on StopWatch timer
https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545
It works on all platforms and is high precision wherever StopWatch.IsHighPrecision == true
Its Elapsed event is guaranteed to be non overlapping (which might be important to know, because state changes inside the event handler may be left unprotected against multi threaded access)
Here is how to use it:
Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}");
Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]");
var timer = new HighResolutionTimer(0.5f);
// UseHighPriorityThread = true, sets the execution thread
// to ThreadPriority.Highest. It doesn't provide any precision gain
// in most of the cases and may do things worse for other threads.
// It is suggested to do some studies before leaving it true
timer.UseHighPriorityThread = false;
timer.Elapsed += (s, e) => { /*... e.Delay*/ }; // The call back with real delay info
timer.Start();
timer.Stop(); // by default Stop waits for thread.Join()
// which, if called not from Elapsed subscribers,
// would mean that all Elapsed subscribers
// are finished when the Stop function exits
timer.Stop(joinThread:false) // Use if you don't care and don't want to wait
Here is a benchmark (and a live example):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3
The results of setting the timer for 0.5 ms on Windows 10:
It is also worth to mention that:
I had the same precision on mono on Ubuntu.
While playing with the benchmark, the maximum and a very rare deviation I saw was about 0.5 ms
(which probably means nothing, it is not realtime systems, but still worth mentioning)
Stopwatch ticks are not TimeSpan ticks. On that Windows 10 machine
HighResolutionTimer.TickLength is 0.23[ns].
CPU usage of the benchmark is 10% for 0.5ms interval and 0.1% for 200ms interval
Pretty late to the party still might be useful for someone looking for an answer as nothing has been changed for over a decade in the topic.
Background
Any .NET delay instructions would always come down to system clock resolution, the one you set with timeBeginPeriod(). No matter if it's a Thread.Sleep(N), Threading.Timer or Waitable.WaitOne(N). Yet time resolution of DateTime.Now() and System.Diagnostic.Stopwatch is way higher so there is a way of implementing precise timing events known as hot loop. Hot loops yet prone to being threatened badly by OS since those tend to occupy processor core at full. And here's what we do to prevent this:
Surrender thread time quant in a hot loop to other threads once there's no more need for it by calling Thread.Sleep(0) or .WaitOne(0)
Below is a code piece showing simple implementation of high resolution scheduler:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// High resolution scheduler.
/// License: public domain (no restrictions or obligations)
/// Author: Vitaly Vinogradov
/// </summary>
public class HiResScheduler : IDisposable
{
/// <summary>
/// Scheduler would automatically downgrade itself to cold loop (Sleep(1)) when there are no
/// tasks earlier than the treshold.
/// </summary>
public const int HOT_LOOP_TRESHOLD_MS = 16;
protected class Subscriber : IComparable<Subscriber>, IComparable
{
public Action Callback { get; set; }
public double DelayMs { get; set; }
public Subscriber(double delay, Action callback)
{
DelayMs = delay;
Callback = callback;
}
public int CompareTo(Subscriber other)
{
return DelayMs.CompareTo(other.DelayMs);
}
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null))
return -1;
var other = obj as Subscriber;
if (ReferenceEquals(other, null))
return -1;
return CompareTo(other);
}
}
private Thread _spinner;
private ManualResetEvent _allowed = new ManualResetEvent(false);
private AutoResetEvent _wakeFromColdLoop = new AutoResetEvent(false);
private bool _disposing = false;
private bool _adding = false;
private List<Subscriber> _subscribers = new List<Subscriber>();
private List<Subscriber> _pendingSubscribers = new List<Subscriber>();
public bool IsActive { get { return _allowed.WaitOne(0); } }
public HiResScheduler()
{
_spinner = new Thread(DoSpin);
_spinner.Start();
}
public void Start()
{
_allowed.Set();
}
public void Pause()
{
_allowed.Reset();
}
public void Enqueue(double delayMs, Action callback)
{
lock (_pendingSubscribers)
{
_pendingSubscribers.Add(new Subscriber(delayMs, callback));
_adding = true;
if (delayMs <= HOT_LOOP_TRESHOLD_MS * 2)
_wakeFromColdLoop.Set();
}
}
private void DoSpin(object obj)
{
var sw = new Stopwatch();
sw.Start();
var nextFire = null as Subscriber;
while (!_disposing)
{
_allowed.WaitOne();
if (nextFire != null && sw.Elapsed.TotalMilliseconds >= nextFire?.DelayMs)
{
var diff = sw.Elapsed.TotalMilliseconds;
sw.Restart();
foreach (var item in _subscribers)
item.DelayMs -= diff;
foreach (var item in _subscribers.Where(p => p.DelayMs <= 0).ToList())
{
item.Callback?.Invoke();
_subscribers.Remove(item);
}
nextFire = _subscribers.FirstOrDefault();
}
if (_adding)
lock (_pendingSubscribers)
{
_subscribers.AddRange(_pendingSubscribers);
_pendingSubscribers.Clear();
_subscribers.Sort();
_adding = false;
nextFire = _subscribers.FirstOrDefault();
}
if (nextFire == null || nextFire.DelayMs > HOT_LOOP_TRESHOLD_MS)
_wakeFromColdLoop.WaitOne(1);
else
_wakeFromColdLoop.WaitOne(0);
}
}
public void Dispose()
{
_disposing = true;
}
}
The system clock "ticks" at a constant rate. In order to raise the accuracy of timer dependent function*s, call **timeGetDevCaps* which determines the minimum supported timer resolution.
Then call timeBeginPeriod setting the timer resolution to its minimum.
Caution: By invoking timeBeginPeriod, other timer dependent function can be significantly affected such as the system clock, system power usage, and the scheduler. Thus start your application with timeBeginPeriod and end it with timeEndPeriod
The previous example doesn't work unless the frequency is in milliseconds; the perf timer frequency is rarely in ms.
private static Int64 m_iPerfFrequency = -1;
public static double GetPerfCounter()
{
// see if we need to get the frequency
if (m_iPerfFrequency < 0)
{
if (QueryPerformanceFrequency(out m_iPerfFrequency) == 0)
{
return 0.0;
}
}
Int64 iCount = 0;
if (QueryPerformanceCounter(out iCount) == 0)
{
return 0.0;
}
return (double)iCount / (double)m_iPerfFrequency;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceCounter(out Int64 iCount);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceFrequency(out Int64 iFrequency);
This returns the perf counter in seconds. The reason you'd use the perf timer is to share a timer with legacy C++ code, or get a more precise timer than the C# StopWatch class.
You could use QueryPerformanceCounter() and QueryPerformanceTimer() as outlined in this article.
Does any know if Unity has a way to count how many particles have been emitted by a particle system? So I could check to see if there has been an emission, like:
public ParticleSystem mySystem;
private int currentParticleCount;
private int lastParticleCount;
void Start () {
lastParticleCount = mySystem.getEmissionCount();
}
void Update () {
currentParticleCount = mySystem.getEmissionCount();
if(currentParticleCount>lastParticleCount) {
DoStuff();
}
lastParticleCount = currentParticleCount;
}
You can use ParticleSystem.particleCount to return the current number of particles. If that's not giving you the proper amount of particles, use the ParticleSystem.GetParticles function since that returns just the current number of alive particles. Below is an example for both of them:
private ParticleSystem ps;
// Use this for initialization
void Start()
{
ps = GetComponent<ParticleSystem>();
}
// Update is called once per frame
void Update()
{
Debug.Log("Particles Count: " + ps.particleCount);
Debug.Log("Particles Alive Count: " + GetAliveParticles());
}
int GetAliveParticles()
{
ParticleSystem.Particle[] particles = new ParticleSystem.Particle[ps.particleCount];
return ps.GetParticles(particles);
}
The exact feature that you are asking for is not build it, BUT:
You can know the current particles displayed by the system, so you can make a counter that acumulates the number, or if you know the "displaying time" you can do the maths.
Knowing the current particles:
ParticleSystem.particleCount https://docs.unity3d.com/ScriptReference/ParticleSystem-particleCount.html
Update:
I isolated the problem to its core components and a much shorter minimal working example in another question:
Observable.Interval not updating UI with expected frequency
I need to display a sequence of images (which are read from a folder) as a "movie", at a constant, previously known, frame-rate.
I have an Image control in WPF, whose Source is data-bound to a ViewModel property Image. I then proceed to update that image on a timely basis, using an Observable.Interval event source. For each elapsed interval, the UpdateImage is called, which updates the image inside a Task.Run() call.
My problem is: when I experimentally increase effective frame-rate (which depends on actual frame-rate and also on playback speed), the playback keeps being at normal speed up tu a given value. Above that value, it starts to look slower.
I believe that it has to do with RaisePropertyChanged call, but I'm not sure. I tried using SubscribeOnDispatcher (or whas it ObserveOnDispatcher?) but anyway it didnt have an effect.
Questions are:
- What I am doing wrong?
- How could I investigate and resolve the problem?
UPDATE:
It's worth mentioning that the getter for Image calls CreateImageForIndex(), which is a method that could have a non-trivial cost. Would it be possible to "async" it?
ViewModel (partial):
CancellationTokenSource _cancelPlay;
double _speed;
public void Play(double speed)
{
_speed = speed;
_cancelPlay = new CancellationTokenSource();
Observable.Interval(TimeSpan.FromSeconds(1.0 / (Math.Abs(speed) * Exame.Model.Configurações.FrameRate)))
.Subscribe(t => ExecuteRun(), _cancelPlay.Token);
}
public void Stop()
{
_cancelPlay?.Cancel();
}
void ExecuteRun()
{
Task.Run(() =>
{
Index = Math.Max(0, Math.Min(_model.MaxIndex, Index + 1 * Math.Sign(_speed)));
});
}
public int Index
{
get { return _model.Index; }
set
{
_model.Index = value;
RaisePropertyChanged(null); // this tells view to get a new value for `Image` too!
}
}
public ImageSource Image => _model.CreateImageForIndex(Index);
I've followed a hint from #Enigmativity (in the other answer I posted), that in Windows the resolution of timers is 15ms at most.
So I tested another strategy: spinning a while loop with an "when elapsed" condition, similar to a game loop, measuring time with a Stopwatch, and everything is working fine now.
public void Play(double speed)
{
_speed = speed;
_cancelPlay?.Cancel();
_cancelPlay = new CancellationTokenSource();
Task.Run(() => PlayLoop(_cancelPlay.Token), _cancelPlay.Token);
}
void PlayLoop(CancellationToken token)
{
var sw = Stopwatch.StartNew();
double previous = sw.ElapsedMilliseconds;
double timeInterval = 1000.0 / (Math.Abs(_speed) * _exame.Model.Configurações.FrameRate);
while (!token.IsCancellationRequested)
{
var current = sw.ElapsedMilliseconds;
var elapsed = current - previous;
if (elapsed > timeInterval)
{
Índice = Math.Max(0, Math.Min(ÍndiceMáximo, Índice + 1 * Math.Sign(_speed)));
previous = current;
}
}
}
I am creating a game in c# unity, and I am having trouble keeping a timer going while the application is closed. I can use TimeSpan fine, and find the ticks when the game closes, then ticks now, and convert it into seconds.
I have this premium currency in the game that I want to go up every x seconds(for this example I was using 10 to test). I have a countdown going in the game called showTimeLeft, and it decreases by 1 per second. I also have a save/load.cs that saves all the variables, loads them. What I was doing was saving the time in seconds from when the game closes, and dividing it by the 10 seconds, and getting the premium currency, but there is a big problem I have.
When I closed the game, and say the timer was at 5, even if 20 seconds went by, the timer would be start back up at 10. So what I wanted to do was take the remaining time from the timer, and subtract it from the total seconds the game was down, take the modulation of the total seconds (15) and time to tick (10) to get 5 remaining seconds, then start my timer back at 5 when I load the game up, take an int of the total seconds(15) / time to tick (10) to get 1. I then wanted to increase premium currency by 1, for when the 5 seconds went to 0, then 1 because the remaining int of total seconds and time to tick is 1, then set the clock back at 5 seconds, which was leftover.
This sounds very confusing and it made me very confused trying to write this script, but I hope that when I put the code down you guys understand what I am trying to get at. I am adding comments on what I am trying to do in the code, not sure if it is all 100% correct or not.
PremiumCurrencyTimerManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
public class PremiumCurrencyTimerManager : MonoBehaviour {
public GameManager GM; //Game Manager, holds most of my variables
public Text text; //Text that holds the timer, and premium currency and max premium currency I can hold.
public Button collect; // this button collects the currency
public int currencyToCollect; //This is the variable that holds my currency
public int maxCurrencyToCollect; //Max of currency I can hold before I collect
public int timeToTick = 10; //Time to tick +1 currency
public TimeSpan showTimeLeft; //Time left on countdown
public TimeSpan endTimeLeft; //0 seconds, if the time left = this variable then add a currency
private void Awake()
{
showTimeLeft = TimeSpan.FromSeconds(timeToTick); //When this game first starts the time left = start time
endTimeLeft = TimeSpan.FromSeconds(0); //variable that shows how many seconds to add a currency, when timer reaches 0 add a currency
collect.onClick.AddListener(onCollect); //When the button is clicked call onCollect()
}
private void Start()
{
StartCoroutine(getCurrency(showTimeLeft)); //Start the countdown, in the background.
StartCoroutine(showTimer()); //Shows the actual countdown.
}
private void Update()
{
text.text = showTimeLeft + "\n\n" + currencyToCollect.ToString() + "/" + maxCurrencyToCollect.ToString();
if (showTimeLeft <= endTimeLeft)
showTimeLeft = TimeSpan.FromSeconds(timeToTick); //If the timer reaches 0, restart at timeToTick, I think my main problem with the save/load is that if I stop the game at 2 seconds, it starts the timer back up at 2 seconds because thats what was saved, and it adds a currency anyway.
}
public void onCollect() //Called when button is clicked, moves the currency I need to collect to my variable page so I can use it
{
GM.premiumCurrency += currencyToCollect;
currencyToCollect = 0;
}
public void addCurrency() //Call this when the timer reaches 0 to add a currency
{
if (currencyToCollect < maxCurrencyToCollect)
currencyToCollect++;
else
currencyToCollect = maxCurrencyToCollect;
}
public IEnumerator getCurrency(TimeSpan timeInSecs) //Background timer
{
int x = timeInSecs.Seconds;
while (true)
{
yield return new WaitForSeconds(x);
addCurrency();
}
}
public IEnumerator showTimer() //Timer we see, subtracts 1 per second
{
while (true)
{
yield return new WaitForSeconds(1);
showTimeLeft = showTimeLeft.Subtract(TimeSpan.FromSeconds(1));
}
}
}
SaveLoad.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using System;
public class SaveLoad : MonoBehaviour{
public GameManager GM;
public PremiumCurrencyManager PCM;
public long ticksSinceClose;
public long totalSeconds;
public int mailboxTicks;
public long test;
public long test2;
public long b;
public void Save()
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Create(Application.persistentDataPath + "/playerInfo.dat");
PlayerData data = new PlayerData();
data.currencyToCollect= PCM.currencyToCollect;
data.maxCurrencyToCollect= PCM.maxCurrencyToCollect;
data.premiumCurrency= GM.premiumCurrency;
data.showTimeLeft = PCM.showTimeLeft;
bf.Serialize(file, data);
file.Close();
}
public void Load()
{
if (File.Exists(Application.persistentDataPath + "/playerInfo.dat"))
{
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/playerInfo.dat", FileMode.Open);
PlayerData data = (PlayerData)bf.Deserialize(file);
file.Close();
ticksSinceClose = System.DateTime.Now.Ticks - data.gameCloseTicks;
totalSeconds = ticksSinceClose / TimeSpan.TicksPerSecond;
if (totalSeconds > data.showTimeLeft.Seconds)
{
totalSeconds -= data.showTimeLeft.Seconds; //Subtract this because it was what was left on the clock, and add a premium currency
PCM.premiumCurrency++;
}
test = totalSeconds % PCM.timeToTick; //temp variable that takes the remainder of the seconds and time to tick, to get the seconds that needs to be put on the clock when we load the game
PCM.maxCurrencyToCollect= data.maxCurrencyToCollect;
GM.premiumCurrency = data.premiumCurrency ;
PCM.showTimeLeft = TimeSpan.FromSeconds(test);
PCM.premiumCurrency = data.premiumCurrency;
mailboxTicks = (int)(totalSeconds/ PCM.timeToTick); //this is just the int of the remainder, so we can add it as premium currency, we take the modulation before and make it the time left. I dont want fractions of a premium currency
if (mailboxTicks + PCM.premiumCurrency < PCM.maxCurrencyToCollect)
PCM.premiumCurrency += mailboxTicks;
else
PCM.premiumCurrency = MaM.maxCurrencyToCollect;
}
}
[System.Serializable]
public class PlayerData
{
public long gameCloseTicks;
public int currencyToCollect;
public int maxCurrencyToCollect;
public int PremiumCurrency;
public TimeSpan showTimeLeft;
}
}
I hope I make more sense now, there should seem like an easier way to do this! The result I get now when I close the game and load it is it goes straight to the max currency even if I only close and reopen the game for 2 seconds.
This question already has answers here:
High resolution timer in C#
(5 answers)
Closed 3 years ago.
I want to have a timer with about 5millisecond resolution. but the current Timer in .Net has a resolution of about 50ms.
I could not find any working solution that creates a high resolution timer, although some claim that you can do it in C#.
In regards to the information that the OP was specifically asking about the Timer class which fires events at regular intervals. I have amended this answer, with my old answer below the horizontal rule.
I tested the following code with the Timer class, and it seems like it can get at least within the 14 - 15 millisecond range on my machine. Try it out for yourself and see if you can reproduce this. So sub-50 millisecond response times are possible, but it can't get down to exactly one millisecond.
using System;
using System.Timers;
using System.Diagnostics;
public static class Test
{
public static void Main(String[] args)
{
Timer timer = new Timer();
timer.Interval = 1;
timer.Enabled = true;
Stopwatch sw = Stopwatch.StartNew();
long start = 0;
long end = sw.ElapsedMilliseconds;
timer.Elapsed += (o, e) =>
{
start = end;
end = sw.ElapsedMilliseconds;
Console.WriteLine("{0} milliseconds passed", end - start);
};
Console.ReadLine();
}
}
NB: The following is my old answer, when I thought the OP was talking about timing things. The following is merely useful information with respect to timing the duration of things, but doesn't provide any way of firing events at a regular interval. For that purpose, the Timer class is necessary.
Try using the Stopwatch class within System.Diagnostics: http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx
You can query it to check if it's high resolution through it's IsHighResolution field. Also, you can check the exact resolution of the Stopwatch:
int resolution = 1E9 / Stopwatch.Frequency;
Console.WriteLine("The minimum measurable time on this system is: {0} nanoseconds", resolution);
If you're worried about where this is actually sourced, the documentation seems to imply that it actually internally calls the lower level Win32 functions:
The Stopwatch class assists the manipulation of timing-related
performance counters within managed code. Specifically, the Frequency
field and GetTimestamp method can be used in place of the unmanaged
Win32 APIs QueryPerformanceFrequency and QueryPerformanceCounter.
What about this one?
public class HiResTimer
{
private bool isPerfCounterSupported = false;
private Int64 frequency = 0;
// Windows CE native library with QueryPerformanceCounter().
private const string lib = "coredll.dll";
[DllImport(lib)]
private static extern int QueryPerformanceCounter(ref Int64 count);
[DllImport(lib)]
private static extern int QueryPerformanceFrequency(ref Int64 frequency);
public HiResTimer()
{
// Query the high-resolution timer only if it is supported.
// A returned frequency of 1000 typically indicates that it is not
// supported and is emulated by the OS using the same value that is
// returned by Environment.TickCount.
// A return value of 0 indicates that the performance counter is
// not supported.
int returnVal = QueryPerformanceFrequency(ref frequency);
if (returnVal != 0 && frequency != 1000)
{
// The performance counter is supported.
isPerfCounterSupported = true;
}
else
{
// The performance counter is not supported. Use
// Environment.TickCount instead.
frequency = 1000;
}
}
public Int64 Frequency
{
get
{
return frequency;
}
}
public Int64 Value
{
get
{
Int64 tickCount = 0;
if (isPerfCounterSupported)
{
// Get the value here if the counter is supported.
QueryPerformanceCounter(ref tickCount);
return tickCount;
}
else
{
// Otherwise, use Environment.TickCount.
return (Int64)Environment.TickCount;
}
}
}
static void Main()
{
HiResTimer timer = new HiResTimer();
// This example shows how to use the high-resolution counter to
// time an operation.
// Get counter value before the operation starts.
Int64 counterAtStart = timer.Value;
// Perform an operation that takes a measureable amount of time.
for (int count = 0; count < 10000; count++)
{
count++;
count--;
}
// Get counter value when the operation ends.
Int64 counterAtEnd = timer.Value;
// Get time elapsed in tenths of a millisecond.
Int64 timeElapsedInTicks = counterAtEnd - counterAtStart;
Int64 timeElapseInTenthsOfMilliseconds =
(timeElapsedInTicks * 10000) / timer.Frequency;
MessageBox.Show("Time Spent in operation (tenths of ms) "
+ timeElapseInTenthsOfMilliseconds +
"\nCounter Value At Start: " + counterAtStart +
"\nCounter Value At End : " + counterAtEnd +
"\nCounter Frequency : " + timer.Frequency);
}
}
I found a solution to this problem in the following blog:
http://web.archive.org/web/20110910100053/http://www.indigo79.net/archives/27#comment-255
It tells you how to use the multimedia timer to have a timer with high frequency. It is working just fine for me!!!
Here is an implementation based on StopWatch timer
https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545
It works on all platforms and is high precision wherever StopWatch.IsHighPrecision == true
Its Elapsed event is guaranteed to be non overlapping (which might be important to know, because state changes inside the event handler may be left unprotected against multi threaded access)
Here is how to use it:
Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}");
Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]");
var timer = new HighResolutionTimer(0.5f);
// UseHighPriorityThread = true, sets the execution thread
// to ThreadPriority.Highest. It doesn't provide any precision gain
// in most of the cases and may do things worse for other threads.
// It is suggested to do some studies before leaving it true
timer.UseHighPriorityThread = false;
timer.Elapsed += (s, e) => { /*... e.Delay*/ }; // The call back with real delay info
timer.Start();
timer.Stop(); // by default Stop waits for thread.Join()
// which, if called not from Elapsed subscribers,
// would mean that all Elapsed subscribers
// are finished when the Stop function exits
timer.Stop(joinThread:false) // Use if you don't care and don't want to wait
Here is a benchmark (and a live example):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3
The results of setting the timer for 0.5 ms on Windows 10:
It is also worth to mention that:
I had the same precision on mono on Ubuntu.
While playing with the benchmark, the maximum and a very rare deviation I saw was about 0.5 ms
(which probably means nothing, it is not realtime systems, but still worth mentioning)
Stopwatch ticks are not TimeSpan ticks. On that Windows 10 machine
HighResolutionTimer.TickLength is 0.23[ns].
CPU usage of the benchmark is 10% for 0.5ms interval and 0.1% for 200ms interval
Pretty late to the party still might be useful for someone looking for an answer as nothing has been changed for over a decade in the topic.
Background
Any .NET delay instructions would always come down to system clock resolution, the one you set with timeBeginPeriod(). No matter if it's a Thread.Sleep(N), Threading.Timer or Waitable.WaitOne(N). Yet time resolution of DateTime.Now() and System.Diagnostic.Stopwatch is way higher so there is a way of implementing precise timing events known as hot loop. Hot loops yet prone to being threatened badly by OS since those tend to occupy processor core at full. And here's what we do to prevent this:
Surrender thread time quant in a hot loop to other threads once there's no more need for it by calling Thread.Sleep(0) or .WaitOne(0)
Below is a code piece showing simple implementation of high resolution scheduler:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// High resolution scheduler.
/// License: public domain (no restrictions or obligations)
/// Author: Vitaly Vinogradov
/// </summary>
public class HiResScheduler : IDisposable
{
/// <summary>
/// Scheduler would automatically downgrade itself to cold loop (Sleep(1)) when there are no
/// tasks earlier than the treshold.
/// </summary>
public const int HOT_LOOP_TRESHOLD_MS = 16;
protected class Subscriber : IComparable<Subscriber>, IComparable
{
public Action Callback { get; set; }
public double DelayMs { get; set; }
public Subscriber(double delay, Action callback)
{
DelayMs = delay;
Callback = callback;
}
public int CompareTo(Subscriber other)
{
return DelayMs.CompareTo(other.DelayMs);
}
public int CompareTo(object obj)
{
if (ReferenceEquals(obj, null))
return -1;
var other = obj as Subscriber;
if (ReferenceEquals(other, null))
return -1;
return CompareTo(other);
}
}
private Thread _spinner;
private ManualResetEvent _allowed = new ManualResetEvent(false);
private AutoResetEvent _wakeFromColdLoop = new AutoResetEvent(false);
private bool _disposing = false;
private bool _adding = false;
private List<Subscriber> _subscribers = new List<Subscriber>();
private List<Subscriber> _pendingSubscribers = new List<Subscriber>();
public bool IsActive { get { return _allowed.WaitOne(0); } }
public HiResScheduler()
{
_spinner = new Thread(DoSpin);
_spinner.Start();
}
public void Start()
{
_allowed.Set();
}
public void Pause()
{
_allowed.Reset();
}
public void Enqueue(double delayMs, Action callback)
{
lock (_pendingSubscribers)
{
_pendingSubscribers.Add(new Subscriber(delayMs, callback));
_adding = true;
if (delayMs <= HOT_LOOP_TRESHOLD_MS * 2)
_wakeFromColdLoop.Set();
}
}
private void DoSpin(object obj)
{
var sw = new Stopwatch();
sw.Start();
var nextFire = null as Subscriber;
while (!_disposing)
{
_allowed.WaitOne();
if (nextFire != null && sw.Elapsed.TotalMilliseconds >= nextFire?.DelayMs)
{
var diff = sw.Elapsed.TotalMilliseconds;
sw.Restart();
foreach (var item in _subscribers)
item.DelayMs -= diff;
foreach (var item in _subscribers.Where(p => p.DelayMs <= 0).ToList())
{
item.Callback?.Invoke();
_subscribers.Remove(item);
}
nextFire = _subscribers.FirstOrDefault();
}
if (_adding)
lock (_pendingSubscribers)
{
_subscribers.AddRange(_pendingSubscribers);
_pendingSubscribers.Clear();
_subscribers.Sort();
_adding = false;
nextFire = _subscribers.FirstOrDefault();
}
if (nextFire == null || nextFire.DelayMs > HOT_LOOP_TRESHOLD_MS)
_wakeFromColdLoop.WaitOne(1);
else
_wakeFromColdLoop.WaitOne(0);
}
}
public void Dispose()
{
_disposing = true;
}
}
The system clock "ticks" at a constant rate. In order to raise the accuracy of timer dependent function*s, call **timeGetDevCaps* which determines the minimum supported timer resolution.
Then call timeBeginPeriod setting the timer resolution to its minimum.
Caution: By invoking timeBeginPeriod, other timer dependent function can be significantly affected such as the system clock, system power usage, and the scheduler. Thus start your application with timeBeginPeriod and end it with timeEndPeriod
The previous example doesn't work unless the frequency is in milliseconds; the perf timer frequency is rarely in ms.
private static Int64 m_iPerfFrequency = -1;
public static double GetPerfCounter()
{
// see if we need to get the frequency
if (m_iPerfFrequency < 0)
{
if (QueryPerformanceFrequency(out m_iPerfFrequency) == 0)
{
return 0.0;
}
}
Int64 iCount = 0;
if (QueryPerformanceCounter(out iCount) == 0)
{
return 0.0;
}
return (double)iCount / (double)m_iPerfFrequency;
}
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceCounter(out Int64 iCount);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int QueryPerformanceFrequency(out Int64 iFrequency);
This returns the perf counter in seconds. The reason you'd use the perf timer is to share a timer with legacy C++ code, or get a more precise timer than the C# StopWatch class.
You could use QueryPerformanceCounter() and QueryPerformanceTimer() as outlined in this article.