‘The Jump’ was an awesome opportunity for a lot of cool immersive elements including user and audience audio, bike engine vibration simulation, and wind simulation. This came with a few challenges to overcome in order to make this a memorable experience.
I wanted to address how I solved some of the user’s and the audience’s audio. This was one of my first experiences with Unity audio that required more than just stereo and there wasn’t anything like Wwise’s Channel Router. And goodness, that would’ve been so helpful had Wwise had that at the time. It most certainly came in handy on later projects.
I needed to have audio that the people watching the user could hear and separate audio for the user. This was to "advertise" the experience to others without giving away the unique experience they would have as the user. The challenge came with the fact that Unity could output 5.1 surround yet we needed completely different audio coming out of stereo channels for the headphones, stereo for the external speakers and a mono for the bike’s Buttkicker.
The solution became exporting multiple 5.1 audio files with 2D spatial blend set on their sources.
This solved a lot of problems, except how was the audio spatialized within the headset? Luckily there was the panning parameter on audio sources. And with this I was able to achieve a simple spatialization within the headphones.
public class PanAudio : MonoBehaviour {
[SerializeField] private UpdateEyeAnchors eye;
[SerializeField] private Transform rider;
[SerializeField] private Vector3 rayDirection;
[SerializeField] private float angle;
private float calcPanAngleLeft;
private float calcPanAngleRight;
private float pangle;
private float rightPan;
private float leftPan;
private AudioSource sound;
void Update()
{
rider = eye.eyes[0].transform;
//get audio source that this is attached to
sound = GetComponent<AudioSource>();
//forward pointing ray cast
Vector3 fwd = rider.TransformDirection(Vector3.forward) * 30;
Debug.DrawRay(rider.position, fwd, Color.cyan);
//left pointing raycast for checking if source is generally on left side
Vector3 lefty = rider.TransformDirection(Vector3.left) * 30;
Debug.DrawRay(rider.position, lefty, Color.yellow);
//right pointing raycast for checking if source is generally on right side
Vector3 righty = rider.transform.TransformDirection(Vector3.right) * 30;
Debug.DrawRay(rider.position, righty, Color.magenta);
//raycast between player to this audio source
rayDirection = transform.position - rider.position;
Debug.DrawRay(rider.position, rayDirection, Color.green);
//get the angle between audio source and the direction the user is looking
angle = Vector3.Angle(rayDirection, rider.forward);
//calculate left panning angle
calcPanAngleLeft = Vector3.Angle(rayDirection, lefty);
if (calcPanAngleLeft >= 0 && calcPanAngleLeft < 90)
{
leftPan = -(90 - calcPanAngleLeft) / 90f;
}
if (calcPanAngleLeft >= 90 && calcPanAngleLeft < 180)
{
leftPan = (calcPanAngleLeft - 90) / 90f;
}
//calculate right panning angle
calcPanAngleRight = 180 - Vector3.Angle(rayDirection, righty);
if (calcPanAngleRight >= 0 && calcPanAngleRight < 90)
{
rightPan = -(90 - calcPanAngleRight) / 90f;
}
if (calcPanAngleRight >= 90 && calcPanAngleRight < 180)
{
rightPan = (calcPanAngleRight - 90) / 90f;
}
if (sound.CompareTag("Right"))
{
//clamp it so that you never have hard pans in an ear
sound.panStereo = Mathf.Clamp(rightPan, -0.75f, 0.75f);
}
if (sound.CompareTag("Left"))
{
//clamp it so that you never have hard pans in an ear
sound.panStereo = Mathf.Clamp(leftPan, -0.75f, 0.75f);
}
}
}
In retrospect, I can see different ways that I might do the angle calculation as far as finding ways to remove the if statements like by using different syntax or having a single algorithm for calculating everything rather than splitting between left and right.



