Translations of this page:

10. Sound and Music

WME has very extensive support for sound and music. You can make your game sound quite well without having to needlessly restrict yourself. In this chapter we're going to add a few sounds and a music to our game to make it more alive.

Before we do that, we have to clarify a few things. WME currently supports two audio formats ogg/vorbis and wav.
The main difference is let's say a tradeoff between quality and size.

  • ogg/vorbis - is a compressed format, which allows much smaller sizes but there's an issue of quality loss, although if you choose good ratio, it's not recognizable for many people.
  • wav - is an uncompressed format which is not that usable for music(in CD quality 1 minute of stereo sound takes cca. 5.3MB), but it can prove useful for little short sounds as there's no delay in decompressing sound.

We'll start with the resource kit for chapter 10. We have there two music tracks, we're going to use. Metric.ogg is the music for a dream scene and for the circle and voyages.ogg is the music for the rest. Create new folder in the data folder and call it music and copy those two files in there.

Let's speak about a music a bit.

We access music from the Game object and we have basically two options how to play our music. Either we call

Game.PlayMusic(ogg file);

Or we use a specific music channel to do that

Game.PlayMusicChannel(ogg file);
  • Game.PlayMusic(file, Looping, Loop Start) has two additional arguments - Looping (true if the file should repeat all over) and Looping Start in ms, which defines from what position the music should repeat.
  • Game.PlayMusicChannel (channel, file, Looping, Loop Start has also the number of channel used to play music.
  • Game.StopMusic(); stops the current music.
  • Game.StopMusicChannel(channel); stops the music running in specified channel.
  • Game.PauseMusic(); pauses the music (PauseMusicChannel(channel))
  • Game.ResumeMusic(); resumes the paused music (PauseMusicChannel(channel))
  • Game.IsMusicPlaying(); returns true if Music currently plays (IsMusicChannelPlaying(channel))
  • Game.GetMusicVolume(); returns number from 0-100 with current volume (GetMusicChannelVolume(channel)
  • Game.SetMusicVolume(volume); sets new volume (SetMusicChannelVolume(channel,volume)

There are more methods but for now we'll be happy with those. Now what's the difference from using channel versions? You can use more music tracks at the same time, you can crossfade them (one goes volume down while the other volume up for seamless transition).

Open the scene_init.script of the Dream scene and modify the beginning.

#include "scripts\base.inc" 
 
// here comes the stuff which initializes the scene 
 
Game.Interactive = false; 
 
Game.PlayMusic("music\metric.ogg"); 
Game.SetMusicVolume(70);

Test the game and you immediately see, that it gets better atmosphere. But we'll now change the mechanism to the music channels so we can make a neat crossfades. Modify the lines to read:

  Game.PlayMusicChannel(0,"music\metric.ogg",true); 
  Game.SetMusicChannelVolume(0,70);

We've first of all used the music channel 0 for the music playing, but we've also set the track to looping track by providing true as a looping value.
For crossfading we use Game.MusicCrossfade(channel1,channel2,time, swap); which is a method decreasing volume of channel1 and increasing volume of channel2 in time. Swap means that what was in the channel1 gets to channel2 and vice versa so new track is - after the crossfade - in channel1. Note that this is automatically true, so if you don't want the swap you have to specify false as the last parameter.

In this light, we'll open the Street scene and modify scene_init.script at the bottom to read:

//////////////////////////////////////////////////////////////////////////////// 
if(!StateStreet.Visited) 
{ 
  StateStreet.Visited = true; 
  Game.PlayMusicChannel(1,"music\voyages.ogg",true); 
  Game.MusicCrossfade(0,1,6000); 
  // this is our first visit in this scene... 
}

Simply said, we start the new music track in channel 1 and then crossfade both channels to achieve the fluent transition from track to track.
Last but not least we'll return back the original track but this time we'll use new set of functions just for the sake of trying them.

Open the scene_init.script of the Circle scene and to the beginning (just below the include) put:

 Game.Interactive = false; 
 for (var volume=Game.GetMusicChannelVolume(0);volume>0;volume=volume-1) 
 { 
    Game.SetMusicChannelVolume(0,volume); 
    Sleep(20); 
 } 
 
 Game.StopMusicChannel(0); 
 Game.PlayMusic("music\metric.ogg",true);   
 
 Game.Interactive = true;

This means, that first we set the game to noninteractive mode so it would really complete. Then we fadeout the volume programatically (starting at actual volume and going to 0) and finally stopping the music channel track and playing the metric again.

Last thing we'll show here is a more elegant way how to do this through a daemon (we've spoken about those already). Create an empty file called music_fade.script and put inside the following code:

 for (var volume=Game.GetMusicChannelVolume(0);volume>0;volume=volume-1) 
 { 
    Game.SetMusicChannelVolume(0,volume); 
    Sleep(20); 
 } 
 
 Game.StopMusicChannel(0); 
 Game.PlayMusic("music\metric.ogg",true);

Then modify the scene_init.script to look like this (also simplify it a bit):

#include "scripts\base.inc" 
 
Scene.AttachScript("scenes\Circle\scr\music_fade.script"); 
 
actor.SkipTo(507, 526); 
actor.Direction = DI_DOWN; 
actor.Active = true; 
actor.GoTo(498,474);

Basically we've shifted the fadeout/song change logic to separate thread so it won't delay the code execution and looks more elegant as it runs in the background. Also you could attach such a script in the Scene Editor, but personally I prefer having full control over what I'm loading in the scripts.

Let's leave music for a while and look at the sound designing. We've already seen how to trigger sound in the sprite editor and also that we can assign sounds to the entities in the Scene Editor. But here we'll look at ways how to use sounds from the scripts.

Create a folder called sounds in our trusty data folder and copy the resource sounds to it.

With sounds we have apart from volume also another possibility - we can adjust their panorama1) by attaching them to positioned entities. Open the scene Street in the Scene Editor and create a sprite entity called Fun. Turn off Interactive for it and shift it to position 1, 370. Also be sure that you have Sound Panning option set on for that entity.

Next open the scene_init.script and to the very bottom write the following code:

var e = Scene.GetNode("Fun"); 
 
e.PlaySound("sounds\drnci.ogg"); 
 
for (var x=1; x<900;x=x+1) 
{ 
  e.X = x; 
  Sleep(5); 
} 
this.StopSound();

As we could easily expect, we can play sound through PlaySound (similary to PlayMusic) and stop it through StopSound (Similary to StopMusic), we can PauseSound or ResumeSound - we've seen it before and the logic is the same as in the music but here we're doing more magic.
First we get a Node (our newly created sprite entity Fun) and attach sound to it. Then we move the entity on the screen and sound follows the position on the screen so it runs from left to right.
If we assigned any imagery to the sound, we could easily have a passing car followed by its sound. Neat, isn't it?

But we've here uncovered something even more substantial! Sounds can be attached to any kind of object. So we can have Game.PlaySound, Scene.PlaySound, actor.PlaySound, entity.PlaySound etc. We are not limited by anything else than performance issues. This way you can make your game sound incredibly alive.

Another very useful method is IsSoundPlaying() (which returns true or false - we'll make use of it soon enough).

Just for the record as a very advanced feature we can set a realtime ambience for the sound, we have triplet of the functions:

SoundFXEcho, SoundFXReverb and SoundFXNone, but usage is beyond the scope of this book. For inquisitive minds, it can be great to have one sound and then adding realtime ambience through WME rather than having multitude of sounds.

But back to our modest needs. As a demonstration we'll handle a door. There are three states - locked door, unlocking the door and open door squeak.



First we have to modify scene_init.script a bit to throw away again our moving sound and make the music lower as it's too loud.

if(!StateStreet.Visited) 
{ 
  StateStreet.Visited = true; 
  Game.PlayMusicChannel(1,"music\voyages.ogg",true); 
  Game.SetMusicChannelVolume(1,50); 
  Game.MusicCrossfade(0,1,6000); 
} 
 
//End of the file.

Then in the door.script we adjust the sounds:

#include "scripts\base.inc" 
 
global DoorUnlocked; 
 
on "LeftClick" 
{ 
  Game.Interactive = false;  
  actor.GoTo(695,344); 
 
  if (!DoorUnlocked) 
  { 
    this.PlaySound("sounds\locked.ogg"); 
    while (this.IsSoundPlaying()) Sleep(1); 
    actor.Talk("Rats! The door is locked. What should I do now?"); 
  } 
  else 
  { 
    Game.PlaySound("sounds\doorsqueak.ogg"); 
    Game.ChangeScene("scenes\room\room.scene");   
  } 
  Game.Interactive = true;  
 
} 
 
on "Lockpick" 
{ 
  Game.Interactive = false;  
  actor.GoTo(695,344); 
 
  this.PlaySound("sounds\unlock.ogg"); 
  while (this.IsSoundPlaying()) Sleep(1); 
 
  DoorUnlocked = true;  
  Game.DeleteItem("Lockpick"); 
  actor.Talk("Ha! I've unlocked the door. The lockpick broke apart in the process though."); 
 
  Game.Interactive = true; 
}

Starting from beginning we read:

  if (!DoorUnlocked) 
  { 
    this.PlaySound("sounds\locked.ogg"); 
    while (this.IsSoundPlaying()) Sleep(1); 
    actor.Talk("Rats! The door is locked. What should I do now?"); 
  }

We play sound of the locked door and wait for the sound to end so the player can comment on the locked door.

  else 
  { 
    Game.PlaySound("sounds\doorsqueak.ogg"); 
    Game.ChangeScene("scenes\room\room.scene");   
  }

Now this might need a bit of explaining. Everything in the Scene terminates with the ChangeScene and the sound would have been cut off if we attach it to door. But Game object lasts for the whole game and by attaching our sound to Game object, we make sure, that the sound is played while the scene is being changed. This also creates a great joining moment soundwise because the scene change suddenly becomes more vivid.

And lastly our good old unlocking sound.

 this.PlaySound("sounds\unlock.ogg"); 
 while (this.IsSoundPlaying()) Sleep(1);

We again wait for the sound to end and then go on. Remember this construction, you'll often use it! Of course you can also make sound loop exactly the same way as you loop music. Full function looks like PlaySound(filename, Looping, Loop Start);

Sound design is quite a hard work. You can easily tell, that sounds from the resources pack don't match the environment at all. They are actually direct rip offs from Ghost in the Sheet, but for demonstrating principles they're more than suitable. Don't forget that propper sound design is at least 60% of atmosphere. Don't forget to add good ambient sounds (sounds of the night town would be good for the street for example with some crickets, ocassional dog barking etc.) and be very precise. With good sounds your game immediately jumps up in quality.

1) by panorama I mean position in the stereo channels
 
wmebook/ch10.txt · Last modified: 2008/01/10 14:12 by metamorphium
Recent changes RSS feed Creative Commons License Driven by DokuWiki