CameraJuice.cs

Component that adds camera shake and a brief hitstop effect. It stores a target CameraComponent, applies randomized positional offsets each update while shake intensity is positive, and temporarily lowers PhysicsWorld.TimeScale for hitstop via an async delay.

NetworkingFile Access
using Sandbox;
using System;
using System.Threading.Tasks;

public sealed class CameraJuice : Component
{
	public static CameraJuice Instance { get; private set; }

	[Property] public CameraComponent TargetCamera { get; set; }

	private float _shakeIntensity = 0f;
	private float _shakeDecay = 5f;
	private Vector3 _originalPos;

	protected override void OnStart()
	{
		Instance = this;
		if ( TargetCamera == null )
		{
			TargetCamera = Components.Get<CameraComponent>();
		}
	}

	protected override void OnUpdate()
	{
		if ( _shakeIntensity > 0f && TargetCamera.IsValid() )
		{
			Vector3 randomOffset = new Vector3(
				Game.Random.Float( -1f, 1f ),
				Game.Random.Float( -1f, 1f ),
				Game.Random.Float( -1f, 1f )
			) * _shakeIntensity;

			TargetCamera.Transform.LocalPosition += randomOffset * Time.Delta;
			_shakeIntensity = MathF.Max( 0f, _shakeIntensity - Time.Delta * _shakeDecay );
		}
	}

	public void Shake( float intensity = 15f, float decay = 8f )
	{
		_shakeIntensity = intensity;
		_shakeDecay = decay;
	}

	public async void TriggerHitstop( float durationSeconds = 0.05f )
	{
		Scene.PhysicsWorld.TimeScale = 0.05f;
		await Task.DelaySeconds( durationSeconds );
		if ( Scene.IsValid() )
		{
			Scene.PhysicsWorld.TimeScale = 1.0f;
		}
	}
}