Day 14: Finish Attacking the Enemy and Walking Sounds In Unity

Josh Unity VR Development Leave a Comment

We’re back in Day 14. I finally solved the pesky problem from Day 13 where the Knight refuses to get pushed back when we shoot at him.

Afterwards, I decided to get some sound effects to make the game a little livelier.

Without delay, let’s get started!

Adding Player Hit Effects Part 2

As you might recall, we last ended up trying to push back the Knight when we shoot them by changing the Knight’s velocity, however the Knight continues to run forward.

The problem

After a long investigation, it turns out that Brute running animation that I used naturally moves your character’s position forward.

The solution

After finally searching for unity animation prevents movement I found the answer on StackOverflow.

In the animator, disable Apply Root Motion and then we must apply the movement logic ourselves (which we already are).

Writing the Knock Back code

Once we have our Root Motion disabled. We’re relying on our code to move our knight.

The first thing we need to do is update our PlayerShootingController script to call the knock back code:

using UnityEngine;

public class PlayerShootingController : MonoBehaviour
{
    public float Range = 100;
    public float ShootingDelay = 0.1f;

    private Camera _camera;
    private ParticleSystem _particle;
    private LayerMask _shootableMask;
    private float _timer;

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

        if (Input.GetMouseButton(0) && _timer >= ShootingDelay)
        {
            Shoot();
        }
    }

    private void Shoot()
    {
        _timer = 0;
        Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit = new RaycastHit();

        if (Physics.Raycast(ray, out hit, Range, _shootableMask))
        {
            print("hit " + hit.collider.gameObject);
            _particle.Play();

            EnemyHealth health = hit.collider.GetComponent<EnemyHealth>();
            EnemyMovement enemyMovement = hit.collider.GetComponent<EnemyMovement>();
            if (enemyMovement != null)
            {
                enemyMovement.KnockBack();
            }
            if (health != null)
            {
                health.TakeDamage(1);
            }
        }
    }
}

The biggest change is that we get our EnemyMovement script and then call KnockBack() which we haven’t implemented yet.

Once we have this code in, we need to implement KnockBack() inside our EnemyMovement script. Here’s what it looks like:

using UnityEngine;
using UnityEngine.AI;

public class EnemyMovement : MonoBehaviour
{
    public float KnockBackForce = 1.1f;

    private NavMeshAgent _nav;
    private Transform _player;
    private EnemyHealth _enemyHealth;

    void Start ()
    {
        _nav = GetComponent<NavMeshAgent>();
        _player = GameObject.FindGameObjectWithTag("Player").transform;
        _enemyHealth = GetComponent<EnemyHealth>();
    }
    
    void Update ()
    {
        if (_enemyHealth.Health > 0)
        { 
            _nav.SetDestination(_player.position);
        }
        else
        {
            _nav.enabled = false;
        }
    }

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

I know this was a one liner for KnockBack(), but there was a lot of work involved to get to this point.

Here’s how the code works:

  1. When our shooting code hits the enemy, we call KnockBack() which sets the velocity to be the direction behind the knight, making the illusion of being pushed back.
  2. This is only temporary as our Nav Mesh Agent will come back and move our Knight towards the player in the next Update()
  3. Here’s how KnockBackForce effects the velocity
    1. At 1, the knight stays in place when you shoot
    2. <1, the knight gets slowed down
    3. >1, the knight gets pushed back

Adding Sound Effects

Now that we finally solved the knockback problem, we moved on to the next thing.

At this point, playing the game seems dull. Do you know what could make things a little bit more interesting? Sound effects!

I went back to the Unity Asset Store to find sound effect assets specifically:

  1. Player shooting sound
  2. Player walking sound
  3. Player hit sound
  4. Enemy hit sound
  5. Enemy running sound
  6. Enemy attack sound

Randomly searching on Unity, I found the Actions SFX Vocal Kit which contains everything we need. Fantastic!

Once we have finished downloading and importing the SFX into our Unity project, we’ll start using them.

Adding Enemy Hit Sound Effects

Adding the script

The first thing we’re going to do is that we need to add our Male_Hurt audio clips to our Knight.

Normally, we need to add an Audio Source component for our Knight. However, before that, let’s step back and think: what sounds do our knight need to play?

  1. Hit sound
  2. Walking sound
  3. Attack sound

If we were to add an Audio Source component to the Knight Object and use that to play the sound, what will happen is that one sound will immediately be replaced by the other one. We don’t want that.

What we could do is create multiple AudioSources components and then manually attach them to our script, however that’s not very scalable if we ever decided that we needed more types of sounds.

Instead I found this great way to add multiple audio sources on a single GameObject.

The idea is that instead of manually creating multiple components and then attaching them to a script component, why not create the component in code?

Here’s what I did:

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 AudioSource _walkingAudioSource;
    private float _time;

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

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

    
}

There’s a lot of code that was added in, but I tried to separate them as much as I can to easy to understand pieces.

Here’s the flow:

  1. In Start(), we instantiate our new private fields, specifically our new variables:
    1. _walkingAudioSource: our AudioSource for our steps
    2. _time: to track how long the enemy steps take
  2. We call SetupSound() from Start() and create a new instance of an AudioSource that will only appear when the game starts and we set the volume to 0.2f
  3. In Update(), we add logic to play the stepping sound whenever the it has been 0.2 seconds and that if we’re still in the running animation.
    1. Note: GetCurrentAnimatorStateInfo(0) the 0 refers to index 0 layer, which I’m not really sure why, but that’s what people use. From there we can check which state the knight is in.
  4. In PlayRandomFootstep(), we randomly choose the walking sound clips that we downloaded and play them

Once we have all of this we need to add the audio clips in.

Go to EnemyMovement script attached to the Knight and then under Walking Clips change the size to 4. We can do this, because Walking Clips is an array of clips.

Then add in Footstep01-04 into each spot. Make sure that Walking Delay is set to 0.4 if it’s not already.

Run the game and you’ll see that the enemy makes running sounds now!

If you’re using a different animation, you might have to change the Walking Delay to match the animation, but on the high level, that’s what you must do!

Whenever the knight attacks us, the sound will stop and whenever the knight resumes running after us (with the help of some shooting knockback) the running sound will resume!

Conclusion

Today in Day 14, we found the problem with the knight knockback had something to do with the root animation we used.

After disabling it we can start adding our knockback code without any problems.

With the knock back implemented, the next thing that we added was sound effects. We found some assets in the Unity store and then we added them to our enemy, where for the first time, we created a component via code.

My concern at this point is what happens when we start spawning a lot of knights? Will that create an unpleasant experience?

Either way, come back tomorrow for Day 15, where I decided I’m going to add the enemy hit sound and the player shooting sound.

Day 13 | 100 Days of VR | Day 15

Home

Subscribe To Our Weekly Newsletter!
Like these coding articles? Join my mailing list for the latest updates and influence the code that I write!
We hate spam. Your email address will not be sold or shared with anyone else.

Leave a Reply

Your email address will not be published. Required fields are marked *