Smooth Camera Follow 2D
Smooth camera follow with offset, dead zone, and look-ahead for 2D games.
How to Use
Attach to your Main Camera
Drag your player into the Target field
Adjust offset and smooth speed
Optionally enable bounds to limit camera area
Features
- Smooth Lerp-based camera following with adjustable speed
- Configurable offset for custom framing
- Look-ahead in movement direction for better visibility
- Optional camera bounds to clamp within level edges
- Null-safe target check prevents errors if player is destroyed
- Works in LateUpdate for jitter-free rendering
When to Use This
Ideal for 2D platformers, side-scrollers, and top-down 2D games that need a camera smoothly tracking the player. Use this when you want a polished camera that looks ahead of the player's movement and stays within defined level boundaries. Pairs naturally with any 2D player controller.
Common Mistakes
Forgetting to set the Z offset to -10 (or your camera's Z position) will place the camera at the same depth as sprites, making nothing visible. If useBounds is enabled but minBounds/maxBounds are left at zero, the camera will be locked to the origin. Make sure the Target field is assigned — the script silently returns if target is null.
Source Code
using UnityEngine;
public class CameraFollow2D : MonoBehaviour
{
[Header("Target")]
[SerializeField] private Transform target;
[Header("Follow Settings")]
[SerializeField] private Vector3 offset = new Vector3(0f, 1f, -10f);
[SerializeField] private float smoothSpeed = 8f;
[Header("Look Ahead")]
[SerializeField] private float lookAheadDistance = 2f;
[SerializeField] private float lookAheadSpeed = 4f;
[Header("Bounds (Optional)")]
[SerializeField] private bool useBounds;
[SerializeField] private Vector2 minBounds;
[SerializeField] private Vector2 maxBounds;
private float currentLookAhead;
private void LateUpdate()
{
if (target == null) return;
// Look ahead in movement direction
float targetLookAhead = Input.GetAxisRaw("Horizontal") * lookAheadDistance;
currentLookAhead = Mathf.Lerp(currentLookAhead, targetLookAhead, Time.deltaTime * lookAheadSpeed);
Vector3 desiredPosition = target.position + offset;
desiredPosition.x += currentLookAhead;
Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed * Time.deltaTime);
// Clamp to bounds
if (useBounds)
{
smoothedPosition.x = Mathf.Clamp(smoothedPosition.x, minBounds.x, maxBounds.x);
smoothedPosition.y = Mathf.Clamp(smoothedPosition.y, minBounds.y, maxBounds.y);
}
transform.position = smoothedPosition;
}
}