Search Unity

Music Chain - Simple script to give your game's background music a bit more freedom

Discussion in 'Assets and Asset Store' started by Screenhog, Jul 11, 2011.

  1. Screenhog

    Screenhog

    Joined:
    Jul 2, 2009
    Posts:
    498
    Unity has some great resources for adding sound to your scene, however, I've noticed things slightly lacking if you want to add background music. For instance, what if you want to:

    - add background music that consists of two audio clips, an intro and a main, with only the main looping after that point?
    - add a sequence of audio clips, so that you can save a bit of filesize by using the same audio clip in multiple places in the sequence?
    - have a group of audio clips that play in random order, i.e. a forest scene with ambient noises like bird tweets and wind gusts?

    So, I wrote this. It's a script that can be placed wherever you want in your Assets folder. Just add it to a GameObject with an Audio Source attached, add your audio clips, and you have a bit more variety in your musical choices.

    Code (csharp):
    1.  
    2. //To use: Place this script on an empty movie clip with an Audio Source attached
    3. //Do NOT use pitch bending with this program. Pitch bending changes the duration of the clip, making the code inconsistent in its timing.
    4. //Script works best if source audio files are not 3D sounds.
    5.  
    6. var audioClips: AudioClip[]; //What audio will be used? Increase the number to however many clips you need, and then drag the audio clips in.
    7. var playInSequence: boolean = true; //Should audio clips play in a specific sequence? If not, audio will be played randomly
    8.  
    9. var introSequence: String; //Is there a predefined intro? If so, add audio ID numbers in order. Intro is not affected by randomness.
    10. var mainSequence: String; //What is the main sequence of audio? This must not be left blank.
    11.  
    12. /*
    13. In either introSequence or mainSequence, add a series of numbers corresponding to the audio clips you have added, separated by commas.
    14. For example: 0, 1, 2, 2, 1, 2, 1, 3, 4, 1, 3, 2
    15.  
    16. Intro will play first, then the main sequence will play, looping itself if doesSequenceLoop is toggled. If playInSequence is toggled, the main
    17. sequence of numbers will be randomly played, looping infinitely.
    18.  
    19. If you wish to insert silence into either introSequence or mainSequence, add a float.
    20. For example: 0, 0.8, 1, 1.5, 2, 3.0, 3 will play clip 0, then 0.8 seconds, then clip 1, then 1.5 seconds, then clip 2, then 3.0 seconds, then clip 3.
    21. */
    22.  
    23. private var introStringArray: Array;
    24. private var mainStringArray: Array;
    25. private var totalStringArray: Array;
    26. var doesSequenceLoop: boolean = true; //Does the sequence loop? This does nothing if playInSequence is false;
    27.  
    28. private var audioTimer: float; //Timer, marks the beginning of each new audio clip
    29. private var timerOffset: float; //Offset, records the length of the current clip
    30. private var currentClip: int = 0;
    31.  
    32. function Start () {
    33.     if (mainSequence.length == 0) {
    34.         Debug.LogError("Main Sequence must contain at least one number", transform);
    35.     }
    36.    
    37.     introStringArray = introSequence.Split(", "[0]);
    38.     mainStringArray = mainSequence.Split(", "[0]);
    39.     totalStringArray = introStringArray.Concat(mainStringArray); //both sequences added together into one array
    40. }
    41.  
    42. function Update () {
    43.     //Debug.Log (Time.time - audioTimer);
    44.     if (audioTimer + timerOffset < Time.time) { //if the end of the current audio clip is reached
    45.         if (!playInSequence  introStringArray.length == 0) {
    46.             currentClip = Random.Range (0, totalStringArray.length); //if there's no intro and it's set to random, choose the first clip
    47.         }
    48.        
    49.         if (totalStringArray[currentClip].Contains(".")) { //if the chosen element of the array is a floating point number...
    50.             timerOffset = parseFloat(totalStringArray[currentClip]); //set the length of silence to that float
    51.         } else {
    52.             audio.clip = audioClips[parseInt(totalStringArray[currentClip])]; //find the correct audio clip to play
    53.             timerOffset = audio.clip.length;
    54.             audio.Play();
    55.         }
    56.         //Debug.Log("Clip is "+totalStringArray[currentClip]+" in sequence number "+currentClip);
    57.        
    58.         if (playInSequence) { //if audio is to be played in a specific sequence
    59.             if (totalStringArray.length > currentClip+1) { //if we're not at the end of the sequence
    60.                 currentClip++; //advance to the next clip
    61.             } else {
    62.                 if (doesSequenceLoop) { //otherwise, should we loop at all? If yes...
    63.                     currentClip = introStringArray.length; //go back to the beginning of the main looping sequence
    64.                 } else {
    65.                     audio.Stop();
    66.                 }
    67.             }
    68.         } else { //if we're playing audio randomly
    69.             if (currentClip < introStringArray.length-1) { //if the next clip is still part of the intro (which is never played randomly)
    70.                 currentClip++; //go to the next part of the intro
    71.             } else {
    72.                 currentClip = Random.Range(introStringArray.length, totalStringArray.length);
    73.             }
    74.         }
    75.         audioTimer = Time.time;
    76.     }
    77. }
    78.  
    79. //Music Chain created by Chris Hendricks in 2011.
    80. //www.screenhog.com
    81.  
    Any comments/bugs/thoughts? Note that this is not intended to be a music sequencing program, and never will be. It's just a way to accommodate frequent musical tasks.

    EDIT: Note that, as with most all background music, this will work best if the AudioClips are 2D Sound.
     
    Last edited: Jul 29, 2011
  2. twitchfactor

    twitchfactor

    Joined:
    Mar 8, 2009
    Posts:
    356
    This sounds great (no pun intended).

    I'm really surprised at how rudimentary the music playback/manipulation is in Unity. Actually, I guess I'm not. Most people think that fire-and-forget loops of long tunes and the occasional "boing" sound is the only audioscape you need.

    I'll have to check this out, when I get a free moment.

    Now, if someone could just write me better hooks into Tracker playback (like ability to change tempo, mute tracks), I'd be golden!
     
  3. Rush-Rage-Games

    Rush-Rage-Games

    Joined:
    Sep 9, 2010
    Posts:
    1,997
    Looks very cool, I'll check this out when I get a moment...
     
  4. dr.steam

    dr.steam

    Joined:
    Jun 24, 2011
    Posts:
    185
    Very smart, thanks for sharing!
     
  5. Screenhog

    Screenhog

    Joined:
    Jul 2, 2009
    Posts:
    498
    It's not really that surprising. Sound, while it can make or break your game, is not as in-your-face as visuals are. Sound is at its best when it blends in so well that it's not noticed. Unfortunately, this also means that people tend to forget about putting it in their game properly.

    To Unity's credit, though, they do have some great tools for sound effects... it's just the music playback that needed some advancement for me.
     
    Last edited: Jul 21, 2011
  6. namoricoo

    namoricoo

    Joined:
    Apr 14, 2011
    Posts:
    534
    I have a script that will work with any number of files. Let's me give you an example. If your game has 120 AudioClip, 12 Levels, you can Open UniDDatabase, load all 120 clips, they will automatically get organized by primary key. In each level you can specify 12 clips at a time, click repeat all check box. And you'll be good to go with Zero lines of code. Check out the Audio functionality I added to Edy's physics.

     
  7. Screenhog

    Screenhog

    Joined:
    Jul 2, 2009
    Posts:
    498
    That's a neat demo, for sure... I didn't watch the entire 24 minutes, but it seemed like it was more of a sound effects tool than background music. What portion of your video talked about music?
     
  8. namoricoo

    namoricoo

    Joined:
    Apr 14, 2011
    Posts:
    534

    The video is a combination of three scripts. The backgroundAudio, The Collision Detection audio, and the carNoise audio. Three separate files. If you'd like I can make a video to show only the background audio at work.
     
  9. TvM79

    TvM79

    Joined:
    Nov 26, 2010
    Posts:
    53
    Awesome script Screenhog. I have one suggestion that would make it even better IMO, to be able to crossfade between different different instances of this. For example if you want have a scene with different zones, you could have one instance per zone that crossfades between them.

    Thanks again for the script:)
     
  10. Screenhog

    Screenhog

    Joined:
    Jul 2, 2009
    Posts:
    498
    Good idea, TvM79! I'm not sure if this script is the best place for that, though. I'd first try putting multiple empty GameObjects on the stage, each with this script attached. Set up each GameObject the way you want it, and then control the volume levels of each with another external script.

    However, if anyone has a better idea of how to make this work, I'd love for someone to modify it for more functionality.
     
  11. pixelsteam

    pixelsteam

    Joined:
    May 1, 2009
    Posts:
    924
    Great stuff Screenhog, thanks for sharing this.
     
  12. TvM79

    TvM79

    Joined:
    Nov 26, 2010
    Posts:
    53
    I made a basic trigger script that controls fadein/out of sound/music based on triggered animations. My scripting knowledge is very poor, but I wrote this for the people that know even less than me. Hopefully if this solution is a bad one, someone will chime in with a "fix":) The bad thing with my solution is that you have to create animated volume fades and assigne them to the audio object you want to fade. Anyways here's a short "reciepe":

    1.Create object A and assign the sound you want to play.
    2. Assign the animated fades you have created.
    3. Create object B with a collider set to trigger.
    4. Assign this script to B and fill in what you want to trigger the fade in and out, for example "Player".
    5. Write in the names of the animation clips that were assigned to A in the "Fade In" / "Fade Out"
    6. Drag and drop A into the "Fade Object slot on B".

    Code (csharp):
    1. var tags : String;
    2.  
    3. var fadeIn : String;
    4.  
    5. var fadeOut : String;
    6.  
    7. var fadeObject : GameObject;
    8.  
    9.  
    10.  
    11. function OnTriggerEnter(trigger : Collider) {
    12.  
    13.     if (trigger.gameObject.CompareTag (tags)){
    14.  
    15.         fadeObject.animation.Play(fadeIn);
    16.  
    17.            
    18.  
    19.         }
    20.  
    21. }
    22.  
    23.        
    24.  
    25. function OnTriggerExit (trigger : Collider) {
    26.  
    27.     if (trigger.gameObject.CompareTag (tags)){
    28.  
    29.         fadeObject.animation.Play(fadeOut);
    30.  
    31.     }
    32.  
    33. }
    34.  
    35.