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.
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;
}
}
}