Home
FAQ
Contact
About us
Blog
Home FAQ Contact About us Blog Pixel
First person shooting in Unity using raycast - 23.08.2019
blog post cover

Have you ever wanted to build your own first-person shooter game?
You got the character and movement setup in Unity but need some kind of shooting functionality?
This can easily be accomplished using raycasts.

Step 1:
- Create a new script called AttackController.cs
- Add the script to your Unity character. (TIP: if you don't have a script for movement, 
                                                                        download Unity's standard assets plugin at the store
                                                                        and drag the "RigidBodyFPSController.prefab" into the scene.

Step 2:
- Create another script called Weapon.cs
- Add a game object to the scene which is going to represent a weapon.
- Add Weapon.cs as a component to the game object.
- Add a 3D cube inside the weapon's game object and scale it to the size of the gun barrel.
- Move the 3D cube to the point where the bullet is supposed to be fired from.
- Remove the Mesh- and Mesh filter component from the cube.
- Rename the cube to "GunBarrel"

Step 3:
- Open Weapon.cs
- Add 5 public variables at the top of the class:
public class Weapon : MonoBehaviour
{
public float damage;
public float fireRate; // This variable determines how fast the weapon can shoot
public float fireLength; // This variable determines how far the bullet fly when fired
public float reloadTime; // This variable determines how fast the weapon reload
public Transform gunBarrel; // A reference to the weapon's gun barrel game object
}
- Click on the weapon's game object inside Unity and set up the properties of your weapon.
- Fx damage=10, fireRate=0.5 and fireLength=10
- Drag the gun barrel into the 'gunBarrel' field.

Step 4:
- Open AttackController.cs
- Add the following variables:
public class AttackController : MonoBehaviour
{
public Weapon currentWeapon; // The current weapon our player is using

private bool shoot; // Used to check if the player wants to shoot
private bool isShooting; // Used to check if the player should wait before shooting another bullet
private bool isReloading; // Used to check if the player is reloading
}
- Add the following inside an 'Update()' function and add a new function called 'Shoot()':
public class AttackController : MonoBehaviour
{
// ...

void Update () {

// If the player click the fire button (mouse left click) we set 'fire=true'
// and if the player release the fire button we set 'shoot=false'
// and last if shoot is true we run our 'Shoot()' function
// By doing so can we make the player shoot as long the fire button haven't been released.
if (Input.GetButtonDown("Fire1")) {
shoot = true;
}

if (Input.GetButtonUp("Fire1")) {
shoot = false;
}

if (shoot) {
Shoot();
}
}

void Shoot() {
}
}
- Move down to 'Shoot()' and add the following:
public class AttackController : MonoBehaviour
{
// ...

void Shoot() {
// Return until the fire cooldown is gone and if we don't have a weapon
if (isShooting || currentWeapon == null) {
return;
}

// If a weapon doesn't have a fire rate we assume it's a non-automatic weapon
// and we set 'shoot=false' to implement a 'tap-to-shoot' functionality
if (currentWeapon.fireRate == 0) {
shoot = false;
}

// We set 'isShooting=true' and run FireCooldown as an IEnumerator to use the WaitForSeconds class
isShooting = true;
StartCoroutine(FireCooldown());
}

IEnumerator FireCooldown() {
// The weapon will be ready to fire another bullet when 'isShooting=false'
yield return new WaitForSeconds(currentWeapon.fireRate);
isShooting = false;
}
}
- Move back to the top of the class and add a private RaycastHit called hit:
public class AttackController : MonoBehaviour
{
public Weapon currentWeapon; // The current weapon our player is using

private bool shoot; // Used to check if the player wants to shoot
private bool isShooting; // Used to check if the player should wait before shooting another bullet
private bool isReloading; // Used to check if the player is reloading
private RaycastHit hit; // This variable will store information about raycast hits. Check out the docs here.

// ...
}
- Move back to 'Shoot()' and add the following:
public class AttackController : MonoBehaviour
{
// ...
void Shoot() {
// Return until the fire cooldown is gone and if we don't have a weapon
if (isShooting || currentWeapon == null) {
return;
}

// If a weapon doesn't have a fire rate we assume it's a non-automatic weapon
// and we set 'shoot=false' to implement a 'tap-to-shoot' functionality
if (currentWeapon.fireRate == 0) {
shoot = false;
}

// We set up a Ray which moves towards the center of the main camera
Ray ray = Camera.main.ViewportPointToRay(new Vector2(.5f, .5f));
// We set the origin of the ray to gun barrel position so it doesn't start at the camera position
ray.origin = currentWeapon.gunBarrel.position;
// We fire the raycast in an 'if' statement and pass the 'ray', our RaycastHit variable
// and we limit the distance of the raycast to match the current weapon's fire length
if (Physics.Raycast(ray, out hit, currentWeapon.fireLength))
{
// if the raycast it's a game object we check if it has the player tag
if (hit.collider.CompareTag("Player")) {
// and finally if it is a player, we want to add some damage to him/her
// I won't explain how to build a health controller class in this tutorial,
// but you would be able to access one like this if the player had one:
// var healthCtrl = hit.collider.GetComponent<HealthController>();
// healthCtrl.ApplyDamage(currentWeapon.damage);
}
}

// We set 'isShooting=true' and run FireCooldown as an IEnumerator to use the WaitForSeconds class
isShooting = true;
StartCoroutine(FireCooldown());
}

// ...
}
- That's it! You now have a very simple "weapon attack system" which is able to shoot and detect if it hits another player.

Step 5:
- We also want to add a reload functionality to our weapons, so that we have to reload some times between shooting.
- To simplify this in the tutorial, we will create a system where all weapons use the same type of bullets.
- Add the following above our private variables and add a 'Start()' function above 'Update()' where you set 'currentExtraBullets=maxExtraBullets':
public class AttackController : MonoBehaviour
{
public Weapon currentWeapon; // The current weapon our player is using

public int currentExtraBullets; // The number of bullets the player has available for reloading
public int maxExtraBullets = 100; // The number of extra bullets the player CAN have

private bool shoot; // Used to check if the player wants to shoot
private bool isShooting; // Used to check if the player should wait before shooting another bullet
private bool isReloading; // Used to check if the player is reloading
private RaycastHit hit; // This variable will store information about raycast hits. Check out the docs here.

void Start() {
currentExtraBullets = maxExtraBullets;
}

// ...
}
- Open Weapon.cs and add the following:
public class Weapon : MonoBehaviour
{
public float damage;
public float fireRate; // This variable determines how fast the weapon can shoot
public float fireLength; // This variable determines how far the bullet fly when fired
public float reloadTime; // This variable determines how fast the weapon reload
public Transform gunBarrel; // A reference to the weapon's gun barrel game object

public int currentBullets; // The number of bullets this weapon has
public int maxBullets = 30; // The number of bullets this weapon CAN have

void Start() {
// We set the weapon to start with a full magazin
currentBullets = maxBullets;
}
}
- Open AttackController.cs and move down to 'Shoot()' and add the following in the if statement at the top of the function:
public class AttackController : MonoBehaviour
{
// ...
void Shoot() {
// Return until the fire cooldown is gone and if we don't have a weapon
// OR
// if the player is reloading the weapon or the weapon's magazin is empty
if (isShooting || currentWeapon == null ||
isReloading || currentWeapon != null && currentWeapon.currentBullets <= 0) {
return;
}
// remove a bullet from the weapon's magazin
currentWeapon.currentBullets -= 1;

// ...
}

// ...
}
- At this point do we use 1 bullet every time we shoot, so we only need to implement a new function to start reloading
- Move to the 'Update()' function and add these new lines below our 'if(shoot)' statement:
public class AttackController : MonoBehaviour
{
// ...

void Update () {

// ...
// if we click the 'R' button on the keyboard we run our reload function
if (Input.GetKeyDown(KeyCode.R)) {
Reload();
}
}
// ...

}
- Move down below 'Shoot()' and 'FireCooldown()' and add two new functions called 'Reload()' and 'ReloadCooldown()'
- And add the following lines:
public class AttackController : MonoBehaviour
{
// ...
void Reload() {
// return the function if the player already is reloading
// OR the player don't have a weapon
// OR the player have a weapon that already is full
// OR the player don't have any extra bullets to insert into the weapon's magazin
if (isReloading || currentWeapon == null ||
currentWeapon != null && currentWeapon.currentBullets >= currentWeapon.maxBullets ||
currentExtraBullets <= 0) {
return;
}

// The first thing we need to know is how many bullets we can insert into the player's weapon
var diff = currentWeapon.maxBullets - currentWeapon.currentBullets;

// Then we check if the number of bullets missing is less or equal to the number our player has
// we know if this is true, the player must have more or the exact number of extra bullets as
// the current weapon is missing and we can therefore just set the number of bullets on the weapon
// to the max number of bullets the weapon's magazin can store
if (diff <= currentExtraBullets) {
currentExtraBullets -= diff;
currentWeapon.currentBullets = currentWeapon.maxBullets;
} else {
// if the player doesn't have more or the exact number of bullets it's either 0 or greater
// and we can safely add the current number of extra bullets to our weapon and set
// the player's number of extra bullets to 0
currentWeapon.currentBullets += currentExtraBullets;
currentExtraBullets = 0;
}

// And then we do the exact same thing here as in the Shoot() function
// We could just have added a function to handle this, but it might be easier
// to implement other functionality to the reload or shoot function if you keep the seperated
isReloading = true;
StartCoroutine(ReloadCooldown());
}

IEnumerator ReloadCooldown() {
yield return new WaitForSeconds(currentWeapon.reloadTime);
isReloading = false;
}
}
- And that's everything for the reloading functionality.
- The last thing we need is to move the weapon's game object inside the character's at a position where you want him to hold it
and drag the reference of the weapon into the AttackController component on the player's game object.

Find the finished scripts below:
Weapon.cs:
public class Weapon : MonoBehaviour
{
public float damage;
public float fireRate; // This variable determines how fast the weapon can shoot
public float fireLength; // This variable determines how far the bullet fly when fired
public float reloadTime; // This variable determines how fast the weapon reload
public Transform gunBarrel; // A reference to the weapon's gun barrel game object

public int currentBullets; // The number of bullets this weapon has
public int maxBullets = 30; // The number of bullets this weapon CAN have

void Start() {
// We set the weapon to start with a full magazin
currentBullets = maxBullets;
}
}

- AttackController.cs:
public class AttackController : MonoBehaviour
{
public Weapon currentWeapon; // The current weapon our player is using

public int currentExtraBullets; // The number of bullets the player has available for reloading
public int maxExtraBullets = 100; // The number of extra bullets the player CAN have

private bool shoot; // Used to check if the player wants to shoot
private bool isShooting; // Used to check if the player should wait before shooting another bullet
private bool isReloading; // Used to check if the player is reloading
private RaycastHit hit; // This variable will store information about raycast hits. Check out the docs here.

void Start() {
currentExtraBullets = maxExtraBullets;
}

void Update () {

// If the player click the fire button (mouse left click) we set 'fire=true'
// and if the player release the fire button we set 'shoot=false'
// and last if shoot is true we run our 'Shoot()' function
// By doing so can we make the player shoot as long the fire button haven't been released.
if (Input.GetButtonDown("Fire1")) {
shoot = true;
}

if (Input.GetButtonUp("Fire1")) {
shoot = false;
}

if (shoot) {
Shoot();
}

// if we click the 'R' button on the keyboard we run our reload function
if (Input.GetKeyDown(KeyCode.R)) {
Reload();
}
}

void Shoot() {
// Return until the fire cooldown is gone and if we don't have a weapon
// OR
// if the player is reloading the weapon or the weapon's magazin is empty
if (isShooting || currentWeapon == null ||
isReloading || currentWeapon != null && currentWeapon.currentBullets <= 0) {
return;
}
// remove a bullet from the weapon's magazin
currentWeapon.currentBullets -= 1;

// If a weapon doesn't have a fire rate we assume it's a non-automatic weapon
// and we set 'shoot=false' to implement a 'tap-to-shoot' functionality
if (currentWeapon.fireRate == 0) {
shoot = false;
}

// We set up a Ray which moves towards the center of the main camera
Ray ray = Camera.main.ViewportPointToRay(new Vector2(.5f, .5f));
// We set the origin of the ray to gun barrel position so it doesn't start at the camera position
ray.origin = currentWeapon.gunBarrel.position;
// We fire the raycast in an 'if' statement and pass the 'ray', our RaycastHit variable
// and we limit the distance of the raycast to match the current weapon's fire length
if (Physics.Raycast(ray, out hit, currentWeapon.fireLength))
{
// if the raycast it's a game object we check if it has the player tag
if (hit.collider.CompareTag("Player")) {
// and finally if it is a player, we want to add some damage to him/her
// I won't explain how to build a health controller class in this tutorial,
// but you would be able to access one like this if the player had one:
// var healthCtrl = hit.collider.GetComponent<HealthController>();
// healthCtrl.ApplyDamage(currentWeapon.damage);
}
}

// We set 'isShooting=true' and run FireCooldown as an IEnumerator to use the WaitForSeconds class
isShooting = true;
StartCoroutine(FireCooldown());
}

IEnumerator FireCooldown() {
// The weapon will be ready to fire another bullet when 'isShooting=false'
yield return new WaitForSeconds(currentWeapon.fireRate);
isShooting = false;
}

void Reload() {
// return the function if the player already is reloading
// OR the player don't have a weapon
// OR the player have a weapon that already is full
// OR the player don't have any extra bullets to insert into the weapon's magazin
if (isReloading || currentWeapon == null ||
currentWeapon != null && currentWeapon.currentBullets >= currentWeapon.maxBullets ||
currentExtraBullets <= 0) {
return;
}

// The first thing we need to know is how many bullets we can insert into the player's weapon
var diff = currentWeapon.maxBullets - currentWeapon.currentBullets;

// Then we check if the number of bullets missing is less or equal to the number our player has
// we know if this is true, the player must have more or the exact number of extra bullets as
// the current weapon is missing and we can therefore just set the number of bullets on the weapon
// to the max number of bullets the weapon's magazin can store
if (diff <= currentExtraBullets) {
currentExtraBullets -= diff;
currentWeapon.currentBullets = currentWeapon.maxBullets;
} else {
// if the player doesn't have more or the exact number of bullets it's either 0 or greater
// and we can safely add the current number of extra bullets to our weapon and set
// the player's number of extra bullets to 0
currentWeapon.currentBullets += currentExtraBullets;
currentExtraBullets = 0;
}

// And then we do the exact same thing here as in the Shoot() function
// We could just have added a function to handle this, but it might be easier
// to implement other functionality to the reload or shoot function if you keep the seperated
isReloading = true;
StartCoroutine(ReloadCooldown());
}

IEnumerator ReloadCooldown() {
yield return new WaitForSeconds(currentWeapon.reloadTime);
isReloading = false;
}
}

Author: 
Nicolai B. Andersen

Save player data in Unity - Published 4 months ago
blog post cover

Do you want to save and load player data in your Unity project? There are several ways to implement this type of functionality, fx playerPrefs or by using a SQL database and access it via an API. This tutorial will focus on explaining how to get started using the persistence data system.


Game Development Unity tutorial Devlog
READ
Remember Robot Invasion Wars - shooter version 1.0? - Published over 1 year ago

The first version of Robot Invasion Wars. A lot has changed since we released the game on May 7, 2018. The first version 
did only contain 6 maps, 7 weapons, and 6 items. you didn't have any armor, you...


Flashback Mobile Games Free games Robot Invasion Wars App Icon Robot Invasion Wars Devlog
READ
Highlands world of adventures App Icon Highlands world of adventures
Release date: Nov 22, 2018 - Last update: Feb 14, 2019 - Version number: 1.2

3D Platformer Game
Join a journey in a magic world. But be careful, the danger lurks in the dark. Become the most powerful wizard as you return the four staffs of elements and bring peace back to Highlands.



App Store badge US-UK Get it on Google Play



- The game is not supported in your browser - HOW TO JUMP: USE THE W KEY / CLICK INSIDE THE GAME AREA