Game Development Community

Plastic Gem #5: Portability

by Anthony Rosenbaum · 06/13/2008 (9:19 am) · 2 comments

Download Code File

i936.photobucket.com/albums/ad202/vincismurf/banner.jpg

Plastic Gem #5: Portability

Difficulty: Easy

For a list of gems see the Gem A Day page.

So if you are like us at Plastic Games you are probably using TGE. Maybe you plan to move to TGE Advanced, maybe you have already started. Merging one project between two engines can be cumbersome; so in order to consolidate code while maintaining scripts between both engines we have added a new global to both engines. This global variable is used to execute the appropriate script code for the appropriate engine.

The challenge of using the very different audio interfaces between TGE and TGEA motivated us to create the AudioManager which hides those differences.

Finally we use the same exe for multiple projects with the same engine (Bixby and Zombie ). While developing each project we have made some useful tools that we don't want to replicate for each project, so we have devised a central location for our tools, not unlike the common or creator folders found in stock Torque.

This resource will show you how to add our new isJugg global to both code bases TGE and TGEA. It will also show you how to add theplastic folder, and AudioManager .

This code can be implemented in TGEA 1.7 and TGE 1.5.2

Let's start with TGE

1) Open game/game.cc under the includes add

// > pg isjugg
   bool gIsJugg = false; // set to false cause TGE is not Jugg
// < pg isJugg

Right about now you might say 'why IsJugg' well GG has consolidated all their code bases into a Super Engine that is call Juggernaut. TGEA 1.7 is actually just the shader engine part of Jugg. On the other hand, TGE 1.5.2 was released prior to full jugg integration so there are small changes between Jugg and earlier projects, for instance the new audio layer uses SFX audio system instead of strict ALX OpenAl audio.

2) In the same file, in the at the top of the GameInit() function add:


// > pg isJugg
   Con::addVariable("isJugg", TypeBool, &gIsJugg);
// < pg isJugg

Now let's move into TGEA

3) Open T3D/gameFunctions.cpp under the includes add:

// > pg isJugg
bool gIsJugg = true;
// < pg IsJugg

4) In the same file at the top of the RegisterGameFunctions() function add :

// > pg isJugg
   Con::addVariable("isJugg", TypeBool, &gIsJugg);
// < pg isJugg

See, I told you things are different in Jugg, anyway do a FULL REBUILD on both engines and verify this worked. Open the console (~) and typing echo($isJugg); you should get a 0 in TGE and 1 in TGEA.


Time to add the plastic, first thing you will notice is we put the plastic folder in different places due to how Jugg behaves.
We shall start with TGE.

5) Place the plastic folder in the project's root. This folder will be at the same file location as the common, or show folders.

6) Add this code chunk to the top of main.cs at the same root location.

// > pg isJugg
if($isJugg){
	$plasticRef = "tools/plastic/";
}else{
	$plasticRef = "plastic/";
}
// < pg isJugg


Can anyone guess where you will put the plastic folder in Jugg?
Before we move on to Jugg lets finish the TGE changes. First we need to execute the whole folder on the client.

7) In YOURGAMEPROJECT/client/init.cs in initClient() function after the guis and script have been executed add

// > pg plasticFolder
loadDir("plastic");
// < pg plasticFolder
Now we will execute the server side files only on the server.

8) In YOURGAMEPROJECT/server/scripts/game.cs in the onServerCreated() function add this before any other files get executed.

// > pg plasticFolder
exec($plasticRef@"server/init.cs");
// < pg plasticFolder

Next we'll be the plastic folder. Within the plastic folder there are two sub folders one labeled audio and another labeled server.

There are two files in the root of the plastic folder called init.cs and main.cs.

9) Let's review what is in the plastic/init.cs

// Plastic Gems - Copyright (C) PlasticGames.com, LLC
// see readme_plastic.txt in the same folder as this source file for more info
// also see readme_tweak.txt for more info on the Tweak system
//-----------------------------------------------------------------------------

echo("--------- Initializing: Plastic Gems ---------");
// NOTE: this file only inits the CLIENT side Plastic Gems code
//       the server code must be init when server is created
//       by calling plastic/server/init.cs
exec("./audio/audioManager.cs");  
echo("--------- Plastic Gems - Initialized ---------");

Inside the plastic/server folder there is also an init.cs, this is a stub file that executes only server code, right now it is empty but in future Gems we will add some goodie.

10) Now review plastic/server/init.cs

//-----------------------------------------------------------------------------
// Plastic Gems-- Copyright (C) PlasticGames.com, LLC
// see readme_plastic.txt in the same folder as this source file for more info
// also see readme_tweak.txt for more info on the Tweak system
//-----------------------------------------------------------------------------

echo("--------- Initializing: Server Side Plastic Gems ---------");
//for future resources
echo("--------- Server Side Plastic Gems - Initialized ---------");


Inside plastic/audio you will notice there is a file audioManager.cs. The audio manager uses the $isJugg global to execute the proper audio code. You will also notice there is more functionality for the TGEA code base than with TGE.

11) Let review the plastic/audio/audioManager.cs code.

//-----------------------------------------------------------------------------
// Plastic Gems - Copyright (C) PlasticGames.com, LLC
// see readme_plastic.txt in the same folder as this source file for more info
// also see readme_tweak.txt for more info on the Tweak system
//-----------------------------------------------------------------------------

// AudioManager        - singleton for top level control of client side sounds

// singleton class
if (isObject(AudioManager))
   AudioManager.delete();
new ScriptObject(AudioManager)
{

};

function AudioManager::playAudio(%this, %sound, %position)
{
   if($isJugg){
       %handle = sfxPlay(%sound, getWord(%position,0),getWord(%position,1),getWord(%position,2) );   
   }else{
      %handle = alxPlay(%sound, getWord(%position,0),getWord(%position,1),getWord(%position,2));   
   }
   return %handle;
}
//-----------specials needs handel for TGE
function AudioManager::stopAudio(%this, %handle)
{
   if(%handle $= "")
      return;
         
   if($isJugg){
     %handle.stop();
   }else{
      alxStop(%handle);   //handle only
   }
}
function AudioManager::isPlaying(%this,  %sound)
{
   if($isJugg){
      %bool = %sound.isPlaying();
   }else{
      %bool = alxIsPlaying(%sound);   //handle only
   }
   return %bool;
}
//-- Jugg only
function AudioManager::isPaused(%this, %sound)
{
   if($isJugg){
      %bool = %sound.isPaused();
   }else{
      error("isPaused only available in Jugg");  
   }
   return %bool;
}
function AudioManager::setVolume(%this, %sound, %volume)
{
   if($isJugg){
      %sound.setVolume(%volume);
   }else{
      error("setVolume only available in Jugg"); 
   }
}
function AudioManager::setPitch(%this, %sound, %pitch)
{
   if($isJugg){
      %sound.setPitch(%pitch);
   }else{
      error("setPitch only available in Jugg"); 
   }
}
function AudioManager::setCallback(%this, %sound, %callbackFunction)
{
   if($isJugg){
      %sound.setCallback(%callbackFunction);
   }else{
      error("setCallback only available in Jugg");    
   }
}

function AudioManager::stopAllAudio(%this)
{
   if($isJugg){
      sfxStopAll(); 
   }else{
      alxStopAll();    
   }
}

On to TGEA, luckily we can copy the whole plastic folder into TGEA, though we will be executing the files slightly differently and placing the folder in another location.

12) Again place this code in main.cs at the root.

// > pg isJugg
if($isJugg){
	$plasticRef = "tools/plastic/";
}else{
	$plasticRef = "plastic/";
}
// < pg isJugg

13) This time put the plastic folders inside of the tools folder.


The function loadDir()acts differently in jugg so we have another way to execute client side scripts. You might have noticed I didn't mention plastic/main.cs. That is because it is TGEA specific.

The tools folder is designed to look for and execute a main.cs in the root of tool sub-folders. It specifically wants a function within labeled InitializingFOLDERNAME(), in our case initializePlastic()

14) Let's review plastic/main.cs

//-----------------------------------------------------------------------------
// Plastic Gems - Copyright (C) PlasticGames.com, LLC
// see readme_plastic.txt in the same folder as this source file for more info
// also see readme_tweak.txt for more info on the Tweak system
//-----------------------------------------------------------------------------
function initializePlastic(){
   echo(" % - Initializing Plastic");
   exec("./init.cs");
}


Now we will execute the server side files only on the server.

15) In YOURGAMEPROJECT/server/scripts/game.cs in the function onServerCreated()add this before any other files get executed.

// > pg plasticFolder
exec($plasticRef@"server/init.cs");
// < pg plasticFolder
Both engines are now setup to use the audioManager for client sounds.

Now to give you a few examples of setting up the audio profiles and descriptions, so that sounds will work with both jugg and TGE without having to make separate files.

16) Open YOURGAMEPROJECT/client/scripts/audioProfiles.cs and redesign your descriptions to match.

// Channel assignments (channel 0 is unused in-game).
// Channel assignments (channel 0 is unused in-game).
// rdbnote: this was moved here because on the server these didn't exist, thus
//    audio descriptions defined on the server were defaulting to channel 1 and
//    being sent to the client, giving the user less control of audio volumes
$SimAudioChannel     = 1;  // weapons, explosions, lightning, etc
$GuiAudioChannel     = 2;  // buttons, clicking, etc
$ChatAudioChannel    = 3;  // quick chat, text chat, etc
$MusicAudioChannel   = 4;  // looping music
$NearAudioChannel		= 5;  // stuff that happens on or near the player
$CrowdAudioChannel	= 6;
// rdbnote: we have room for 8 total channel types


$GuiAudioType     = $GuiAudioChannel;
$SimAudioType     = $SimAudioChannel;
$MessageAudioType = $ChatAudioChannel;

// > pg audio 
if($isJugg){
   new SFXDescription(AudioGui)
   {
      volume = 1.0;
      is3D = false;
      isLooping = false;
      isStreaming = false;

      channel = $GuiAudioChannel;
   };
}else{
   new AudioDescription(AudioGui)
   {
      volume   = 1.0;
      isLooping= false;
      is3D     = false;
      type     = $GuiAudioType;
   };
}

Notice how we reuse the same name but only the descriptions for the proper engine is ever executed, it is a little heavy handed (granted) but it maintains keeping the same file for both engines. On a finer, note also notice how the Type property of the AudioDescription is now called Channel in SFXDescription.

17) In the same vein re-factor your profiles to match this pattern, like this.

if($isJugg){
   new SFXProfile(AudioButtonOver)
   {
      filename = "~/data/sound/buttonOver.wav";
      description = "AudioGui";
      preload = true;
   };
}else{
   new AudioProfile(AudioButtonOver)
   {
      filename = "~/data/sound/buttonOver.wav";
      description = "AudioGui";
      preload = true;
   };
}


The last thing you need to know is how to use the audio manager. Instead of alxplay() alxstop() or their new equivalents sfxPlay() and %handle.stop(); they are handled by our manager. Here is the interface as it currently stands.

Supported by both engines
AudioManager.playAudio( %sound, %position);
AudioManager.stopAudio( %handle);
AudioManager.isPlaying(  %sound);
AudioManager.stopAllAudio();

Jugg only Functions
AudioManager.setCallback(%sound, %callbackFunction);
AudioManager.setPitch(%sound, %pitch);
AudioManager.setVolume(%sound, %volume);
AudioManager.isPaused( %sound);

18) The Next Gem

That is all for this resource. It lacks a really solid example of use but I hope this resource has made it's usefulness clear.

Following this first week of gems involves a lot of work. After doing so your codebase will have a host of new features not all of which have really been fully explained. Suffering through this week is worth the pain, though, because it allows next weeks gems to have a lot more punch per effort. It will cover applications of the gems from this week.

#1
06/19/2008 (8:51 pm)
Nice work there guys- this is indeed a useful gem. Thanks a bunch, and keep 'em coming!
#2
02/18/2010 (5:22 pm)
Integrated nicely into 1.8.2