First-Person Shooter: Raycast gun

Tutorial: First-Person Shooter in Unity - Raycast gun

This is Elympics First-Person Shooter tutorial: part 7. In this part we’ll be creating raycast gun. See: Part 6.

Raycast gun

The second weapon type to implement is a raycast-damage gun. Just like RocketLauncher, it will inherit from the Weapon class, but due to the use of raycast, it will not require additional scripts such as bullet or explosion area.

Let’s go straight to the implementation:

public class RailGun : Weapon
{
	[Header("Parameters:")]
	[SerializeField] private float loadingTime = 1.0f;
	[SerializeField] private float damage = 50.0f;

	[Header("References:")]
	[SerializeField] private new Camera camera = null;

	private ElympicsFloat currentLoadingTime = new ElympicsFloat(0.0f);
	private ElympicsBool isLoadingToShot = new ElympicsBool(false);

	public event Action<float, float> LoadingTimeChanged;
	public event Action<RaycastHit> WeaponFired;

	public override void Initialize()
	{
		base.Initialize();

		currentLoadingTime.ValueChanged += HandleCurrentLoadingTimeChanged;
	}

	protected override void ProcessWeaponAction()
	{
		if (isLoadingToShot)
			return;

		currentLoadingTime.Value = 0.0f;
		isLoadingToShot.Value = true;
	}

	public override void ElympicsUpdate()
	{
		base.ElympicsUpdate();

		if (isLoadingToShot)
		{
			if (currentLoadingTime.Value >= loadingTime)
			{
				ChangeCurrentLoadingTime(0.0f);
				isLoadingToShot.Value = false;
			}
			else
			{
				ChangeCurrentLoadingTime(currentLoadingTime.Value + Elympics.TickDuration);
			}
		}
	}


	private void HandleCurrentLoadingTimeChanged(float lastValue, float newValue)
	{
		if (lastValue >= loadingTime && newValue < loadingTime)
			ProcessRayShot();
	}

	private void ProcessRayShot()
	{
		RaycastHit hit;

		if (Physics.Raycast(camera.transform.position, camera.transform.forward, out hit, Mathf.Infinity))
		{
			if (hit.transform.TryGetComponent<StatsController>(out StatsController statsController))
			{
				statsController.ChangeHealth(-damage, (int)PredictableFor);
			}
		}

		WeaponFired?.Invoke(hit);

		ChangeCurrentLoadingTime(0.0f);
		isLoadingToShot.Value = false;
	}

	public override void SetIsActive(bool isActive)
	{
		base.SetIsActive(isActive);

		if (!isActive)
			isLoadingToShot.Value = false;

		ChangeCurrentLoadingTime(0.0f);
	}

	private void ChangeCurrentLoadingTime(float newCurrentLoadingTime)
	{
		currentLoadingTime.Value = newCurrentLoadingTime;
		LoadingTimeChanged?.Invoke(currentLoadingTime, loadingTime);
	}
}

The parameters that determine the weapon are the damage it deals after hitting the player with a raycast and the recharge time. Unlike the RocketLauncher, this weapon won’t fire immediately after a click but will start the loading operation and will only fire after a certain amount of time. To be able to use raycasts precisely, this component needs a reference to the player’s main camera.

Calling the main method through ProcessWeaponAction first checks if the weapon is being loaded. If not - the timer responsible for loading the weapon is reset and a flag is set that determines the loading status of the weapon.

ElympicsUpdate checks if the timer has reached the value needed for the weapon to fire. If so, the ProcessRayShot method is called.

The ProcessRayShot method is responsible for using the raycast and checking whether it hit the object with the StatsController component - if so, the ChangeHealth method is called on it, whose argument is the previously defined damage value. Finally, all variables are reset and the weapon is ready to be used again.

Having the raycast gun class prepared we can create a raycast gun prefab. For this purpose, as in the case of RocketLauncher, we create an Empty Game Object and immediately add another Empty Game Object to it, which will be a container and a mesh.

First-Person Shooter

Next, we add the previously created script to the parent object “RailGun” and create a prefab from this object.

First-Person Shooter

Then we add the prepared prefab to the FirstPersonViewContainer component in the player’s prefab, positioning it appropriately and adding a reference to the player’s camera.

First-Person Shooter

We also add prefab to the list of available weapons in the LoadoutController in the parent player object.

First-Person Shooter

Let’s try a new weapon!

From now on, players can use both bullet projectile weapons and raycast weapons!

First-Person Shooter

In the next part we’ll focus on player’s respawn! 💀➞🕺


Last modified February 10, 2023: feat: add og images for fps tutorial steps (6fe3197)