SFX code for Zombie Runner (State dependant zombie Sounds

Hey,
looking for some help in implementing different noises depending on whether the zombie is idle, chasing, attacking, or dying. I want the sounds to be able to loop when audio is done, and transition when enemy state changes.

so far, I have created a Enemy Audio controller with the following, as well as altered the EnemyAI with some state public bools.

public class ZombieAudioController : MonoBehaviour
{
[SerializeField] AudioClip zombieIdleSFX;
[SerializeField] AudioClip zombieEnagedSFX;
[SerializeField] AudioClip zombieAttackingSFX;
[SerializeField] AudioClip zombieDyingSFX;

AudioSource audioSource;
EnemyAI enemyAI;

bool isIdle = false;
void Start()
{
    audioSource = GetComponent<AudioSource>();
    enemyAI = GetComponent<EnemyAI>();

}

// Update is called once per frame
void Update()
{
    if (isIdle == true)
    {
        audioSource.PlayOneShot(zombieIdleSFX);
    }
    if (enemyAI.isChasing == true)
    {
        isIdle = false;
        audioSource.PlayOneShot(zombieEnagedSFX);
    }
    if (enemyAI.isAttacking == true)
    {
        audioSource.PlayOneShot(zombieAttackingSFX);
    }
    if(enemyAI.isDying == true)
    {
        audioSource.PlayOneShot(zombieDyingSFX);
    }
}

}

When I play, the effect works, but the initial idle audio gets played every frame. but when it transitions, the idle sound continues, and the chasing audio only plays once and repeats correctly. same for the attacking audio. Can someone give me some help on this?

Hi,

Have you already looked the PlayOneShot method up in the API? This is from the description:

AudioSource.PlayOneShot does not cancel clips that are already being played by AudioSource.PlayOneShot and AudioSource.Play.

Since bools get checked that don’t have anything to do with one another, it might be that isIdle is true when you expect other sounds to be played. If isIdle is true, audioSource.PlayOneShot(zombieIdleSFX); gets executed. And if the audioSource is already playing a clip after this line of code, other PlayOneShot method calls won’t stop the audio source. That’s just a theory, though.

Add Debug.Logs to your code to see what’s going on during runtime.

Playing in the Update loop is likely overloading the AudioSource, and as Nina pointed out PlayOneShot() won’t interrupt other sounds (as essentially a new sound is spawned). What you really want is to only update these sounds when your state changes…

Let’s start with an Enum for the state, which you will calculate in Update():

Enum State
{
      Idle,
      Chasing,
      Attacking,
      Dying
}
State currentState=State.idle;
State oldState = State.idle;
void Update()
{
     if(isIdle) 
     {
          currentState=State.idle;
      }
     if(enemyAI.isChasing)
     {
           isIdle=false;
           currentState=State.Chasing;
      }
      if(enemyAI.isAttacking)
      {
            currentState = State.Attacking;
      }
      if(enemyAI.isDying)
      {
            currentState = State.Dying;
      }
      if(currentState!=oldState) 
      {
             ChangeAudioClip();
      }
      oldState=currentState;
}

void ChangeAudioClip()
{
      audioSource.Stop();
      switch(currentState)
      {
            case State.Idle: audioSource.clip = zombieIdleSFX; 
                                      audioSource.loop = true;
                                      break;
            case State.Chasing: audioSource.clip = zombieEngagedSFX;
                                             audioSource.loop = true;
                                             break;
            case State.Attacking: audioSource.clip = zombieAttacking SFX; 
                                               audioSource.loop = true;
                                               break;
            case State.Dying: audioSource.clip = zombieDyingSFX; 
                                         audioSource.loop = false; //Assuming once it's done dying, it's quiet
                                         break;
     }
     audioSource.Play();
}

What we’re doing with this is capturing the state of the zombie in the Update loop, but not actually playing it. We then compare that to the previous state (oldState). If it’s changed, then we call our ChangeAudioClip method.
In the ChangeAudioClip method, we’re setting the value of the clip and loop properties based on our enum. We’re then Play()ing the audioSource. The sounds will continue to loop, as long as the state doesn’t change. If it changes, then the sound is replaced with the correctly changed sound. I turned looping off in the last one, as generally speaking, once you’re dead (really dead, not Walking Dead), you’re dead. Adjust this as needed for your game.

Hey Brian, Thanks for the input.

Will the Audio controller now become where other scripts get the current state? Or do i have to create references to any other script that controls animations in order for the audio to line up with animation state changes?

At the moment I have a set of public bools representing the enemy state in SC EnemyAI and getting that within the Audio Controller. Now that I have an Enum State in Audio Controller, what changes need to be made to Enemy AI in order to trigger the state change?

I’m not familiar with the rest of the project you’re working on. I simply used the criteria you were already using in the original script to generate a state for this script.

As far as maintaining a global state that all scripts can access to get the current state, this would be the wrong script, ideally that would be in the controller itself… then the ZombieAudioController would poll the AIController for the state, compare it to the last frame’s state, and change the sound if it’s changed. Alternatively, when the state changes in AIController, you could use an event to instruct the ZombieAudioController to change the audio clip. (depending on where you are in the courses, you may not be familiar with events).