Welcome back to Day 16! Today we’re going to go fix some of the assets for the game.
Specifically, today I want to:
- Use a real gun asset
- Add the shooting effect back in
- Create code to trigger reloading
Do you know what that means?
We’re going to go back to the Unity asset store to find new assets! Let’s get started!
Setting Up a Real Gun Asset
I looked around for a gun asset and I found a great machine gun model to use!
I found this great looking Machine Guns asset.
After downloading and loading the asset into our Unity game, you’ll find the files in the FreeMachineGun folder in the main project tab.
The first thing we should do is replace our cube with our gun asset.
In the folder FreeMachineGun: FreeMachineGun > Prefabs we find MachineGun_00. Drag the prefab into our hierarchy and make it a child of our Main Camera game object, making it the child of our previous Gun game object.
I made some configuration to my transform to make it fit here’s what I have, however you might have to eyeball it yourself.
- Position: (0.25, -0.5, 0.75)
- Rotation: (0. 90, 0)
You should have something like this:
I like how straightforward this is!
Setting up the Gun Animation to Run When We Shoot
Right now, when we left click, our gun will start shooting and spilling out ammos.
You might be thinking: “Wow! This model is amazing it even has the shooting code done for me!”
Unfortunately, it isn’t that easy. What we’re seeing is the particle effect of the gun that we activate in our PlayerShootingController.
You might also notice that after we shoot, our particle effect never stops either, that’s because in our code, we never had to stop our particle effect before.
Before we go in and fix this, let’s first understand the states to our animation.
For us to interact with the animation of a model, we must go to the Animatortab to set animation states and transitions.
Select MachineGun_00 in the hierarchy pane and then go to the Animatortab.
If the Animator tab isn’t available for you can find it in Window > Animator
Here’s what we see.
We have 3 trigger parameters to work with:
- DoOpen
- DoReload
- Shoot
As for our state, we have 4 states:
- Default
- MachingGun_open
- MachineGun_shoot
- MachineGun_reload
Looking at our transitions…
In default:
- Shoot transition to the MachineGun_shoot
- DoOpen transitions to MachineGun_open
- DoReload transitions to MachineGun_reload
In MachineGun_shoot
- Shoot transition back to default
- DoOpen transitions to MachineGun_open
- DoReload transitions to MachineGun_reload
In MachineGun_open:
- Shoot transition to the MachineGun_shoot
- DoOpen transitions back to default
- DoReload transitions to MachineGun_reload
In MachineGun_reload:
- After the animation time is finish we transition to default
Now that we understand how the trigger logic works, we can work on the shooting script.
Now I don’t know what the DoOpen animation is for, however we don’t need it for the game in my mind. We just need to shoot and reload.
Creating the Code to Shoot
Now that we have the assets in place, we’re going to write the code to use it.
The first thing is that for some reason, I attached the PlayerShootingController to the camera. I’m going to move that to our new MachineGun_00 game object.
If you created a new PlayerShootingController, make sure the settings are:
- Range: 100
- Shooting Delay: 0.2
- Shot Sfx Clips: Machine_Gunfire_01
Before going to the code, I had to think about the general cases that I would encounter for our animation to work.
It’s interesting to think about it, because we have 3 states that our gun can be in: default, shooting, and reloading. Now imagine how much more complicated if we had to transition between even more!
When I first started coding, I just had the general case, press R to reload and then shoot.
However, there were problems where my code would put me in permanent shooting animation mode among many other things.
There were some that I had to encounter, here were the specific animation cases I had to consider:
- Hit reload while we’re not doing anything
- Hit reload while we’re shooting
- Shoot while we’re reloading
- Never let go of our mouse before we shoot, reload, and then hold on until after we’re done reloading
Without any further waiting, here’s our code:
using UnityEngine;
public class PlayerShootingController : MonoBehaviour
{
public float Range = 100;
public float ShootingDelay = 0.1f;
public AudioClip ShotSfxClips;
private Camera _camera;
private ParticleSystem _particle;
private LayerMask _shootableMask;
private float _timer;
private AudioSource _audioSource;
private Animator _animator;
private bool _isShooting;
private bool _isReloading;
void Start () {
_camera = Camera.main;
_particle = GetComponentInChildren<ParticleSystem>();
Cursor.lockState = CursorLockMode.Locked;
_shootableMask = LayerMask.GetMask("Shootable");
_timer = 0;
SetupSound();
_animator = GetComponent<Animator>();
_isShooting = false;
_isReloading = false;
}
void Update ()
{
_timer += Time.deltaTime;
if (Input.GetMouseButton(0) && _timer >= ShootingDelay && !_isReloading)
{
Shoot();
if (!_isShooting)
{
TriggerShootingAnimation();
}
}
else if (!Input.GetMouseButton(0))
{
StopShooting();
if (_isShooting)
{
TriggerShootingAnimation();
}
}
if (Input.GetKeyDown(KeyCode.R))
{
StartReloading();
}
}
private void StartReloading()
{
_animator.SetTrigger("DoReload");
StopShooting();
_isShooting = false;
_isReloading = true;
}
private void TriggerShootingAnimation()
{
_isShooting = !_isShooting;
_animator.SetTrigger("Shoot");
}
private void StopShooting()
{
_audioSource.Stop();
_particle.Stop();
}
private void Shoot()
{
_timer = 0;
Ray ray = _camera.ScreenPointToRay(Input.mousePosition);
RaycastHit hit = new RaycastHit();
_audioSource.Play();
_particle.Play();
if (Physics.Raycast(ray, out hit, Range, _shootableMask))
{
print("hit " + hit.collider.gameObject);
EnemyHealth health = hit.collider.GetComponent<EnemyHealth>();
EnemyMovement enemyMovement = hit.collider.GetComponent<EnemyMovement>();
if (enemyMovement != null)
{
enemyMovement.KnockBack();
}
if (health != null)
{
health.TakeDamage(1);
}
}
}
// called from the animation finished
public void ReloadFinish()
{
_isReloading = false;
}
private void SetupSound()
{
_audioSource = gameObject.AddComponent<AudioSource>();
_audioSource.volume = 0.2f;
_audioSource.clip = ShotSfxClips;
}
}
There’s been a lot of changes and addition to the code, but in general here’s the flow:
- We created more variables to keep track of what state we’re in. Currently we’re using Booleans, however I think using Enums to represent our state might be a better idea, especially if we have more states to keep track of.
- In Start() we initialize our new boolean states
- In Update(), while we’re not reloading and we hit Shoot, if we haven’t started shooting, we’ll start our shooting animation in TriggerShootingAnimation() and we’ll run our shooting logic in Shoot()
- Whenever we let go of our mouse, we’ll go to the else statement in Update() and stop our shooting animation and run our stop shooting logic in StopShooting() which stops our music and particle effect
- A side note: in Shoot(), I started the particle effect when we hit something with the raycast, we want it to always run whenever we shoot.
- In Update() when we want to reload, we press R and we’ll go into StartReloading(), here we would trigger our reload animation to start and disable our shooting so the players can’t continue shooting.
- We have a public function ReloadFinish() that tells us we’re down reloading. We’re going to have to go to the animation for our machine gun and add an event to run this function whenever we’re done reloading.
So now that we have our code in place, let’s add the event into our reload animation to run our ReloadFinish() code when the animation finishes.
To do that, select MachineGun_00 and select the Animation tab.
If you don’t have the Animation tab, you can open it by going to Window -> Animation.
Here’s what we will have at the end:
In the animation tab:
- Select MachineGun_reload as our animation
- Go to frame 68
- Click the add event button below our frame number
- Select ReloadFinish() as the function to run once we get to that frame
With all of this in place, we’ll be able to reload now when we hit R and then resume normal shooting controls after the animation ends.
Conclusion
There we go! Today we added a real gun asset to replace our fake cube gun. As you can see, it wasn’t that bad and a lot of the things we did, we already learned from the Zombie Shooter tutorial we did at the end.
With our gun in place, we’re done now, right? WRONG!
Back when I was doing my Survival shooter post, Scouting Ninja commented to me on how shooting really works and it turns out that the raycast we create isn’t actually coming directly from the screen.
It’s coming from the muzzle of our gun to the middle of the screen.
Tomorrow, I’m going to investigate how to shoot a raycast from our gun muzzle instead of directly from our screen!
Until then, I’ll see you all in Day 17!
Source: Day 16
Visit the 100 Days of Unity VR Development main page.
Visit our Homepage