Part of these game systems:
First Person Camera Pro
Advanced FPS camera with head bob, weapon sway, lean, recoil system, and FOV zoom for immersive first person gameplay.
How to Use
1
Attach to your Camera (child of player)
2
Adjust mouse sensitivity and vertical clamp
3
Head bob activates automatically when WASD keys are pressed
4
Weapon sway follows mouse movement — tune amount and smoothing
5
Press Q/E to lean left/right
6
Call AddRecoil(vertical, horizontal) from your weapon script on fire
7
Right-click to zoom (ADS), sprint widens FOV automatically
8
Press Escape to toggle cursor lock
Source Code
C#
using UnityEngine;
public class FirstPersonCameraPro : MonoBehaviour
{
[Header("Mouse Look")]
[SerializeField] private float mouseSensitivity = 2f;
[SerializeField] private float verticalClampMin = -85f;
[SerializeField] private float verticalClampMax = 85f;
[SerializeField] private bool lockCursor = true;
[Header("Head Bob")]
[SerializeField] private bool enableHeadBob = true;
[SerializeField] private float bobFrequency = 8f;
[SerializeField] private float bobAmplitudeX = 0.02f;
[SerializeField] private float bobAmplitudeY = 0.04f;
[SerializeField] private float sprintBobMultiplier = 1.4f;
[Header("Weapon Sway")]
[SerializeField] private bool enableWeaponSway = true;
[SerializeField] private float swayAmount = 0.02f;
[SerializeField] private float swayMaxAmount = 0.06f;
[SerializeField] private float swaySmoothing = 6f;
[Header("Lean")]
[SerializeField] private bool enableLean = true;
[SerializeField] private KeyCode leanLeftKey = KeyCode.Q;
[SerializeField] private KeyCode leanRightKey = KeyCode.E;
[SerializeField] private float leanAngle = 15f;
[SerializeField] private float leanOffset = 0.4f;
[SerializeField] private float leanSmoothing = 8f;
[Header("Recoil")]
[SerializeField] private float recoilReturnSpeed = 8f;
[SerializeField] private float recoilSnapSpeed = 15f;
[Header("FOV")]
[SerializeField] private float defaultFOV = 70f;
[SerializeField] private float zoomFOV = 40f;
[SerializeField] private float sprintFOV = 80f;
[SerializeField] private float fovTransitionSpeed = 8f;
[SerializeField] private KeyCode zoomKey = KeyCode.Mouse1;
private Camera cam;
private float xRotation;
private float yRotation;
// Head bob
private Vector3 originalLocalPos;
private float bobTimer;
// Weapon sway
private Vector3 swayPosition;
// Lean
private float currentLean;
private float targetLean;
// Recoil
private Vector3 currentRecoil;
private Vector3 targetRecoil;
// FOV
private float targetFOV;
// External state
private bool isSprinting;
private bool isMoving;
private void Start()
{
cam = GetComponent<Camera>();
if (cam == null) cam = Camera.main;
originalLocalPos = transform.localPosition;
targetFOV = defaultFOV;
if (lockCursor)
{
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
}
private void Update()
{
HandleMouseLook();
HandleRecoil();
HandleHeadBob();
HandleWeaponSway();
HandleLean();
HandleFOV();
// Apply final rotation
Quaternion lookRotation = Quaternion.Euler(xRotation + currentRecoil.x,
yRotation + currentRecoil.y, currentLean);
transform.localRotation = lookRotation;
// Apply position offsets
Vector3 posOffset = originalLocalPos;
if (enableHeadBob) posOffset += GetBobOffset();
if (enableWeaponSway) posOffset += swayPosition;
if (enableLean) posOffset += transform.right * (currentLean / leanAngle * -leanOffset);
transform.localPosition = Vector3.Lerp(transform.localPosition, posOffset, 10f * Time.deltaTime);
// Cursor toggle
if (Input.GetKeyDown(KeyCode.Escape))
{
bool locked = Cursor.lockState == CursorLockMode.Locked;
Cursor.lockState = locked ? CursorLockMode.None : CursorLockMode.Locked;
Cursor.visible = locked;
}
}
private void HandleMouseLook()
{
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
yRotation += mouseX;
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, verticalClampMin, verticalClampMax);
// Apply yaw to parent (player body)
if (transform.parent != null)
transform.parent.rotation = Quaternion.Euler(0f, yRotation, 0f);
}
private void HandleRecoil()
{
targetRecoil = Vector3.Lerp(targetRecoil, Vector3.zero, recoilReturnSpeed * Time.deltaTime);
currentRecoil = Vector3.Slerp(currentRecoil, targetRecoil, recoilSnapSpeed * Time.deltaTime);
}
private void HandleHeadBob()
{
if (!enableHeadBob) return;
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
isMoving = Mathf.Abs(h) > 0.1f || Mathf.Abs(v) > 0.1f;
if (isMoving)
{
float multiplier = isSprinting ? sprintBobMultiplier : 1f;
bobTimer += Time.deltaTime * bobFrequency * multiplier;
}
else
{
bobTimer = Mathf.Lerp(bobTimer, 0f, 4f * Time.deltaTime);
}
}
private Vector3 GetBobOffset()
{
float x = Mathf.Sin(bobTimer) * bobAmplitudeX;
float y = Mathf.Sin(bobTimer * 2f) * bobAmplitudeY;
return new Vector3(x, y, 0f);
}
private void HandleWeaponSway()
{
if (!enableWeaponSway) return;
float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
Vector3 targetSway = new Vector3(
Mathf.Clamp(-mouseX * swayAmount, -swayMaxAmount, swayMaxAmount),
Mathf.Clamp(-mouseY * swayAmount, -swayMaxAmount, swayMaxAmount),
0f
);
swayPosition = Vector3.Lerp(swayPosition, targetSway, swaySmoothing * Time.deltaTime);
}
private void HandleLean()
{
if (!enableLean) return;
targetLean = 0f;
if (Input.GetKey(leanLeftKey)) targetLean = leanAngle;
else if (Input.GetKey(leanRightKey)) targetLean = -leanAngle;
currentLean = Mathf.Lerp(currentLean, targetLean, leanSmoothing * Time.deltaTime);
}
private void HandleFOV()
{
if (cam == null) return;
if (Input.GetKey(zoomKey))
targetFOV = zoomFOV;
else if (isSprinting)
targetFOV = sprintFOV;
else
targetFOV = defaultFOV;
cam.fieldOfView = Mathf.Lerp(cam.fieldOfView, targetFOV, fovTransitionSpeed * Time.deltaTime);
}
/// <summary>
/// Add recoil impulse. Call from weapon system on fire.
/// </summary>
public void AddRecoil(float verticalRecoil, float horizontalRecoil)
{
targetRecoil += new Vector3(-verticalRecoil,
Random.Range(-horizontalRecoil, horizontalRecoil), 0f);
}
/// <summary>
/// Set sprint state from external controller.
/// </summary>
public void SetSprinting(bool sprinting)
{
isSprinting = sprinting;
}
}