informa
3 min read
article

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

Adventures in threaded programming on the PS3.

 

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 

Latest Jobs

Disbelief

Chicago, Illinois
05.10.22
Producer

Build a Rocket Boy Games

Edinburgh, Scotland
05.12.22
Lead Animation Programmer

Windwalk Games

Austin, Texas
05.16.22
Game Designer

Sucker Punch Productions

Bellevue, Washington
05.10.22
Campaign Director
More Jobs   

CONNECT WITH US

Register for a
Subscribe to
Follow us

Game Developer Account

Game Developer Newsletter

@gamedevdotcom

Register for a

Game Developer Account

Gain full access to resources (events, white paper, webinars, reports, etc)
Single sign-on to all Informa products

Register
Subscribe to

Game Developer Newsletter

Get daily Game Developer top stories every morning straight into your inbox

Subscribe
Follow us

@gamedevdotcom

Follow us @gamedevdotcom to stay up-to-date with the latest news & insider information about events & more