Search Unity

Stealth Game Project - PlayerHealth Script and Repeating Audio on Death

Discussion in 'Scripting' started by korsobar, May 3, 2014.

  1. korsobar

    korsobar

    Joined:
    Apr 29, 2014
    Posts:
    1
    Hi. I am new to the forums so please be gentle.

    I put together the Stealth tutorial project and generally had no problems. After finishing it, there was one unexpected behavior. Only when the player death animation would begin, the alarm audio would initiate playing repeatedly at a very high rate until the level reset. I know this quickly repeating audio was the audio components tied to the sirens "prop_megaphone_00x" as referenced in the lastPlayerSighting script through an array because I commented out the call to play the audio found here:

    Code (csharp):
    1. for(int i = 0; i < sirens.Length; i++)
    2.         {
    3.             // ... if alarm is triggered and the audio isn't playing, then play the audio.
    4.             if((position != resetPosition)  (!sirens[i].isPlaying))
    5.                 sirens[i].Play();   // <--- what I commented out to end problem temporarily
    6.  
    7.             // Otherwise if the alarm isn't triggered, stop the audio.
    8.             else if(position == resetPosition)
    9.                 sirens[i].Stop();
    10.            
    11.         }
    Once commented out there was no problem. I do not understand why the sirens.isPlaying property would not properly prevent the audio from repeating only when the player was in the dying state? I fixed this entire issue by reseting the playerLastSighting position to its reset value in the PlayerDying function of PlayerHealth script instead of the PlayerDead function. After my change it looked like this:

    Code (csharp):
    1. void PlayerDying ()
    2.     {
    3.         // The player is now dead.
    4.         playerDead = true;
    5.        
    6.         // Set the animator's dead parameter to true also.
    7.         anim.SetBool(hash.deadBool, playerDead);
    8.        
    9.         // Play the dying sound effect at the player's location.
    10.         AudioSource.PlayClipAtPoint(deathClip, transform.position);
    11.  
    12.         // Reset the player sighting to turn off the alarms.
    13.         lastPlayerSighting.position = lastPlayerSighting.resetPosition;  //<--- moved this to here
    14.     }
    15.    
    16.    
    17.     void PlayerDead ()
    18.     {
    19.         // If the player is in the dying state then reset the dead parameter.
    20.         if(anim.GetCurrentAnimatorStateInfo(0).nameHash == hash.dyingState)
    21.             anim.SetBool(hash.deadBool, false);
    22.        
    23.         // Disable the movement.
    24.         anim.SetFloat(hash.speedFloat, 0f);
    25.         playerMovement.enabled = false;
    26.        
    27.  
    28.         // Stop the footsteps playing.
    29.         audio.Stop();
    30.  
    31.                 //  <---moved from here
    32.  
    33.     }
    In this way the player's position is reset as soon as his health hits zero and PlayerDying is called. PlayerDying is called only once, and PlayerDead is called many times (because the PlayerDead and LevelReset are called over and over again until some time has passed), yet that does not explain why the Play function would be called on the audio in the previous code snippet despite the isPlaying being evaluated as part of the conditional? This is not a big problem, as everything else works as expected and I was able to fix it, but I am curious as to why it happens? Why did my fix work? Everything else in my scripts is pretty much exactly from the scripts as shown below the videos as I copied and pasted them before following along with the explanations in the videos.

    Thanks in advance.
     
  2. Heisenberg1901

    Heisenberg1901

    Joined:
    May 11, 2014
    Posts:
    1
    Bump!
    I'd like an explanation as well
    Thanks for providing a fix korsobar
     
  3. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    Is your for loop running every Update()? If so, then what is happening is that one frame you play the clip. The next frame, sirens.isPlaying is true, so we go to the else branch and stop the clip. The following frame, isPlaying is false so we play the clip again.

    I suspect your audio is restarting every other frame.
     
  4. CursingLlama

    CursingLlama

    Joined:
    May 30, 2014
    Posts:
    1
    I just started doing this project myself, and have this issue after only step 6 when the LastPlayerSighted.cs script was written. I put some Debug.Log at the start of the for loop, after the audio is told to play, and at the end of the for loop. They return False(prior to if/else if), True(after Play() is called), True (after the else if with the Stop() call) ... every frame, somehow the isPlaying is being reset to False before the next iteration of the SwitchAlarms() call. If someone knows why that might be, I'd appreciate the heads up because I can't find a way to fix it.

    Also worth note the same problem occurs even using the Done version of the scripts... which leads me to believe it has to do with the AudioSource components themselves.
     
    Last edited: May 30, 2014
  5. Thomas.Amundsen

    Thomas.Amundsen

    Joined:
    Jun 1, 2014
    Posts:
    10
    Bump.... I am having the same problem
     
  6. Zarkonaut

    Zarkonaut

    Joined:
    Jun 3, 2014
    Posts:
    1
    I have been looking at this issue myself for the past several hours. I was determined to find a way to fix it, and I believe I did. Try creating 6 separate alarm_triggered.ogg files and then reapplying a different alarm_triggered audio file to each of the 6 megaphones. It should fix the issue (well.. at least as a hot fix as I don't really know what's going on exactly and there should be a better solution, it doesn't really make much sense to me other than some probable referencing issue with the single audio file and the 6 megaphones).

    The best way to set up the 6 alarm_triggered.ogg files would probably be to save what you've done so far, close Unity, go to the folder you have the project saved and go into Assets\Audio and then make 5 copies of the alarm_triggered.ogg file, and as they come up, simply change their names to alarm_trigger2.ogg, alarm_trigger3.ogg, and so forth to alarm_triggered6.ogg. Once done, Open up Unity and select each of the six individual megaphones, applying each of the corresponding, newly copied numbered alarm_triggered files to the megaphones' AudioSources (i.e. trigger goes to megaphone 001, trigger2 goes to megaphone 002, and so on and so forth).
    Hope it works for you as it did for me. It's simpler to do than it seems, I just gave a longwinded message on how to do it, just so that my results can be more easily reproducable. :) Happy coding.
     
  7. Thomas.Amundsen

    Thomas.Amundsen

    Joined:
    Jun 1, 2014
    Posts:
    10
    Yes, that definitely works, but it is essentially a hack. I wish a Unity developer would come in here and look at this issue. This is one of the tutorials used to market this engine...
     
  8. matthjes

    matthjes

    Joined:
    Jun 22, 2014
    Posts:
    1
    There is another solution: select the alarm_triggered audio file and change the load type from "compressed in memory" to "decompress on load". This should fix the problem, however, don't ask me why and if it's the better solution as I just started using Unity myself :)
     
    shiningcross and unity1080 like this.
  9. unity1080

    unity1080

    Joined:
    Jun 28, 2014
    Posts:
    1
    Man, I just spent a couple hours on this stupid problem and this works! How the heck did you figure that out?
     
  10. shiningcross

    shiningcross

    Joined:
    Jul 24, 2014
    Posts:
    2
    indeed great! I'm impressed!
     
  11. TheXiphias

    TheXiphias

    Joined:
    Oct 27, 2014
    Posts:
    1
    I came across this thread as I was having the same issue as the OP (alarm audio oscillation on player death).

    After some debugging with MonoDevelop the issue I discovered was that after you die, the OnTriggerStay function in the EnemySight script is still being called (which is expected, as the player collider remains within the NPC's trigger collider even when dead.) - the bug here is that there is no test in that function to see if the player is alive before updating the lastPlayerSighting.position - as a result it gets repeatedly reset to the player's current position each time the OnTriggerStay function is called. Meanwhile the PlayerDead() function in PlayerHealth is also still being called continuously and is setting lastPlayerSighting.position back to the defined Reset Position to deactivate the alarms, and as SwitchAlarms in LastPlayerSighting is also being continually called it oscillates between playing and stopping the alarm sound each time the lastPlayerSighting.position is changed whilst the player is dead.

    The solution I found was to change the following line in EnemySight.cs:
    Code (csharp):
    1. void OnTriggerStay (Collider other)
    2.     {
    3.         // If the player has entered the trigger sphere...
    4.         if(other.gameObject == player)
    5.         {
    To the following:
    Code (csharp):
    1. void OnTriggerStay(Collider other)
    2.     {
    3.         // If the player has entered the trigger sphere (and is not dead)...
    4.         if (other.gameObject == player && playerHealth.health > 0)
    5.         {
    So that the lastPlayerSighting.position is only changed if the player is still alive.