Click here to Skip to main content
15,885,546 members
Articles / Game Development / Unity
Technical Blog

Day 43 of 100 Days of VR: Setting up Google Spatial Audio in Unity

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
30 Nov 2017CPOL4 min read 4.7K   2   1
How to set up Google Spatial Audio in Unity

Today, we’re going to have a relatively short day! Today will perhaps be our final day working on this game before I move on to creating what I hope to be a complete game!

However, before I get ahead of myself, let’s finish today first!

Today, there are two things that I want to do:

  1. Fix our score and health UI on our game which broke
  2. Use Google’s Spatial Audio system! Let’s get to it!

Step 1: Re-Fixing the UI System

One important thing I completely forgot about when we duplicated and moved our gun from the gaze controller to our Daydream controller is that we forgot to change a couple of places to use our new UI element.

Let’s put our components back in the correct places.

There are 2 places that use our UI components:

  1. PlayerHealth
  2. ScoreManager

Let’s add these back in.

  1. In GameManager, find the GameManager Drag and drop our new Score UI Text from Player > Main Camera > GvrControllerPointer > MachingGun_00 (1) > MachineGun_01 > Score into our Score slot.
  2. In Player, we have our PlayerHealth script. Drag and drop our new Health Bar Slider from Player > Main Camera > GvrControllerPointer > MachingGun_00 (1) > MachineGun_01 > Health Bar into our HealthBar slot.

With these added in, when we play our game again, we can see the time on our gun and our health.

Plus, now that we can individually move our weapon, it’s a much more pleasant experience with our UI. We no longer have our time and health on our page anymore. Now its location is based on our controller. Nice!

Step 2: Adding Spatial Audio to the Game

Looking at the documentation page, there are a couple of elements that are needed to make this work. We only need to do 2 things:

  1. Replace the AudioListener component in MainCamera with GvrAudioListener
  2. Replace all AudioSource with GvrAudioSource

There’s also another prefab and script called GvrAudioRoom that allows us to reflect sound as if we were in a room with varying levels of surface materials to bounce off sounds. We won’t be working with this today, but it’s there.

Let’s set everything up!

First, let’s add our GvrAudioListener script:

  1. In the Hierarchy go to Player > Main Camera
  2. Click Add Component and select
  3. Note: We still need the old AudioListener

Now the more complex part. We’re going to have to go in and replace all the AudioSources with GvrAudioSources.

Looking at the source code, it doesn’t look like GvrAudioSources inherits directly from Audio Source, rather, it’s more like a wrapper that has the same functions to call, and uses an AudioSource component underneath along with its extra changes.

Luckily for us, we only use them in a couple of places and only in code. Specifically, in these scripts:

  • PlayerShootingController
  • EnemyMovement
  • EnemyAttack
  • EnemyHealth

I’ll be replacing all the AudioSource:

PlayerShootingController.cs

C#
using UnityEngine;
using System.Collections;

public class PlayerShootingController : MonoBehaviour
{
    public float Range = 100;
    public float ShootingDelay = 0.1f;
    public AudioClip ShotSfxClips;
    public Transform GunEndPoint;

    private Camera _camera;
    private ParticleSystem _particle;
    private LayerMask _shootableMask;
    private float _timer;
    private GvrAudioSource _audioSource;
    private Animator _animator;
    private bool _isShooting;

    void Start () {
		_camera = Camera.main;
	    _particle = GetComponentInChildren<ParticleSystem>();
	    Cursor.lockState = CursorLockMode.Locked;
	    _shootableMask = LayerMask.GetMask("Shootable");
	    _timer = 0;
        SetupSound();
        _animator = GetComponent<Animator>();
        _isShooting = false;
    }
	
	void Update ()
	{
	    _timer += Time.deltaTime;

        if (Input.GetButton("Fire1") && _timer >= ShootingDelay)
	    {
            Shoot();
	        if (!_isShooting)
	        {
	            TriggerShootingAnimation();
	        }
	    }
        else if (!Input.GetButton("Fire1"))
	    {
            StopShooting();
	        if (_isShooting)
	        {
	            TriggerShootingAnimation();
            }
	    }
	}

    private void TriggerShootingAnimation()
    {
        _isShooting = !_isShooting;
        _animator.SetTrigger("Shoot");
    }

    private void StopShooting()
    {
        _audioSource.Stop();
        _particle.Stop();
    }

    public void Shoot()
    {
        _timer = 0;
        Ray ray = _camera.ViewportPointToRay(new Vector3(0.5f, 0.5f, 0f));
        RaycastHit hit = new RaycastHit();
        _audioSource.Play();
        _particle.Play();

        if (Physics.Raycast(ray, out hit, Range, _shootableMask))
        {
            print("hit " + hit.collider.gameObject);
            EnemyMovement enemyMovement = hit.collider.GetComponent<EnemyMovement>();
            if (enemyMovement != null)
            {
                enemyMovement.KnockBack();
            }
        }
    }

    private void SetupSound()
    {
        _audioSource = gameObject.AddComponent<GvrAudioSource>();
        _audioSource.volume = 0.2f;
        _audioSource.clip = ShotSfxClips;
    }

    public void GameOver()
    {
        _animator.SetTrigger("GameOver");
        StopShooting();
        print("game over called");
    }
}

EnemyAttack.cs

C#
using UnityEngine;

public class EnemyAttack : MonoBehaviour
{
    public FistCollider LeftFist;
    public FistCollider RightFist;
    public AudioClip[] AttackSfxClips;

    private Animator _animator;
    private GameObject _player;
    private GvrAudioSource _audioSource;

    void Awake()
    {
        _player = GameObject.FindGameObjectWithTag("Player");
        _animator = GetComponent<Animator>();
        SetupSound();
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject == _player)
        {
            _animator.SetBool("IsNearPlayer", true);
        }
        print("enter trigger with _player");
    }

    void OnTriggerExit(Collider other)
    {
        if (other.gameObject == _player)
        {
            _animator.SetBool("IsNearPlayer", false);
        }
        print("exit trigger with _player");
    }

    private void Attack()
    {
        print("attack called");
        if (LeftFist.IsCollidingWithPlayer() || RightFist.IsCollidingWithPlayer())
        {
            print("enemy attacked the player");
            PlayRandomHit();
            _player.GetComponent<PlayerHealth>().TakeDamage(10);
        }
    }

    private void SetupSound()
    {
        _audioSource = gameObject.AddComponent<GvrAudioSource>();
        _audioSource.volume = 0.2f;
    }

    private void PlayRandomHit()
    {
        int index = Random.Range(0, AttackSfxClips.Length);
        _audioSource.clip = AttackSfxClips[index];
        _audioSource.Play();
    }
}

EnemyHealth.cs

C#
using System;
using UnityEngine;
using Random = UnityEngine.Random;

public class EnemyHealth : MonoBehaviour
{
    public float Health = 100;
    public AudioClip[] HitSfxClips;
    public float HitSoundDelay = 0.1f;

    private SpawnManager _spawnManager;
    private Animator _animator;
    private GvrAudioSource _audioSource;
    private float _hitTime;
    private Boolean _isEnter;

    void Start()
    {
        _spawnManager = GameObject.FindGameObjectWithTag("SpawnManager").GetComponent<SpawnManager>();
        _animator = GetComponent<Animator>();
        _hitTime = 0f;
        _isEnter = false;
        SetupSound();
    }

    void Update()
    {
        _hitTime += Time.deltaTime;
        if (Input.GetButton("Fire1") && _isEnter)
        {
            TakeDamage(1);
        }
    }
    
    private void TakeDamage(float damage)
    {
        if (Health <= 0) { return; } if (_hitTime > HitSoundDelay)
        {
            Health -= damage;
            PlayRandomHit();
            _hitTime = 0;
        }

        if (Health <= 0)
        {
            Death();
        } 
    }

    private void SetupSound()
    {
        _audioSource = gameObject.AddComponent<GvrAudioSource>();
        _audioSource.volume = 0.2f;
    }

    private void PlayRandomHit()
    {
        int index = Random.Range(0, HitSfxClips.Length);
        _audioSource.clip = HitSfxClips[index];
        _audioSource.Play();
    }

    private void Death()
    {
        _animator.SetTrigger("Death");
        _spawnManager.EnemyDefeated();
        foreach (Collider collider in GetComponentsInChildren<Collider>())
        {
            collider.enabled = false;
        }
    }

    public void HealthEnter()
    {
        _isEnter = true;
        print("enter");
    }

    public void HealthExit()
    {
        _isEnter = false;
        print("exit");
    }
}

EnemyMovement.cs

C#
using UnityEngine;
using UnityEngine.AI;

public class EnemyMovement : MonoBehaviour
{
    public float KnockBackForce = 1.1f;
    public AudioClip[] WalkingClips;
    public float WalkingDelay = 0.4f;

    private NavMeshAgent _nav;
    private Transform _player;
    private EnemyHealth _enemyHealth;
    private GvrAudioSource _walkingAudioSource;
    private Animator _animator;
    private float _time;

    void Start ()
    {
        _nav = GetComponent<NavMeshAgent>();
        _player = GameObject.FindGameObjectWithTag("Player").transform;
        _enemyHealth = GetComponent<EnemyHealth>();
        SetupSound();
        _time = 0f;
        _animator = GetComponent<Animator>();
    }
    
    void Update ()
    {
        _time += Time.deltaTime;
        if (_enemyHealth.Health > 0 && (_animator.GetCurrentAnimatorStateInfo(0).IsName("Run") || 
                   _animator.GetCurrentAnimatorStateInfo(0).IsName("Attack1")))
        { 
            _nav.SetDestination(_player.position);
            if (_time > WalkingDelay)
            {
                PlayRandomFootstep();
                _time = 0f;
            }
        }
        else
        {
            _nav.enabled = false;
        }
    }

    private void SetupSound()
    {
        _walkingAudioSource = gameObject.AddComponent<GvrAudioSource>();
        _walkingAudioSource.volume = 0.2f;
    }

    private void PlayRandomFootstep()
    {
        int index = Random.Range(0, WalkingClips.Length);
        _walkingAudioSource.clip = WalkingClips[index];
        _walkingAudioSource.Play();
    }

    public void KnockBack()
    {
        _nav.velocity = -transform.forward * KnockBackForce;
    }

    // plays our enemy's default victory state 
    public void PlayVictory()
    {
        print("victory");
        _animator.SetTrigger("Idle");
        _nav.enabled = false;
    }
}

Now let’s play our game and see what our spatial setting sounds like…

…except when we try to play the game, we don’t have any audio at all!

After playing around and looking up why GvrAudioSource is not playing, I found the reason. We need to enable a spatial audio plug in.

To do that, we have to:

  1. Go to Edit > Project Settings > Audio to open our AudioManager
  2. In the Spatializer plug in, change from None to Gvr Audio Spatializer

Image 1

With this setup, let’s go back in and try playing again.

It’s amazing how adding spatial sound changes the game. Before, I didn’t know where the enemies were, except that they’re somewhere in the game, however with Spatial Sound enabled, I can figure out where enemies are without having to do a complete 360 look around the game.

Conclusion

Wow! It’s been a long journey! 43 days in and we have gone from a complete beginner in Unity to someone who can create a simple VR First Person Shooter gameplay.

At this point, I’m going to call this project complete.

I’m not saying that this “game” is done, I’m far from having a functional game. Heck, even the gameplay part of the game that we’ve been working on isn’t as complete as I would like it to be!

However, this simple prototype has provided enough value for me (and I hope you the readers too) and spending more time trying to get every detail perfect would not be a good use of time. It’s time to work on something new!

The question now is: what’s next?

I think it’s time to make a new game, one that I hope will be able to ship! Find out more next time!

Day 42 | 100 Days of VR | Day 44

Home

The post Day 43 of 100 Days of VR: Setting up Google Spatial Audio in Unity appeared first on Coding Chronicles.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
Joshua is a passionate software developer working in the Seattle area. He also has experience with developing web and mobile applications, having spent years working with them.

Joshua now finds his spare coding time spent deep in the trenches of VR, working with the newest hardware and technologies. He posts about what he learns on his personal site, where he talks mostly about Unity Development, though he also talks about other programming topic that he finds interesting.

When not working with technology, Joshua also enjoys learning about real estate investment, doing physical activities like running, tennis, and kendo, and having a blast with his buddies playing video games.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Franc Morales30-Nov-17 10:54
Franc Morales30-Nov-17 10:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.