Author Topic: Thoughts on Threading to allow Animation with Libtcod (C++/Libtcod)  (Read 14473 times)

Perdurabo

  • Rogueliker
  • ***
  • Posts: 99
  • Karma: +0/-0
    • View Profile
    • Email
(cross-posted from libtcod forums (http://doryen.eptalys.net/forum/index.php?topic=1628.msg9184#msg9184), I'm interested in a wider view)

So here are a few thoughts on how to provide animation in a libtcod roguelike project, building on top of the data and structures used in the C++ tutorial (http://codeumbra.eu/series/complete-roguelike-tutorial-using-c-and-libtcod).

Feel free to criticise and give feedback. Note this is probably incredibly *unoptimised*.

The basic idea is to use threads (sorry! :p). A worker thread would be used to handle animation of certain tiles during any time that nothing is happening.

I've split off any direct access to the TCODConsole::root into a singleton class called Console which is essentially just a wrapper around method calls to TCODConsole::root (and also allows me to work with std::strings in my code, and it handles the conversion to c_str() where possible). All calls to this need to be mutexed.
 
The update (i.e. AI/respond to player input)/render (draw on screen) methods in the Engine/GUI classes (which contain calls to change the screen) would be critical sectioned, and hence the animate thread would wait until such time as these were completed and flushed().

So, we will need a new class, let's call it Animate.

We can then add a Animate* pointer to the Tile struct and to the Actor class.

An animation object basically contains a vector of Frame structs, as well as a interval (int, in milliseconds, with a reasonable minimum granularity such as 0.1 seconds or so), screen x and y positions, and a Frame* NextFrame pointer. A Frame struct is a POD-object which consists merely of a char, a foreground TCODColor and a background TCODColor (note all these can be NULL/not set).

Essentially, this represents a situation where every interval, at the x any y position on screen, the glyph, background and foreground colours will change. (If the values are the same as previous, then no change in that value should occur).

So to animate a tile, one creates an appropriate Animate object and attaches it to the Tile (or Actor!), presumably at map creation.

So. The main thread maintains a vector of Animate* pointers. Every time the FOV is recalculated, this vector is populated (repopulated - again, room for optimisation here) with any Animate* belonging to tiles/actors currently in view. This needs to be critical sectioned/mutex off of course.

That's all the main thread does.

Meanwhile, the worker thread, every 0.1 seconds, or whatever we've decided the mininum granularity is, comes along, and starts at the top of this vector of Animate*. It checks if an animation is due (a simple mod on current time would work here?), and if so, updates the screen with the contents of the *NextFrame struct (and increases/wraps around the *NextFrame pointer to the "next" Frame*. One the entire list is processed it *flushes* the root console.

Note that the worker thread does NOT make any changes to *any* data other than the screen.

I'd probably implement this in Boost, to allow cross-platformness (is that a verb?).

So. Thoughts?

Quendus

  • Rogueliker
  • ***
  • Posts: 447
  • Karma: +0/-0
  • $@ \in \{1,W\} \times \{1,H\}$
    • View Profile
    • Klein Roguelikes
Re: Thoughts on Threading to allow Animation with Libtcod (C++/Libtcod)
« Reply #1 on: January 31, 2014, 10:02:17 PM »
Unless you have a lot of experience with threading, I would not advise introducing threads for anything that doesn't strictly need them. To play animations while waiting for keyboard input you can use non-blocking input:
http://doryen.eptalys.net/data/libtcod/doc/1.5.1/html2/console_non_blocking_input.html?c=false&cpp=true&cs=false&py=false&lua=false
So threading is not necessary here. You would have to alter the game loop to accommodate the new input method, but that's far preferable to adding a heavy dependency and a thread.

That said, this does seem like a reasonable way to encapsulate the hypothetical extra thread and it's a nice way to handle the data requirements of animated tiles. You might want to use smart pointers to avoid resource leaks, though.

If you intend to make animations loop, then remember to give the player a way to turn them off. Fast looping animations can be very distracting.

Perdurabo

  • Rogueliker
  • ***
  • Posts: 99
  • Karma: +0/-0
    • View Profile
    • Email
Re: Thoughts on Threading to allow Animation with Libtcod (C++/Libtcod)
« Reply #2 on: February 01, 2014, 05:11:25 AM »
Ta Quendus for your thoughts.

That said, I'm really not a fan of non-blocking input (it usually ends up having to install a state-machine in the game engine to handle all the possible status changes). Certainly agreed with your other remarks though.

chooseusername

  • Rogueliker
  • ***
  • Posts: 329
  • Karma: +0/-0
    • View Profile
    • Email
Re: Thoughts on Threading to allow Animation with Libtcod (C++/Libtcod)
« Reply #3 on: February 01, 2014, 05:35:28 AM »
Ta Quendus for your thoughts.

That said, I'm really not a fan of non-blocking input (it usually ends up having to install a state-machine in the game engine to handle all the possible status changes). Certainly agreed with your other remarks though.
I find non-blocking io makes for cleaner code without any extra state machines, especially if you use green threading/coroutines.

Kyzrati

  • 7DRL Reviewer
  • Rogueliker
  • *
  • Posts: 508
  • Karma: +0/-0
    • View Profile
    • Grid Sage Games
    • Email
Re: Thoughts on Threading to allow Animation with Libtcod (C++/Libtcod)
« Reply #4 on: February 01, 2014, 06:42:54 AM »
I guess if you're really bent on using another thread and avoid non-blocking input then it'll be hard to convince you otherwise, but non-blocking input is easy compared to playing with threads. That and I doubt you'd run into any resource limitations that would allow you to benefit from a separate thread. I've done animation in libtcod combined with non-blocking input before and it worked great.

Quendus

  • Rogueliker
  • ***
  • Posts: 447
  • Karma: +0/-0
  • $@ \in \{1,W\} \times \{1,H\}$
    • View Profile
    • Klein Roguelikes
Re: Thoughts on Threading to allow Animation with Libtcod (C++/Libtcod)
« Reply #5 on: February 01, 2014, 05:46:19 PM »
I'd rather have a state machine in my code than a second thread, but that's just, like, my opinion.

Perdurabo

  • Rogueliker
  • ***
  • Posts: 99
  • Karma: +0/-0
    • View Profile
    • Email
Re: Thoughts on Threading to allow Animation with Libtcod (C++/Libtcod)
« Reply #6 on: February 01, 2014, 05:52:51 PM »
Aye, though I must admit, I'm preinclined to try threading because its not something I've done before, and I've written roguelikes before with non-blocking input.

Quendus

  • Rogueliker
  • ***
  • Posts: 447
  • Karma: +0/-0
  • $@ \in \{1,W\} \times \{1,H\}$
    • View Profile
    • Klein Roguelikes
Re: Thoughts on Threading to allow Animation with Libtcod (C++/Libtcod)
« Reply #7 on: February 01, 2014, 09:50:26 PM »
My first experiment with threading was for similar reasons. My conclusion was that one's first attempt at threading should be on a throwaway toy project rather than a game that one expects to maintain after it's finished.

Eben

  • Rogueliker
  • ***
  • Posts: 339
  • Karma: +0/-0
  • Controversializer
    • View Profile
    • SquidPony!
Re: Thoughts on Threading to allow Animation with Libtcod (C++/Libtcod)
« Reply #8 on: February 01, 2014, 10:41:27 PM »
Aye, though I must admit, I'm preinclined to try threading because its not something I've done before, and I've written roguelikes before with non-blocking input.

This is a great reason to try threading and a terrible reason to try it on something someone else will ever see or use. I've had a lot of threading experience over the years and the more I learn, the less I use it. It's extremely handy for some things, but it's a nightmare to debug when things go wrong.

That aside, what you describe is basically what I did for animations in SquidLib (my Java Swing library) so that users could write their normal blocking code and not worry about animations. Because of the limits inherent in manipulating Swing objects and the fact that Swing GUIs already run on their own thread, it made sense.