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.
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.