Translations of this page:

Working with complex sound layers

You could be surprised how much does sound of your game influence the overall impression of your game. I was spending quite a lot of time to discover possibilities of sound implementing into WME and I have to say, that mechanism I am using now is quite sufficient for my purposes. But enough of that and let's have a look into some ideas, how to make your game sounding better.

First of all you should probably wonder what I am talking here about. I am talking of course about background sound layers. Not the sound of doors you open or footsteps of our main hero. I am talking about random sounds in the street, wildlife in the forest, scary things in the haunted house or seagulls by the sea. The better your game sounds the more immersive your game will be. So let's look at some of my proposed technologies of making sound layers.

Note that for these ideas you should be probably quite at home with objects and with basic programming concepts, because otherwise you'll get lost soon. I won't go in details explaining basic stuff, because as you were aware already, this article is advanced. :-)

Thinking in layers

When creating background sounds, I prefer to think in complete layers. So for example in one forest location I can have two layers of birds, one layer of some other animals, one layer of insects and one layer of environmental effects. Each layer will have different randomization so they will sound very natural. So let's say that we create a couple of global variables (I will explain why global later).

global SndLayer1; //we will attach objects to them later 
global SndLayer2;

Then we should create some script which will consist of a few methods:

method Initialize(group,range,minfreq,maxfreq,volume); 
method Preload(); 
method Done(); 
method Pause(); 
method Resume(); 
method RunDaemon();

Initialize is the method you want to call when you initialize your new sound layer. I am using a few parameters which I will try to explain now.

  • group – is path to sound file of one logical layer. I am using numbering convention so for example I have a folder birds and inside I have bird1.ogg,bird2.ogg,bird3.ogg etc. But to this parameter I supply only «birds/bird». The rest is handled by script
  • range – just a number how many sounds are there in the group
  • minfreq (in ms) what is the shortest possible interval script should wait for another sound from layer
  • maxfreq (in ms) what is the biggest interval script should wait for another sound from layer
  • volume – well… volume of the layer

Preload is internal method and I am using it just because of good performance since I use many samples and without preloading them game really stutters.

Done is for destroying sound layer

Pause and Resume only sets some flag which is used in the main sound player

RunDaemon is the simple method which calls this.AttachScript of the body script, which I'll cover later.

Now there is no rocket science about Initialize function. It just attaches parameters to this object and at the end calls Preload(); Function Preload is more interesting and I am dealing with it like this:

var tmp_snd; 
 
//loop through all samples from 1 till last one 
for (var t=1;t<this.SD_range+1;t=t+1)  
{ 
 
// create temporary entity to hold our sound 
  var tmpent = Game.CreateEntity();  
 
//Preload the sound      
  tmpent.LoadSound("sounds/" + this.SD_sound_group + t + ".ogg");  
  this[t-1] = tmpent; //assign it to array created from our object 
} 
 
this.infinite = true; // some flags for main daemon 
this.paused = false; // we reset the paused flag just to be sure. :-)

Another funny method is Done. Again some code follows:

if (this.IsScriptRunning("scripts/sound_daemon/sound_body.script")) 
  this.DetachScript("scripts/sound_daemon/sound_body.script"); 
 
if (this.infinite) 
{ 
  this.DetachScript("scripts/sound_daemon/sound_body.script"); 
  this.infinite = false; 
 
  var tmpent; 
  this.SD_volume = 0; 
  for (var t=1;t<this.SD_range+1;t=t+1) 
  { 
     tmpent = this[t-1];         
     tmpent.StopSound(); 
     Game.DeleteEntity(tmpent); 
  } 
}

Note that I have some safeguards here so even if you call Done to inexistent layer, you will not get Script Compiler error on screen.

I don't believe it's necessary to write about Pause / Resume methods, because it will be clear from the body script. Let's look at RunDaemon method.

//Safeguard  
if (this.IsScriptRunning("scripts/sound_daemon/sound_body.script")) 
  this.DetachScript("scripts/sound_daemon/sound_body.script"); 
 
//Attach the looping part of the script 
this.AttachScript("scripts/sound_daemon/sound_body.script");

Let's look at the body now:

while(!this.infinite) Sleep(1); 
 
var ent = this[0]; 
 
while(this.infinite)     
{ 
  var wait = Random(this.SD_minfreq,this.SD_maxfreq); 
  if (this.infinite) Sleep(wait); 
 
  if (this.infinite && ent!=null) 
  { 
     if (!ent.IsSoundPlaying()) // better sound not playing now. 
     {     
        var t = Random(0,this.SD_range-1); //grab random sound number 
        ent = this[t]; // take it from array. 
        ent.SetSoundVolume(this.SD_volume); 
        if (this.infinite && (!this.paused))  
        { 
          ent.PlaySound(false); 
        } 
     } 
  } 
 
  if (this.infinite) Sleep(20); 
}

Note that I have safeguards on almost each line. I am practicing quite a defensive coding so I prefer handled case than error message on screen. First line arise from the fact, that it takes some time to attach the script and preload the sounds. So I am setting infinite at the end of Preload method and this way my newly attached script waits for all sounds being loaded. Then you can see that I wait some ammount of time in between of max and min specified in Initialize. Again some safeguards (especially usefull when destroying sound layers. The rest is pretty self explanatory.

This approach gives you one major advantage. Sound layers works for multiple screen, so you can use them until you come into different environment. Also they play even in the scene transition so there is not a Moment of Silence included as opposed to attaching sounds to entities on screen. :-)

Lastly you can create quite a lot of layers and easily manage them so the rest rests upon your imagination. I hope that you found this article at least a tiny bit useful and hope that it will help some of you.

Mnemonic: could you please make those code boxes wider, it takes some time reformating code to fit those boxes, but there is quite a big reserve till the end of page. ;-)

 
kbase/working_with_complex_sound_layers.txt · Last modified: 2008/10/24 15:03 by Jyujinkai
Recent changes RSS feed Creative Commons License Driven by DokuWiki