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:
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.
This is only temporary as our Nav Mesh Agent will come back and move our Knight towards the player in the next Update()
Here’s how KnockBackForce effects the velocity
At 1, the knight stays in place when you shoot
<1, the knight gets slowed down
>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:
Player shooting sound
Player walking sound
Player hit sound
Enemy hit sound
Enemy running sound
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?
Hit sound
Walking sound
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:
In Start(), we instantiate our new private fields, specifically our new variables:
_walkingAudioSource: our AudioSource for our steps
_time: to track how long the enemy steps take
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
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.
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.
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.
Original Day 14
Visit the 100 Days of Unity VR Development main page
Visit the Homepage
↧