Welcome back to day 17 of our Unity development. Previously, we switched out our cube gun with a real gun model.
Today, we’re going to look at how to make a gun shoot in Unity.
Originally, our code sends a raycast directly from our camera, however I’ve learned from Scouting Ninja that when we shoot a projectile, it comes from the gun muzzle. We did something similar in the Survival Shooter back in the Unity tutorial.
Technically, since we still don’t have any projectiles coming out, shooting straight from the middle is fine, but let’s say we want to shoot something out, what would we do?
Today, I’m going to make adjustments to fire a raycast from the muzzle of our gun and then go towards where we’re facing.
To do that, I found this great Unity video tutorial to follow: Let’s Try: Shooting with Raycasts.
A lot of the things that were talked about were mentioned in the Survival Shooter tutorial and I’ve also worked on a lot of similar parts my own… wow why didn’t I just follow this tutorial….
Anyways, I’m going to cover the things I learned and added. Let’s get to it!
Debug Raycast
Have you ever wondered what your raycast would look like if you visualized it? Great! Neither have I! Glad I’m not alone on this.
The first addition I want to make is a raycast debugger that visualizes your raycast in the scene tab when you play the game.
Like so:
See the green line on the right?
To attach this, all we need to do is add a line of code:
Debug.DrawRay()
Adding that into PlayerShootingController
inside Update()
, we’ll have something like this:
void Update ()
{
_timer += Time.deltaTime;
Vector3 lineOrigin = _camera.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
Debug.DrawRay(lineOrigin, _camera.transform.forward * Range, Color.green);
}
The important part is that we’re using our camera to create a raycast with Debug.DrawRay()
.
We pass in very similar parameters as you might expect with a raycast except we don’t detect if we hit anything, instead we just create a green line.
Shooting From Our Gun
If we want to shoot from the end of our gun, we need a location to start shooting from. We can’t use the gun itself, because we can’t exactly pinpoint the muzzle of the gun for something to be shot out.
We need to create something that indicates we shot something.
We’ll be using the example provided by the tutorial to make a Line Renderer to create a line from our gun muzzle to the target.
The first thing we need to do is some setup work.
Adding Our Game Objects
The first thing we need to do is create the end of our gun:
I created a cube called gunEnd
as a child MachingGun_00
to represent the end of the gun.
I used a cube so that it’s easier showing you the end of the gun, but I’ll be removing the Mesh Filter and Mesh Renderer so that it’ll just be an empty object.
The position/scale might differ for you than the ones I set above, but just mess around with it in Unity until you get something satisfactory.
In MachingGun_00
, we’re going to create a Line Renderer component to represent a laser that we will shoot from our gun to our mouse click.
Make sure that the Line Renderer has a width of 0.1
.
With all of this in place, we’re ready to use these components for our script.
Writing the Code to Fire Bullets from our Gun
We’re going to make some new additions to our PlayerShootingController
to use our new pieces and shoot a laser from our gun endpoint.
Here’s the code:
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 AudioSource _audioSource;
private Animator _animator;
private bool _isShooting;
private bool _isReloading;
private LineRenderer _lineRenderer;
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;
_lineRenderer = GetComponent<LineRenderer>();
}
void Update ()
{
_timer += Time.deltaTime;
Vector3 lineOrigin = _camera.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
Debug.DrawRay(lineOrigin, _camera.transform.forward * Range, Color.green);
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();
_lineRenderer.SetPosition(0, GunEndPoint.position);
StartCoroutine(FireLine());
if (Physics.Raycast(ray, out hit, Range, _shootableMask))
{
print("hit " + hit.collider.gameObject);
_lineRenderer.SetPosition(1, hit.point);
EnemyHealth health = hit.collider.GetComponent<EnemyHealth>();
EnemyMovement enemyMovement = hit.collider.GetComponent<EnemyMovement>();
if (enemyMovement != null)
{
enemyMovement.KnockBack();
}
if (health != null)
{
health.TakeDamage(1);
}
}
else
{
_lineRenderer.SetPosition(1, ray.GetPoint(Range));
}
}
private IEnumerator FireLine()
{
_lineRenderer.enabled = true;
yield return ShootingDelay - 0.05f;
_lineRenderer.enabled = false;
}
public void ReloadFinish()
{
_isReloading = false;
}
private void SetupSound()
{
_audioSource = gameObject.AddComponent<AudioSource>();
_audioSource.volume = 0.2f;
_audioSource.clip = ShotSfxClips;
}
}
Here’s the flow for our code:
- We have two new variables
GunEndPoint
and _lineRenderer
that we instantiate in Start()
. - The way
LineRenderer
works is that you set 2 points to form the line you want to make. The 1st point is at the tip of our gun. The second point depends on our raycast. - If our raycast hits something, we want to set whatever we hit as our 2nd point in our line.
- If our raycast doesn’t hit anything, we just make the player’s shot go towards the center of our screen by the length of our bullet.
- Finally, we create a Coroutine that calls
FireLine()
that enables our LineRenderer
component so we can see it, wait about 0.05 seconds and then disable it. Doing this gives us the illusion that we’re shooting something.
After we add in our code, make sure that we drag our gunEnd
game object into the appropriate slot in our PlayerShootingController
script and now we can shoot purple lasers!
Conclusion
So that’s it for Day 17.
Today we learned how to use raycast debug so that we can see where our raycast will end up and we used a line renderer to shoot lasers from the end of our gun to where the player is standing at.
It’s very exciting to say that I’ve come a long way since I first started doing the Survival Shooter tutorial, especially since I was just copying code.
Now I’m fluent enough in Unity that I can write some code myself!
With all that being said, it’s getting late so I’m going to sleep. I’ll see you all on day 18 where I’ll be setting up the UI for the game!
Day 16 | 100 Days of VR | Day 18
Home
CodeProject
The post Day 17: Shooting With A Gun In Unity appeared first on Coding Chronicles.