Let's Try: Shooting with Raycasts (article)

Checked with version: 5.3

-

Difficulty: Beginner

Prefer video format?

If you prefer to take this tutorial in video form, please click here.

Questions? Please post in our dedicated forum thread for the Let's Try tutorial series here.

Introduction

description

In this tutorial we will learn how to shoot using raycasting. A ray is an invisible line from point A to point B in the game world. The important point is that this invisible line or ray that is cast into the scene can return information about GameObjects that have been hit by the ray. The GameObjects must have a collider attached in order to register a hit with the ray. When a ray intersects or ‘hits’ a GameObject, the event is referred to as a RaycastHit. This hit can tell us certain details about the GameObject and where it was hit, including a reference to the GameObject’s Transform, the length of the ray when it hits something, the point in the world where the hit happened, and more.

In a shooting game like an FPS we aim by looking around with the mouse or the right joystick on a gamepad. When we click the fire button we cast a ray which creates an invisible line from the camera - the player’s eye as far as the game is concerned - forward into the game world so that whatever they look directly at, they can shoot.

description

Downloading The Assets

If you have not yet downloaded and imported the asset package please click here to download it.

  • Download and import the assets.

Once you have downloaded and imported the assets into your project, in the folder Scenes you will see a scene called ShootingWithRaycasts. This is the version of the scene which is not completed that we will be working in. There is also a completed version of the scene in the Completed Scenes folder.

What We will Build

When we have completed this tutorial, it will match the completed scene. To understand what we intend to build, let’s open the completed scene first.

  • Open CompletedScenes > RaycastShootComplete.

description

In the completed scene we have a first person character controller called FPSController from Unity Standard Assets. We have added a weapon object as a child, named Gun. Ensure that in the Hierarchy you have expanded the FPSController parent GameObject, as well as the FirstPersonCharacter parent GameObject in order to see this. When we rotate the camera using the mouse, the weapon will follow the direction that the camera is looking.

description

We have also added a UI canvas with a target reticle image which is this red dot that we see here.

description

To see how this works, play the scene and test.

  • Play the scene and look around with the mouse.

  • Click the left mouse button to fire.

In play mode you can see in the Scene View that we have a green line which represents the direction that the player is aiming in. This is not the raycast. This is a representation of where the raycast will be that we have added to the scene using a visualizing and debugging tool. If we fire we can hit the boxes and apply physics forces to them. If we shoot them three times they will be deactivated.

If we look at the Scene View while firing we can see two lines. The green line represents the direction that the player is aiming in from the center of the camera, marked by the red reticle dot.

The pink line is the visible laser that we see in the game view which is being rendered from the end of the gun to the point that the player hit with the raycast.

description

  • Exit Play Mode.

Creating The Shooting Script

We will start by closing the completed scene and opening the scene ShootingWithRaycasts.

  • In the Scenes folder double click ShootingWithRaycasts to open it.

The first thing that we will do is create a new script which we will use for our gun. We will call this script RaycastShoot.

  • In the Project View select the Scripts folder and click the Create button.
  • Choose C# Script.
  • Name the new script RaycastShoot.

description

Next, we will attach the new script to our Gun GameObject.

  • Expand the child object FirstPersonCharacter which holds the camera and select the Gun GameObject.

  • Drag the RaycastShoot script from the scripts folder to the Gun in the Hierarchy.

description

Declaring Variables

Now let's get our script ready for editing.

  • Double click on the RaycastShoot script to open it for editing.

Next we will declare a series of public variables and initialize their values. Initializing a variable will set its initial default value.

  • Declare a public int called gunDamage and initialize its value to 1.

  • Declare a public float called fireRate and initialize its value to .25.

public int gunDamage = 1;
public float fireRate = .25f;

gunDamage will determine how much damage is applied to an object when it is shot. This will interact with a simple health script attached to our TargetBox objects called ShootableBox. ShootableBox will be explained later in the tutorial. fireRate will control how often the player can fire their weapon. If the player has just fired they will have to wait .25 seconds before they can fire again.

  • Declare a public float called weaponRange and initialize that to 50.

  • Declare a public float called hitForce and initialize it to 100.

public float weaponRange = 50f;
public float hitForce = 100f;

The variable weaponRange determines how far our ray will be cast into the scene. Initializing it to 50 means that our weapon will have a range of 50 units. If our raycast intersects an object with a rigidbody component attached we will apply force to it, the amount of which will be defined by our hitForce variable.

  • Declare a public Transform called gunEnd.
public Transform gunEnd;

gunEnd will be the Transform component of an empty GameObject which will mark the position at which our laser line will begin. This empty GameObject will be positioned just in front of the barrel or ‘end’ of the gun so that the laser appears to fire from there.

  • Declare a private Camera variable called fpsCam. fpsCam will hold a reference to our first person camera. We will use this to determine the position the player is aiming from.
private Camera fpsCam;
  • Declare a private WaitForSeconds variable called shotDuration and initialize it using a new WaitForSeconds object which will have a duration of .07 seconds.
private WaitForSeconds shotDuration = new WaitForSeconds(.07f);

We will use this variable shotDuration to determine how long we want the laser to remain visible in the game view once the player has fired. The function controlling the display of the laser in the game will be a Coroutine and needs a WaitForSeconds object to determine how long we want the laser to remain visible in the Game View for, once the player has fired. By declaring and caching the WaitForSeconds now, it will be stored in memory and re-used instead of creating a new WaitForSeconds object each time we fire. This will help optimize memory performance.

  • Declare a private AudioSource variable called gunAudio.
private AudioSource gunAudio;

This will hold a reference to an AudioSource that we already have attached to our gun, which we will use to play our shooting sound effect when the player fires.

  • Declare a private LineRenderer variable called laserLine.
private LineRenderer laserLine;

The LineRenderer takes an array of 2 or more points in 3D space and draws a straight line between them in the game view.

  • Declare a private float variable called nextFire.
private float nextFire;

nextFire will hold the time at which the player will be allowed to fire again after firing.

At this stage your script should look like this:

Code snippet

using UnityEngine;

using System.Collections;

public class RaycastShoot: MonoBehaviour {

    public int gunDamage = 1;                                           

    public float fireRate = .25f;                                       

    public float weaponRange = 50f;                                     

    public float hitForce = 100f;

    public Transform gunEnd;

    

    

    private Camera fpsCam;

    private WaitForSeconds shotDuration = new WaitForSeconds(.07f);

    private AudioSource gunAudio;

    private LineRenderer laserLine;

    private float nextFire; 

    

    void Start () 

{

    

    }

    void Update () 

{

    

    }

}

Getting Component References

In the Start function we will set up our component references to our laserLine (LineRenderer), gunAudio (AudioSource) and our fpsCam (Camera).

  • Get and store a reference to our LineRenderer component in our variable laser line.

  • Get and store a reference to our AudioSource in gunAudio.

void Start () 
    {
        laserLine = GetComponent<LineRenderer>();
        gunAudio = GetComponent<AudioSource>();
    }

This script is attached to our Gun GameObject, but we need to store a reference to the Camera component on the Gun’s parent GameObject FirstPersonCamera. To do this, we will use GetComponentInParent to find the component on the parent object, storing a reference in our Camera variable fpsCam.

  • Get and store a reference to the Camera in our fpsCam variable using GetComponentInParent
fpsCam = GetComponentInParent<Camera> ();

GetComponentInParent will search first the GameObject that it is attached to then each GameObject in its parent hierarchy moving recursively upwards one GameObject at a time. It will stop and store a reference to the first component of the specified type that it encounters.

Controlling Player Fire Rate

The core of our code will be written in the Update function. Here we will handle user input, check if the player can fire and ultimately apply physics force to any GameObject with a Rigidbody hit by the ray.

We need to check if the player has pressed the "Fire1" button and if enough time has passed since they last fired. Each time the player fires we need to track the current game time and then add to it the value stored in fireRate, our shot frequency public variable. We store this sum in our private variable called nextFire. This is why in our if statement we need to check if the value of the current game time has exceeded our nextFireTime, meaning the player can fire once more.

  • Add an if statement to check if the Fire1 button is currently held down and Time.time is greater than the value stored in our nextFire variable.

  • If that if statement evaluates to true then we will reset our nextFire time by setting nextFire to equal Time.time plus fireRate.

if (Input.GetButtonDown ("Fire1") && Time.time > nextFire) 

{

     nextFire = Time.time + fireRate;

}

Activating The Shooting Effects

Now it's time to turn on and off our laser effect. We will do this using a coroutine

  • Declare a new private function that returns IEnumerator called ShotEffect.

  • In ShotEffect, call the Play function of our gunAudio AudioSource. This will play the AudioClip assigned to gunAudio. This is a shooting sound effect that has already been assigned in the editor.

private IEnumerator ShotEffect()

{

    gunAudio.Play ();

}
  • On the next line within ShotEffect, enable the LineRenderer laserLine.
laserLine.enabled = true;

We want our coroutine to wait - in this case for .07 seconds - before it turns our laser line back off.

  • Tell our coroutine to yield and return our WaitForSeconds variable shotDuration which will cause our coroutine to wait for .07 seconds.

  • Once it's done waiting, disable the laserLine.

yield return shotDuration;

laserLine.enabled = false;

At this point, your code for ShotEffect should look like this, please check your code before continuing to read.

Code snippet

private IEnumerator ShotEffect()
{
    gunAudio.Play ();
    laserLine.enabled = true;

    yield return shotDuration;

    laserLine.enabled = false;
}

We want to start this coroutine whenever the player fires, which we will detect in the Update function.

  • Back in Update, within the braces for our if statement and below the line in which we set nextFireTime we will call StartCoroutine and specify ShotEffect.
StartCoroutine(ShotEffect());

Raycasting From The Camera

Next, we need an origin point for our ray. In order for the player to be able to aim accurately we want this point to always be at the center of our camera. We will achieve this using ViewportToWorldPoint. In this case we are passing in a new Vector3 which is set to .5 on the X axis, .5 on the Y axis, and 0 on the Z axis. This function takes a position relative to the camera's viewport, and converts it to a 3 dimensional point in World space. By giving this function a Vector3 value, we can use the X and Y values of the Vector3 to determine where on the viewport rectangle our ray should start. Passing in 0.5 on the X and Y axes specifies a point directly in the center of the view, where we expect the player to aim from.

  • In Update, declare a new Vector3 variable called rayOrigin.
  • Set it’s position to the center of the screen using ViewPortToWorldPoint.
Vector3 rayOrigin = fpsCam.ViewportToWorldPoint (new Vector3 (.5f, .5f, 0));

At this point your script should look like this:

Code snippet

using UnityEngine;

using System.Collections;

public class RaycastShoot: MonoBehaviour {

    public int gunDamage = 1;                                           

    public float fireRate = .25f;                                       

    public float weaponRange = 50f;                                     

    public float hitForce = 100f;

    public Transform gunEnd;

    

    

    private Camera fpsCam;

    private WaitForSeconds shotDuration = new WaitForSeconds(.07f);

    private AudioSource gunAudio;

    private LineRenderer laserLine;

    private float nextFire; 

    

    void Start () 

{

        laserLine = GetComponent<LineRenderer> ();

        gunAudio = GetComponent<AudioSource> ();

        fpsCam = GetComponentInParent<Camera> ();

    }

    void Update () 

{

        if (Input.GetButtonDown("Fire1") && Time.time > nextFire) 
        {
            nextFire = Time.time + fireRate;

                StartCoroutine (ShotEffect());

                Vector3 rayOrigin = fpsCam.ViewportToWorldPoint (new Vector3(0.5f, 0.5f, 0.0f));
        }

    }

}

To understand this better, let's return to Unity and take a look at the camera's view frustum.

  • Return to the Unity Editor.

  • In the Hierarchy expand the FPSController GameObject and select the FirstPersonCharacter GameObject which has our Camera component attached.

  • Temporarily de-activate the Directional Light by selecting it in the Hierarchy and un-checking its active checkbox at the top of the Inspector. The Directional Light is a child of the Lighting GameObject, which is a child of Environment.

description

This will simply allow us to see the Camera frustum more easily in this light styled environment, we will re-activate it shortly, but for now, de-activate it, then re-select the FirstPersonCharacter GameObject.

  • Place your mouse over the scene view and press F to frame selected.

  • Use the alt or option key to pan the view around and see the camera's view frustum.

description

The camera's view frustum is a shape which represents the viewable area of the camera. This shape is a four sided cone or pyramid with the tip cut off. The near end of it is the camera’s near clip plane. The larger rectangle farther away is the far clip plane. Only GameObjects within the camera's frustum are rendered. This frustum sets the boundaries for the camera's view. ViewportToWorldPoint uses viewport space. This space is normalized and relative to the camera.

description

The bottom left of the viewport is defined as 0,0. The top right is defined as 1,1. By specifying a value of 0.5 along the X axis and 0.5 along the Y axis we are getting a point at the very center of our camera’s viewport. Passing in 0 for Z axis means that it will be 0 units away from the camera's facing direction.

With that done, let's continue writing our script.

  • Turn the main Directional Light back on and return to the script editor.

Storing Hit Information

We now need to declare a variable to hold the information returned from our ray if it hits a GameObject with a collider attached. This variable will need to be a RaycastHit type of variable, and we will name it hit.

  • In Update we will declare a RayCastHit called hit. We will use it to store information about whatever we hit with our Raycast.
RaycastHit hit;

Setting The Line Start Point

Now we need to determine start and end positions for our laserLine when the player fires. The first thing we need to do is to establish the initial position of the line. In order to draw a line with the LineRenderer we need to specify at least two points for it to draw between.

  • Set the first point or position 0 to gunEnd.position.
laserLine.SetPosition(0, gunEnd.position);

Remember, gunEnd is the empty GameObject that we will attach to the end of our gun in the scene. This empty GameObject will give us the current position in world space for the end of our Gun. We can safely set this here, as every time the player fires we want to start the LineRenderer at the end of the gun.

Casting The Ray

Continuing within our Update function, on the next line we will use Physics.Raycast to cast our ray.

  • Add an if statement.

  • In the if statement parenthesis, call Physics.Raycast.

if (Physics.Raycast(rayOrigin,fpsCam.transform.forward, out hit, weaponRange))

We will use the results of this raycast for a number of things. The first thing we will use it for is to determine where the end point of our laser line will be. Later we will also use the results of this raycast to apply physics forces to the GameObject that it intersected. We will also use the results to deal damage to it if it has a damage script attached.

description

Physics.Raycast returns a boolean, meaning that if it hits something it will evaluate to true. By placing it in our if statement this means that the code that follows will only be executed if our raycast hit something.

The first parameter that we will pass in when casting our ray is rayOrigin. This will be the point in our world where our ray will begin, in this case the center of our camera’s viewport, which we have already stored in our variable rayOrigin.

The direction which we will cast our ray in will be the forward direction of our camera fpsCam, so we pass in fpsCam.transform.forward for the second parameter of the raycast.

We will store additional information about the object that we hit including its rigidbody, collider and surface normal in our RaycastHit variable hit. Using the out keyword allows us to store additional information from a function, in addition to its return type.

Finally we will pass in weaponRange which is the distance over which we want to cast our ray, in this case 50 units.

Two Possible End Points

There are two possible positions for the end of our laser. The first is the position of whatever we hit with our raycast, but it's possible that if we fire into the sky our raycast will hit nothing and we need to account for that behavior as well. In the case where we have hit something with our raycast we will set the second position of our line to hit.point.

  • Within the brackets of the if statement for our raycast, call laserLine.SetPosition and pass in hit.point
if (Physics.Raycast (rayOrigin, fpsCam.transform.forward, out hit, weaponRange))
     {
          laserLine.SetPosition (1, hit.point);
     }

In the case where our raycast has not intersected with anything and therefore our first if statement returns false, we will use an else to set the position of the end of our line 50 units away from rayOrigin, in the forward direction of our camera.

  • Add an else statement.

  • Set the end position of the line to 50 units forward from the camera, by multiplying the forward direction of fpsCam by the weaponRange variable.

else

{
laserLine.SetPosition(1, fpsCam.transform.forward * weaponRange);
}
  • Save the script and return to Unity.

At this point your script should look like this:

Code snippet

using UnityEngine;
using System.Collections;

public class RaycastShoot : MonoBehaviour {

    public int gunDamage = 1;
    public float fireRate = 0.25f;
    public float weaponRange = 50f;
    public float hitForce = 100f;   
    public Transform gunEnd;

    private Camera fpsCam;                                              
    private WaitForSeconds shotDuration = new WaitForSeconds(0.07f);
    private AudioSource gunAudio;
    private LineRenderer laserLine;
    private float nextFire;


    void Start () 
    {
        laserLine = GetComponent<LineRenderer>();

        gunAudio = GetComponent<AudioSource>();

        fpsCam = GetComponentInParent<Camera>();
    }
    

    void Update () 
    {
        if (Input.GetButtonDown("Fire1") && Time.time > nextFire) 
        {
            nextFire = Time.time + fireRate;

            StartCoroutine (ShotEffect());

            Vector3 rayOrigin = fpsCam.ViewportToWorldPoint (new Vector3(0.5f, 0.5f, 0.0f));

            RaycastHit hit;

            laserLine.SetPosition (0, gunEnd.position);

            if (Physics.Raycast (rayOrigin, fpsCam.transform.forward, out hit, weaponRange))
            {
                laserLine.SetPosition (1, hit.point);
            }
            else
            {
                laserLine.SetPosition (1, rayOrigin + (fpsCam.transform.forward * weaponRange));
            }
        }
    }


    private IEnumerator ShotEffect()
    {
        gunAudio.Play ();

        laserLine.enabled = true;

        yield return shotDuration;

        laserLine.enabled = false;
    }
}

Rendering The Visible Laser Effect

Now let's set up our LineRenderer which will make our shots visible in the Game View.

  • Select the Gun GameObject and choose GameObject > Create Empty Child.

  • In the inspector rename this new child object to GunEnd.

  • Apply a label to GunEnd so that we can see it easily in the scene view.

description

Now let's position the GunEnd object in front of the barrel of our gun.

  • Select the GunEnd GameObject

  • In its Transform component set the position on the X-axis to 0.36.

  • Set the position on the Y-axis to -0.18.

  • Set the position of the Z-axis to 1.

description

This will position it directly in front of our gun barrel. To make our shots visible in the Game View we will use a LineRenderer component. The LineRenderer will draw a line from the position of gunEnd to the position that the player is aiming using our invisible raycast.

  • Highlight the Gun GameObject and click Add Component > Effects > LineRenderer.

description

Assigning Variables In The Editor & Setting Line Width

Now, we need to assign the gunEnd variable of our RaycastShoot script.

  • Drag the GunEnd GameObject to the gunEnd field of RaycastShoot.

description

Next, let's set the parameters for our line render to make the line a little bit narrower.

  • Expand the Parameters section of the LineRenderer component, and set the 'Start Width' and 'End Width' properties to 0.05.

description

It's worth noting that we have not assigned a material to the LineRenderer and this is why it will render pink. If we want to change the color of the rendered line, simply drag a material to the element 0 slot in the Materials array. The last thing we need to do before we are done tweaking our LineRenderer component is to disable it.

  • Un-check the Enabled checkbox of the 'LineRenderer’ component to make sure that the component is disabled when the game begins.

The RaycastShoot script will enable the LineRenderer when the player fires the weapon, so it should be disabled by default.

Debugging Raycasts

Raycasts are invisible in both the Scene View and the Game View. When working with raycasts it can be useful to visualize them in the Scene View. We are going to write a simple helper script which will allow us to visualize our raycasts in the scene view. It's worth noting that this script is a development tool and should be removed before creating the final build of your game.

  • Highlight the Scripts folder
  • Create a new C# script called RayViewer
  • Drag RayViewer onto the Gun GameObject in the Hierarchy.
  • Select the Gun and double click RayViewer to open it for editing.

RayViewer will duplicate some of the functionality of RaycastShoot, because its purpose is similar. Instead of casting a ray, we will simply be drawing a line to show where our ray is going in the Scene View.

  • Declare a public float variable called weaponRange and initialize it to 50.
  • Declare a private Camera variable called fpsCam.
public float weaponRange = 50f;

private Camera fpsCam;
  • In Start use GetComponentInParent to store a reference to our Camera in fpsCam.
void Start () 
     {
          fpsCam = GetComponentInParent<Camera>();
     }

In Update we will calculate the origin point of our line, then we will draw it using Debug.DrawRay . To calculate the origin point of the line that we will draw using Debug.DrawRay we will use much of the same code that we used to calculate the origin of our ray.

  • Declare a new Vector3 called lineOrigin. Set lineOrigin to the center of our viewport using ViewportToWorldPoint.
 Vector3 lineOrigin = fpsCam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, 0.0f));
  • Call Debug.DrawRay and pass in the start point which is rayOrigin, the direction which we want it to go in, which in this case is fpsCam.transform.forward, or the forward direction away from our camera.

  • For the second parameter multiply fpsCam.transform.Forward by weaponRange which in this case is set to 50. This will set the end point of the line 50 units away from the camera.

  • For the third parameter pass in Color.Green to set the color of the line which will be drawn in the scene view.

Debug.DrawRay(lineOrigin, fpsCam.transform.forward * weaponRange, Color.green);

It's worth noting that because Debug.DrawRay is a debugging tool it will only be visible in the Scene View.

  • Save the script and return to Unity to test.

  • Play the scene.

With our scene playing we can see that a green line is drawn from center of the camera, away from it in the Scene View. This helps us to visualize where our player will be able to aim.

description

Now, we can draw the visual effect for shooting but we can't yet affect the object in the game.

The Target Box

Before we return to our script let's take a look at the object that we want to interact with.

  • Expand the Environment GameObject in the Hierarchy window.

  • Select the Target Box GameObject.

description

In the inspector we can see that the TargetBox object has a script attached to it called ShootableBox. ShootableBox has a public variable called currentHealth which is set to 3.

  • Double click the script in the script field of ShootableBox to open it.

description

The ShootableBox Script

ShootableBox is a simple health script. In it we have a public integer called currentHealth, which is initialized to 3.

We have a single public function which returns void called Damage. Damage takes an integer as its only parameter called damageAmount.

When the Damage function is called we will subtract damageAmount from currentHealth.

Next we check if currentHealth is less than or equal to 0.

If it is we will call gameObject.SetActive(false) to deactivate the GameObject that the script is attached to.

In game terms - this simply means we can shoot the box 3 times before it is de-activated, which to the player will make it disappear from the game.

Code snippet

using UnityEngine;
using System.Collections;

public class ShootableBox : MonoBehaviour {

    //The box's current health point total

    public int currentHealth = 3;

    public void Damage(int damageAmount)

    {

        //subtract damage amount when Damage function is called

        currentHealth -= damageAmount;

        //Check if health has fallen below zero

        if (currentHealth <= 0) 

        {

            //if health has fallen below zero, deactivate it 

            gameObject.SetActive (false);

        }

    }

}

Applying Damage & Physics Forces To The ShootableBox

Let's return to our RaycastShoot script. In the if statement for our raycast we are going to check if there is a ShootableBox component attached to the GameObject that our raycast hit. We find this by checking the collider which was hit by the ray, and then using GetComponent - here our collider is a shortcut to refer to the GameObject, so it is the same as using gameObject.GetComponent. If it is we will call that object’s Damage function and pass in our variable gunDamage.

  • Declare a variable of the type ShootableBox called health.

  • Set this to equal hit.collider.GetComponent().

ShootableBox health = hit.collider.GetComponent<ShootableBox>();

What this means is that if there is a ShootableBox component attached to the collider we hit, it will be stored in our variable health.

  • Check if health is not equal to null, meaning there was a ShootableBox component attached to the thing we hit.

  • If this statement evaluates to true we will call the Damage function of health and pass in gunDamage as a parameter.

if (health != null)
{
    health.Damage (gunDamage);
}

gunDamage was the first public integer we declared in this script. It is currently equal to 1.

We only want to apply damage to dynamic objects that we know can be repelled by our laser in game terms, these objects all have something in common - a Rigidbody physics component. As such we will check if one such component is present on the GameObject hit by the ray before we try and apply forces to it.

  • Check if there is a Rigidbody component attached to the GameObject we hit with our raycast.

  • If there is we will add force to it to move it around. We will do this using the function AddForce.

  • Add force to the Rigidbody of the object we hit and pass in negative hit.normal multiplied by hitForce.

if (hit.rigidbody != null)

{

hit.rigidbody.AddForce (-hit.normal * hitForce);

}

hit.normal is the outward direction of the surface we hit. By using negative hit.normal we can add force to the TargetBox in a direction that is the inverse of this, repelling it back from it’s surface. Multiplying it by hitForce allows us to control how much force should be added when we shoot an object.

description

  • Save the script and return to Unity.

At this point your script should look like this:

Code snippet

using UnityEngine;
using System.Collections;

public class RaycastShootComplete : MonoBehaviour {

    public int gunDamage = 1;
    public float fireRate = 0.25f;
    public float weaponRange = 50f;
    public float hitForce = 100f;   
    public Transform gunEnd;

    private Camera fpsCam;                                              
    private WaitForSeconds shotDuration = new WaitForSeconds(0.07f);
    private AudioSource gunAudio;
    private LineRenderer laserLine;
    private float nextFire;


    void Start () 
    {
        laserLine = GetComponent<LineRenderer>();

        gunAudio = GetComponent<AudioSource>();

        fpsCam = GetComponentInParent<Camera>();
    }
    

    void Update () 
    {
        if (Input.GetButtonDown("Fire1") && Time.time > nextFire) 
        {
            nextFire = Time.time + fireRate;

            StartCoroutine (ShotEffect());

            Vector3 rayOrigin = fpsCam.ViewportToWorldPoint (new Vector3(0.5f, 0.5f, 0.0f));

            RaycastHit hit;

            laserLine.SetPosition (0, gunEnd.position);

            if (Physics.Raycast (rayOrigin, fpsCam.transform.forward, out hit, weaponRange))
            {
                laserLine.SetPosition (1, hit.point);
                ShootableBox health = hit.collider.GetComponent<ShootableBox>();

                if (health != null)
                {
                    health.Damage (gunDamage);
                }

                if (hit.rigidbody != null)
                {
                    hit.rigidbody.AddForce (-hit.normal * hitForce);
                }
            }
            else
            {
                laserLine.SetPosition (1, rayOrigin + (fpsCam.transform.forward * weaponRange));
            }
        }
    }


    private IEnumerator ShotEffect()
    {
        gunAudio.Play ();

        laserLine.enabled = true;

        yield return shotDuration;

        laserLine.enabled = false;
    }
}

Testing Our Shooting Mechanic

As we can see our Target Box GameObjects already have a Rigidbody component attached. They should be ready to shoot.

description

  • Play the scene, then try to fire at the boxes.

Our shooting is working, but as you may have noticed it's still a little bit difficult to aim.

The Target Reticle UI

Let’s add the final component which will be our red reticle. The reticle UI is very simple. It's a small red UI Image in the center of the screen.

  • Choose GameObject > UI > Image.

description

  • Switch the scene view into 2D Mode.

description

  • With the mouse cursor over the Scene View, Push the F key to frame selected on our image.

  • Zoom out using the Mouse wheel so you can see that we have a white image in the lower left hand corner of our screen. You can also see this in the Game View.

description

  • Change the image’s color. With the image selected click on the Color field in the image component.

description

  • Drag the color swatch all the way to the upper right corner to make the image red.

description

  • Anchor the image to the center of the screen by opening the Anchor Presets, holding down the alt or option key and clicking on the center preset.

description

  • In the Rect Transform click on the Width property and set it to 5.

  • Click on the Height property and set that to 5 as well.

description

  • Play the scene and test again.

As we can see we have our reticle anchored in the center of our screen and we can move it around, and aim and fire much more easily.

Conclusion

That's the basics for shooting with Raycasts. If you want more game mechanic focused tutorials please check out our other Let's Try tutorial assignments.

Mini Tutorials

  1. Let's Try: Shooting with Raycasts (Video)
  2. Let's Try: Shooting with Raycasts (article)