using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; namespace Conjure.Arcade.Overlay { public class TickEngine { private readonly Action _tickAction; private readonly int _targetFps; private readonly CancellationTokenSource _cancellationSource; private bool _isRunning; public TickEngine(Action tickAction, int targetFps = 60) { _tickAction = tickAction; _targetFps = targetFps; _cancellationSource = new CancellationTokenSource(); } public void Start() { if (_isRunning) return; _isRunning = true; Task.Run(RunTickLoop, _cancellationSource.Token); } public void Stop() { _isRunning = false; _cancellationSource.Cancel(); } private async Task RunTickLoop() { var stopwatch = new Stopwatch(); var targetFrameTime = TimeSpan.FromSeconds(1.0 / _targetFps); while (_isRunning && !_cancellationSource.Token.IsCancellationRequested) { stopwatch.Restart(); try { _tickAction(); } catch (Exception ex) { Debug.WriteLine($"Error in tick loop: {ex}"); } var elapsed = stopwatch.Elapsed; if (elapsed < targetFrameTime) { await Task.Delay(targetFrameTime - elapsed); } } } } }