John Lemon's Haunted Jaunt Extra Audio
Updated a year ago
Adding and improving some audio features in John Lemon's Haunted Jaunt
A while ago, I worked with Unity to make sound effects for their 3D beginner project: John Lemon's Haunted Jaunt. During the project, I had some ideas for implementation and additional sound effects that didn't fit the scope and goal of the project. There are a couple of small tweaks that you can add to your project to give it a little bit more shine!
For this project, you'll need to have completed the original tutorial, and you'll need to download some extra sounds from this one drive link. This link contains some separate footstep assets, and an extra sound for the showering ghost.
The Things I want to go over are:
  • Audio listener placement to enable 3D positional audio
  • Animation tagging for more versatile footstep sounds
  • Randomization to looping sounds to prevent unwanted phasing
  • Giving that shower ghost some sound

Audio listener placement

In the tutorial, the audio listener gets moved from the camera to John Lemon's model. The problem that arises, and gets addressed in the tutorial, is that when the player turns towards the camera, the panning gets turned around too! Instead of fixing this issue by changing the audio sources, we are going to fix it by changing the audio listener.
Start off by removing the listener form John Lemon, by clicking on 'Remove Component' and 'Apply to prefab'. Next, add an empty child object to the Main Camera, this will be our new listener. Position this empty child object just a bit behind John, I have it at 0, 0.1, 7. Because the main camera is rotated downwards, you'll have to rotate the empty child object the other way, so it's pointing forward instead of downwards. Copy the rotation parameters of your main camera, and paste them onto the empty child object, but add an "-" in front of it. Finally, add the audio listener to the empty child object. It should look like this:
Because the listener is parented to the camera, it will always have the same orientation as it. So even when John turns around, panning will stay consistent with the player's view.
You can now also go into the Ghost prefab, and set the Spread to 0, so that 3D panning is working again! After you do that, you can also un-check `Force To Mono` on the `SFXGhostMove` sound in the Audio Asset folder.
Because 3D panning now works, you can also go into the "Light" prefab, and set it's spread to 0, and it's Spatial Blend to 3D. Don't forget to set the max distance of those lights to 5 and up the volume to 1. Since there are bulbs in the lights, you can also remove the `SFXCandle` component. I also changed the Min Distance to plateau at around 2.25 distance, but that's up to you! My settings look like:

Animation tagging

In the tutorial, a looping audioclip is used for footstep sounds. This is fine for the project, but we can do a bit more if we add a little script and modify the existing scripts that trigger audio for John.
First, removing the part in the script that triggers the audio when John starts and stops walking. It should look like this afterwards:
if (isWalking) { } else { }
We're going to keep those references for later.
Next, add a new script to JohnLemon, and call it `AudioTrigger`. This script will get triggered through animation events later. Also, the script will change the footstep sounds based on the type of ground John is walking on. The script will look like this:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class AudioTrigger : MonoBehaviour { public AudioClip[] audioClipWood; public AudioClip[] audioClipTiles; public AudioSource audioSource; public int floorType = 0; private void OnTriggerEnter(Collider other) { if(other.tag == "Tiles") { floorType = 1; } } private void OnTriggerExit(Collider other) { if(other.tag == "Tiles") { floorType = 0; } } public void triggerAudio() { audioSource.pitch = (Random.Range(0.8f, 1.1f)); switch (floorType) { case 0: audioSource.PlayOneShot(audioClipWood[Random.Range(0, audioClipWood.Length)], 1f); break; case 1: audioSource.PlayOneShot(audioClipTiles[Random.Range(0, audioClipTiles.Length)], 0.7f); break; } } }

In the script, we have 2 lists of audioclips. 1 for wood and 1 for tile footsteps. We'll declare those later in the inspector. The audiosource will also be declared in the inspector, this is just preference and makes it easier to have multiple audiosources on the same object. We'll later add box colliders around the segments with tiles, which will then change the floor type. Because there are only 2 floor types, the script only checks if you've entered or exited that trigger. If you want to add more floortypes, you can do so by checking for other tags which you can add yourself. Don't forget to give it a unique case number. Here, tiles are floortype 1, and wood is floortype 0.
Note: I can't say that hardcoding the volume in the script is a best practice, but for simplicity of the tutorial I'll keep it like that.
Next, back in the inspector, make the size of the wood type sounds 6, and add the 4 SFXFootstep_Wood sounds. Because number 4 has a creak in it, and you only want that to trigger occasionally, put SFXFootstep_Wood01 and 02 in Element 4 and 5. This makes it less likely that the creak footstep gets triggered multiple times in a row. Give the Tiles audio clips a size of 4, and add the 4 different SFXFootstep_Tiles sounds. Finally, reset the audio source that's already attached to John, and declare it in the Audio Source of the Audio Trigger script.
You can make the floor type not-public if you want.
On to tagging the animation. Go the the `John@Walk` file in the `Animation>Animation` folder. In the inspector, you'll need to go to the `Events` tab, and scroll the animation to frame 15, at around 38%. Press the `Add Event` button, which will place an event marker on the animation. Under `Function`, fill in `triggerAudio` and under `Object` give it the `AudioTrigger` Script. Repeat that step at frame 34, at around 88%. Click `Apply` and the animations are tagged! Now, whenever John passes those events, the `triggerAudio` in the `AudioTrigger` script will be called, and a footstep will play!
To finish things up in the script department, go back to the `PlayerMovement` script, and add a new bool named `finalStep` and set it to false. Then add the following lines of code to the if (isWalking) part of the script:
if (isWalking) { if(finalStep == false) { finalStep = true; } } else { if (finalStep == true) { FindObjectOfType<AudioTrigger>().triggerAudio(); finalStep = false; } }
This makes it so that if John stops walking, it will trigger one last footstep. We can't trigger the idle animation, since it will just play a footstep sound whenever it loops.
Now, to add those footstep changing colliders! Start by adding an empty game object to the scene, and calling it `TilesCollider`. Give it a Box Collider and place it around the bathroom. This is easier when you disable the camera effects, which can be done with the toggle above the scene view, and set the view to orthographic. Click `Edit Collider` to set the bounds around the room. Make sure it fits horizontally and vertically. Do the same for the dining room area. Then, for both colliders, tick the `Is Trigger` mark. Else John will not be able to enter those areas as he'll collide with the box. Finally, add the Tiles tag, which can be done by clicking the drop down menu `Tag` on the `TilesCollider` object, clicking `Add Tag` and adding the `Tiles` tag. Make sure to then go back to the `TilesCollider` object to set the tag on `Tiles`. Now, when John walks from floor type to floor type, his footstep sounds will change and he will make a sound when he stops walking.

Adding randomization to looping sounds

When multiple instances of the same looping sound are played close together, there can be some unwanted phasing, which is cased by playing the same sound but just slightly out of sync. To fix this, we are going to create a script that changes the pitch and start time of all looping sounds. By changing the pitch, we also change the playback speed just a little. So even if the sounds do end up starting at the same time, they will soon drift apart.
Create a script called `audioController` and add these lines.
using System.Collections; using System.Collections.Generic; using UnityEngine; public class audioController : MonoBehaviour { void Start() { GetComponentInParent<AudioSource>().pitch = Random.Range(0.8f, 1.1f); GetComponentInParent<AudioSource>().time = Random.Range(0f, GetComponentInParent<AudioSource>().clip.length); } }
At the start of the scene, this script will get the current AudioSource attached to the parent, and set the pitch between 0.8 and 1.1, it then sets the start point of the audio clip to be anywhere between the beginning and end of the current audioclip. Now, add this script to all objects with a looping sound, like the Ghosts, the candles and the lights.

Adding sound to Shower Ghost

Here's a little extra sound that's not in the tutorial, it's a looping sound for the showering ghost in the start of the level! Adding this doesn't require any extra coding, but there are some settings that we have to get right. First, start by adding an audio source to the ghost in the shower, and have it play `SFXShowerGhost`. Make sure to set it to play on awake, loop, and have spatial blend set to 3D. Now, for the different attenuation curves, have a look at how I have it set up:
For a little extra sparkle, you can add the `Audio Low Pass Filter` and `Audio Reverb Filter` to the ghost. Set the Reverb Preset on `Bathroom`. Then, change the curves for those effects to be like this:
Harm-Jan Wiechers
Audio designer / music producer - Artist