1

I can't seem to find a good answer to this:

I'm making a game, and I want the logic loop to be separate from the graphics loop. In other words I want the game to go through a loop every X milliseconds regardless of how many frames/second it is displaying.

Obviously they will both be sharing a lot of variables, so I can't have a thread/timer passing one variable back and forth... I'm basically just looking for a way to have a timer in the background that every X milliseconds sends out a flag to execute the logic loop, regardless of where the graphics loop is.

I'm open to any suggestions. It seems like the best option is to have 2 threads, but I'm not sure what the best way to communicate between them is, without constantly synchronizing large amounts of data.

FreshWaterTaffy
  • 230
  • 1
  • 2
  • 17
  • 1
    The best option is *not* to have two threads, unless you really need to. Thread synchronization is not for the faint of heart, and requires significant effort to get right (without it quickly devolving into lock-spaghetti code, anyway). In your main loop, simply call a function every time the number of milliseconds since you last called it exceeds your threshold (and then reset the counter). – Cameron Apr 23 '15 at 22:39
  • _"Obviously they will both be sharing a lot of variables, "_ You can share data between threads. Just using [appropriate locking mechanisms](http://en.cppreference.com/w/cpp/thread/mutex) for reading/writing shared data to prevent race conditions. You don't need to _transfer the data_. – πάντα ῥεῖ Apr 23 '15 at 22:49
  • This is a really simple idea, but you can simply enclose a loop around the code in your main function and use an if statement to evaluate the System clock for *x* milliseconds. – Slothrop Apr 23 '15 at 22:50
  • The free lunch is over. With CPUs quad core and up nowadays you either use multiple threads or you waste the cycles of all but one core. – Fozi Apr 24 '15 at 00:48

4 Answers4

3

You can very well do multithreading by having your "world view" exchanged every tick. So here is how it works:

  1. Your current world view is pointed to by a single smart pointer and is read only, so no locking is necessary.
  2. Your logic creates your (first) world view, publishes it and schedules the renderer.
  3. Your renderer grabs a copy of the pointer to your world view and renders it (remember, read-only)
  4. In the meantime, your logic creates a new, slightly different world view.
  5. When it's done it exchanges the pointer to the current world view, publishing it as the current one.
  6. Even if the renderer is still busy with the old world view there is no locking necessary.
  7. Eventually the renderer finishes rendering the (old) world. It grabs the new world view and starts another run.
  8. In the meantime, ... (goto step 4)

The only locking you need is for the time when you publish or grab the pointer to the world. As an alternative you can do atomic exchange but then you have to make sure you use smart pointers that can do that.

Fozi
  • 4,666
  • 1
  • 28
  • 55
1

Most toolkits have an event loop (built above some multiplexing syscall like poll(2) -or the obsolete select-...), e.g. GTK has g_application_run (which is above:) gtk_main which is built above Glib main event loop (which in fact does a poll or something similar). Likewise, Qt has QApplication and its exec methods.

Very often, you can register timers within the event loop. For GTK, use GTimers, g_timeout_add etc. For Qt learn about its timers.

Very often, you can also register some idle or background processing, which is one of your function which is started by the event loop after other events and timeouts have been processed. Your idle function is expected to run quickly (usually it does a small step of some computation in a few milliseconds, to keep the GUI responsive). For GTK, use g_idle_add etc. IIRC, in Qt you can use a timer with a 0 delay.

So you could code even a (conceptually) single threaded application, using timeouts and idle processing.

Of course, you could use multi-threading: generally the main thread is running the event loop, and other threads can do other things. You have synchronization issues. On POSIX systems, a nice synchronization trick could be to use a pipe(7) to self: you set up a pipe before running the event loop, and your computation threads may write a few bytes on it, while the main event loop is "listening" on it (with GTK, using g_source_add_poll or async IO or GUnixInputStream etc.., with Qt, using QSocketNotifier etc....). Then, in the input handler running in the main loop for that pipe, you could access traditional global data with mutexes etc...

Conceptually, read about continuations. It is a relevant notion.

Basile Starynkevitch
  • 1
  • 16
  • 251
  • 479
0

You could have a Draw and Update Method attached to all your game components. That way you can set it that while your game is running the update is called and the draw is ignored or any combination of the two. It also has the benefit of keeping logic and graphics completely separate.

0

Couldn't you just have a draw method for each object that needs to be drawn and make them globals. Then just run your rendering thread with a sleep delay in it. As long as your rendering thread doesn't write any information to the globals you should be fine. Look up sfml to see an example of it in action.

If you are running on a unix system you could use usleep() however that is not available on windows so you might want to look here for alternatives.

Community
  • 1
  • 1
HSchmale
  • 1,541
  • 2
  • 17
  • 40