Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Day 14 of 100 Days of VR: Survival Shooter - Finish Attacking the Enemy and Walking Sounds in Unity

DZone's Guide to

Day 14 of 100 Days of VR: Survival Shooter - Finish Attacking the Enemy and Walking Sounds in Unity

In this Unity 3D tutorial, learn how to complete the enemy attack code and add walking sounds to your virtual reality game app.

· Mobile Zone ·
Free Resource

We’re back on 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 Knockback 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 knockback 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_Hurtaudio 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 it has been 0.2 seconds and if we’re still in the running animation.
    1. Note: In 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 Knightand 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 on 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 knockback 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.

Topics:
unity ,game development ,unity 3d ,vr ,mobile ,tutorial

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}