Sponsored By

[ARC-Infinitum] Multi-threaded Game Logic, first attempt......

Adventures in threaded programming on the PS3.

Slade Villena, Blogger

February 26, 2010

3 Min Read
Game Developer logo in a gray background | Game Developer

 

Most of the text I read on concurrency use threads in a "task" model; multiple threads on items in array-X, AI agents, "granular + brute force" and all that.

Most of the time, we do game logic in one massive loop.  Sure sure, you'll throw them somewhere in "update()".  Everyone does it.  Its usually the safest bet, and the easiest way to debug.

Game logic, I'd like to define as "event-object producers"; they are aspects of the game that add more enemies to the map, add loot to an inventory, "drops", subtract HP.  They are also game events, status effects on a character, an explosion, AOE attack lasting X-seconds.

I wanted to have game logic in a threaded context make some damn sense; most of the time you'll find a lot of your logic rules fitting in their own categories, mutually exclusive from each other.  These rules are fairly atomic; they don't depend on other rules nor other events, and they are usually the first things you calculate.

 

Example : a hypothetical game logic loop;

 

MASSIVE_UPDATE(void) {

/* do some game events and abilities */ 

 calculateStatusEffects(All_GameObjects);

 calculateSpaceFleetAbilities(All_GameObjects); 

          pruneDeadCharacters(All_GameObjects); 

 

/* Inventory calculations */ 

calculateAmmoAndSupplies(ALL_FLEETS);

calculateEnemyAmmoAndSupplies(ALL_FLEETS);

 

/* AI Update */

SeekAndDestroyFunction(ALL_FLEETS);

SeekAndDefendFunction(ALL_FLEETS);

RetreatAndChickenOutFunction(ALL_FLEETS); 

 

Depending on your design doc, hopefully you have rules that DO NOT depend on EVERYTHING else; you wanna make these logic crunches as "aspect oriented" as possible. Usually, a players coin/gilla/moniez/credits doesn't have anything to do with status effects, the squad-overlord-AI for enemies shouldn't have to count the ammo (it lets the worker-fighter AI's do that for him).

So, for each family/aspect of events, if they are atomic,  throw em in a thread!

 

MASSIVE_UPDATE_THREAD_SCHEDULER() {

  .....

signalThread( calculateStatusEffects(All_GameObjects).functionPointer );

signalThread(  calculateSpaceFleetAbilities(All_GameObjects).functionPointer );

  waitForThreadsToFinish(); 

 

  .......

 

Now of course, there are rules and logic that DO depend on the atomic stuff.  Here's my current solution; have em go in "the second layer".

 

 MASSIVE_UPDATE_ATOMIC_LAYER() {

  .....

signalThread( calculateStatusEffects(All_GameObjects).functionPointer );

signalThread(  calculateSpaceFleetAbilities(All_GameObjects).functionPointer );

  waitForThreadsToFinish(); 

 

  signalLayer(MASSIVE_UPDATE_LAYER_ONE());

 

MASSIVE_UPDATE_LAYER_ONE() {

waitForSignal(); 

 

signalThread(  pruneDeadCharacters(All_GameObjects).functionPointer ); 

waitForThreadsToFinish(); 

signalLayer( MASSIVE_UPDATE_LAYER_TWO());

 

 

....and you have those game events, and rules, that depend, on the rest of your calculations?  Welp....another layer.

 

MASSIVE_UPDATE_LAYER_TWO() {

waitForSignal(); 

signalThread(  SeekAndDestroyFunction(ALL_FLEETS).functionPointer ); 

signalThread(  SeekAndDefendFunction(ALL_FLEETS).functionPointer );  

signalThread(  RetreatAndChickenOutFunction(ALL_FLEETS).functionPointer );  

 

  waitForThreadsToFinish();

........ 

 

 

The layering helps step-up your game sequence, and at the same time have YOU track it easily in your head.  The last thing you need to do is think about ultra-granular tasks just being pumped out to a scheduler.  Its easier to kill off race-conditions and Heisenbugs this way. The more aspect-oriented the bug, the easier they are to find.  Another way to recon for Heisenbugs; the "higher" the bug, whether its a bug in layer two and up, the more DEPENDENCIES that bug might have.

Usually you don't wanna see bugs at the higher levels.  May Root have mercy on you if you do, You'll have to check all its "parent layers" to track down its probable causes. 

 

We're doing this right now in ARC Infinitum, ship-on-ship events, physical-object producing+consuming events, ship-behaviors and AI, all in their little threads, working in a thread hierarchy of signals.

 

Hope this helps!  I would love suggestions on how to make this approach fine-tuned for threads, with respect to game logic.

 

Slade V.

@TeamARC 

Read more about:

Featured Blogs
Daily news, dev blogs, and stories from Game Developer straight to your inbox

You May Also Like