Home / Legacy / Lesson 4: Shooting with Unity 3d raycasting

Lesson 4: Shooting with Unity 3d raycasting

/
/
/
3493 Views
This entry is part 6 of 15 in the series Unity 3d Fundamentals Course

In this lesson we are going to learn how to shoot with our first person weapon.

Lesson Outline

Detect the Player Input

First we need to create a script to deal with the weapon behaviour. So just create a Weapon.cs script and apply it to the AK-47 GameObject or the one you are using for the weapon.

When dealing with Input Events we have several methods to choose from:

These methods should be used to detect action type events only. To detect input for movement behaviours we would need to use the Input.GetAxis.

  • Keyboard
    • GetKeyDown(string name);
    • GetKey(string name);
    • GetKeyUp(string name);
  • Mouse Buttons
    • GetMouseButtonDown(int button);
    • GetMouseButton(int button);
    • GetMouseButtonUp(int button);
  • Virtual Buttons – can be mapped to several physical controls
    • GetButtonDown(string buttonName);
    • GetButton(string buttonName);
    • GetButtonUp(string buttonName);

The first two groups are more limited because are exclusively dedicated either to detect the Keyboard or the Mouse Buttons.

The Virtual Buttons are more flexible and more complete. We are able to configure the type of controls we want and also add alternative controls for the same action. The player is also able to configure the controls on a configuration screen at the beginning of the game (in standalone builds).

0.0

All these Input methods return a boolean value when each event is detected:

  • ButtonDown – return true during the frame the user presses down the button (only once);
  • Button – return true while the user is pressing down the button (in loop);
  • ButtonUp – return true during the frame the user releases the button (only once).

To detect the player input we need the Update method. So add it to your script if it’s missing.

We want to detect the exact moment the Player presses down the button, so we will use the Input.GetButtonDown method.

To configure the Virtual Buttons we need to access the Input Manager.

1_1

On the Input Manager Window we can see the Buttons that are already defined by Unity and also add a few more if needed.

In this case we will be using the “Fire1” Button. We can see that it uses as the main control – Positive Button – the left Ctrl and as an alternative control – Alt Positive Button – the Left Mouse Button (mouse 0).

1_2

 

Detect the Bullet Collisions

Now that we know when the player wants to shoot, we need to actually do it. In fact we won’t be doing it per se. I mean we will be firing our weapon, it will make a sound and a few special effects but no actual bullets will be shot. Instead we will focus on detecting where the bullet would hit and dealing with the collision.

Since the player is not able to see the bullets while firing there is no point on recreating them. A few special effects will simulate a bullet being shot and the collision will simulate the end result, that’s all it’s needed.

To detect the bullet collision we will use Raycasting.

What is Raycasting?

Raycasting is a method to cast invisible rays onto our scene and gather information about the objects being hit (if any).

Why should we use Raycasting?

It is a lot more efficient to create rays when we just want to test collisions and don’t need an actual object being shot.

But how does the Raycast works?

Each time the player presses the Fire1 button a ray will be created from the middle of the screen onto the scene. If the ray hits anything with a collider it will return the information about that object.

Check the following example:

And back to the bullet collision…

To create a Raycast we use the Physics.Raycast method.

bool Raycast(Ray ray, RaycastHit hitInfo, float distance = Mathf.Infinity);

  • bool – returns true when it hits something;
  • ray – create a ray emitted from the center of the screen onto the scene;
  • hitInfo – variable that holds information about the object that was hit;
  • distance – maximum length of the ray.

So first we need two private variables to hold the ray and the hitInfo information.

A new ray will be created each time the users presses the fire button. So the code must go inside the if-statement created earlier. Since we are casting the ray from the camera there is a method on the Camera class that casts a ray from a point – position – in our screen onto the scene – Camera.ScreenPointToRay.

Ray ScreenPointToRay(Vector3 position);

  • Ray – returns a new ray;
  • position – screen position from where the ray is being cast;

This method is commonly used to cast a ray onto the scene from the mouse position which in that case the parameter would be equal to Input.mousePosition.

Now that we have our Ray we can implement the Raycast method. Since it returns a boolean value we need an if-statement run the code only when the method returns true, meaning that a collision was detected. We also need to pass the variable hit to store the object being hit by the raycast and define a distance for our Ray. Our Ray will be as long as the camera frustum so we will use the size of the camera far clipping plane.

Now our weapon is able to shoot stuff however we still cannot see anything happening. So let’s add some bullet holes to the objects being hit.

Create the Bullet Holes

For the bullet hole effect we need to create a new plane with the hole texture where the object is hit by the raycast. First of all we need the texture to use for the bullet hole.

bullethole

To create objects at runtime we use the Instantiate method of the GameObject class.

static Object Instantiate(Object original, Vector3 positionQuaternion rotation);

  • original – this method requires an original object to duplicate, usually a prefab;
  • position – the position to instantiate the new object;
  • rotation – the rotation of the new object.

To create the original GameObject we use a prefab with the bullet hole texture. Start by creating a Quad, apply the texture and create a prefab with that object.

2_12_22_32_4

Remove the Mesh Collider from the bullet hole GameObject.

Our weapon script will need to know about this prefab, so we need a public reference to the GameObject.

The position of the instantiated object will be the exact point where the raycast intersected the model. We can get this position by using the hit object of the Raycast method.

The rotation of the prefab is a little bit more complicated because it needs to adjust itself to the object being hit.

2_5

In this example we can see three bullet holes on our mixing barrel. Each one of them has a completely different rotation because it needed to adjust itself to the orientation of the point – the polygon – it hit. We will use the point Normal for this.

What is a Normal?

A Normal is an object such as a line or vector that is perpendicular to a given object.

normal

 

Luckily there is a property that is already defined for the point of contact called the hit.normal. Now that we know how to access the point normal we need to adjust the rotation of our prefab to the rotation of the normal.

2_6

In this example the red arrow is representing the surface normal. We need the quad to always match the rotation of this arrow. We see that the quad forward axis – the Z axis, the blue one – is pointing exactly in the opposite direction of the red arrow so we will use this to match the rotations.

To match the rotation of a given object to another object rotation we will use the Quaternion.FromToRotation.

static Quaternion FromToRotation(Vector3 fromDirection, Vector3 toDirection);

  • fromDirection – the rotation to change;
  • toDirection – the rotation to match;

In this case the fromDirection is the opposite of the quads forward axis and the toDirection is the surface normal.

Now that we have all three parameters we can instantiate the bullet hole prefab.

You may want to add the bullet hole as a child of the hit. After instantiating the object add this hole.transform.parent = hit.transform;

Assign the Bullet Hole Prefab to the script applied to your Weapon GameObject and let’s test this.

2_7

It’s clear that we need to adjust the size of the bullet hole so just change the scale of your prefab and test again.

2_8

2_9

There are two problems that we also need to solve. The first one is that the raycast is intersecting our First Person Capsule and creating bullets that look like they are hanging in the air. To solve this we need to tell the raycast to ignore this object. We can do this by changing the object layer to Ignore Raycast – More on Layers later on the series.

2_10

Don’t forget to do the same to the Weapon Trigger object.

The second problem is that some objects are using generic colliders, such as the Box Collider that don’t reflect their shape. So let’s change these colliders to use the Mesh Collider component and remove the other one.

2_11

2_12

 

Run another test.

2_13

We still have one problem to solve. The bullet holes are being instantiated exactly on surface of the hit point. This is creating some depth problems because the engine doesn’t know which one to show on top creating this flickering pattern – this is also called z-fighting.

To solve this we need to instantiate the bullet hole not exactly on the surface but with a little offset. Change the bullet hole position to this.

Run another test.

2_14

Maybe scale down the bullet hole prefab because on edges the hole is appearing outside of the model.

Run another test.

2_15

 

Full Code of Weapon.cs

 

Names to keep

What’s next…

On the next lesson we will add some special effects to our weapon so that it looks like it’s actually firing.

Series Navigation<< Lesson 3: Prefabs and Unity 3d PhysicsLesson 5: Particle Systems and Audio >>
  • Facebook
  • Twitter
  • Google+
  • Linkedin
  • Pinterest
  • Reddit

7 Comments

  1. thanks very much
    You did excellent tutorial.
    It gave me a lot of satisfaction.
    Beautiful I was demoralizing because he could not conclude anything good by itself.
    thanks again

  2. great tutorials i love addcomponent.com
    pls cover Magazine و Number of bullets in magazine and inventory

    in “FULL CODE OF WEAPON.CS”
    change
    public class test : MonoBehaviour
    to
    public class Weapon : MonoBehaviour

Leave a Comment

Your email address will not be published. Required fields are marked *