4

I am currently writing a programm in c#, for which I need something simular to the Update method in the Unity Engine. Basically a loop that executes a given code, over and over again, at a given tick rate. To re-create this functionality I was planing on just using a while (true) loop and somehow run it without holding up the rest of my thread. Is there any way to do this? My current approach looks like this.

public void Update(int tickrate)
{
    while (true)
    {
        foreach (IAgent agent in Agents)
        {
            agent.Move();
        }

        Task.Delay(tickrate / 60);
    }
}
Ram
  • 131
  • 8
Hale Jan
  • 75
  • 7
  • 1
    Several issues with this: `Task.Delay` would do nothing here, because it is not awaited. So, I guess you are talking about something like a game loop? I am afraid you'll need to get much more sophisticated than that. Especially if you want it executed at (**about**) a given tick rate (remember we are not in a real-time-system!). – Fildor Dec 09 '22 at 14:58
  • 1
    Use a [timer](https://stackoverflow.com/a/57731361/12342238) ? That should work as long as your processing time is much faster than the frame rate. In any case, you should measure the actual time between frames and use this to scale everything that has to do with time. – JonasH Dec 09 '22 at 14:58
  • Unrelated: `tickrate` is in what? Apples per Capita? mph? Celsius? ;D – Fildor Dec 09 '22 at 14:59
  • you might check "asynchronous". – Dominique Dec 09 '22 at 15:01
  • would it perhaps work to use an event combined with the timer? @JonasH – Hale Jan Dec 09 '22 at 15:03
  • @HaleJan Depends what you consider "work". First of all, there are several Timers in .Net ... and they behave differently. Think about what happens if the next tick fires before the previous one has finished? – Fildor Dec 09 '22 at 15:05
  • @HaleJan, why not... but then again, use the API provided by whatever timer you choose to use. Don't reinvent or work against the chosen timer's API... ;-) –  Dec 09 '22 at 15:05
  • Also, more advanced games may decouple different systems, you might for example run your input and core game logic at a fixed 60fps, AI planning at 0.5fps, and the graphics however fast your CPU/GPU can handle, possibly capped by screen refresh rate. I.e. you could make things like this very complicated if you want to. – JonasH Dec 09 '22 at 15:06
  • ^^ Which is why there _are_ Game Engine Platforms. – Fildor Dec 09 '22 at 15:07
  • 1
    Most timers have an event that you attach to, so I'm not sure what you mean by "combined". As Fildor mentions, there can be issues with timers, but if your timer interval is 33ms and moving all the agents takes 2ms you will most likely be fine. – JonasH Dec 09 '22 at 15:11
  • 1
    Using a game engine would be easy, I know. Sadly this programm I am making, is going to be used to simulate movement for a little sensor thing I am building. I want to to test many inputs without having to actually wave lots of lights in front of a sensor and for that i can't really use an Engine. :/ @Fildor – Hale Jan Dec 09 '22 at 15:11
  • Thanks, @JonasH I will check it out. Looks like it might be the best solution! ^^ – Hale Jan Dec 09 '22 at 15:12
  • 1
    Ah, so you are not actually making a game. Don't get me wrong: I didn't say "it won't work". I am just saying: you need to consider some things. But Batesias answer is looking promising. – Fildor Dec 09 '22 at 15:20
  • 1
    Well, I am sort of making a game. It's just not very fun ;D @Fildor – Hale Jan Dec 09 '22 at 15:22

1 Answers1

6

This could be a good use-case for the new PeriodicTimer in .Net 6.

Here's a good video explaining it.

Your game loop could look something like this.

public async Task RunGame()
{
    // Create the timer by defining the period.
    // I used a period of 1/60 seconds (which would result in 60 updates per second).
    var timer = new PeriodicTimer(TimeSpan.FromSeconds(1.0/60));
    while (true)
    {
        foreach (IAgent agent in Agents)
        {
            agent.Move();
        }

        // WaitForNextTickAsync waits for the right amount of time
        // by considering the time spent updating the game state.
        await timer.WaitForNextTickAsync(CancellationToken.None);
    }
}

Note that I used CancellationToken.None for brevity/simplicity. It would make sense to pass a CancellationToken as an argument of the RunGame method.

Batesias
  • 1,914
  • 1
  • 12
  • 22