terminalCode Example

Sandbox: CameraNoiseSystem — screen shake, punch, and recoil with ICameraSetup

calendar_today May 4, 2026 schedule ~1 min read person patrickjr verified 50

CameraNoiseSystem — Screen Shake, Punch, and Recoil Effects

CameraNoiseSystem is a GameObjectSystem that implements ICameraSetup to apply camera noise effects (shake, punch, recoil) each frame. Effects are instantiated by constructing them — the constructor auto-registers with the system.

System

CSHARP
public class CameraNoiseSystem : GameObjectSystem<CameraNoiseSystem>, ICameraSetup
{
    List<BaseCameraNoise> _all = new();

    void ICameraSetup.PreSetup( CameraComponent cc )
    {
        foreach ( var effect in _all )
            effect.Update();

        _all.RemoveAll( x => x.IsDone );
    }

    void ICameraSetup.PostSetup( CameraComponent cc )
    {
        foreach ( var effect in _all )
            effect.ModifyCamera( cc );
    }
}

public abstract class BaseCameraNoise
{
    public float LifeTime { get; protected set; }
    public float CurrentTime { get; protected set; }
    public float Delta => CurrentTime.LerpInverse( 0, LifeTime, true );
    public float DeltaInverse => 1 - Delta;

    public BaseCameraNoise()
    {
        // Auto-register with the system on construction
        CameraNoiseSystem.Current.Add( this );
    }

    public virtual bool IsDone => CurrentTime > LifeTime;
    public virtual void Update() => CurrentTime += Time.Delta;
    public virtual void ModifyCamera( CameraComponent cc ) { }
}

Punch — spring-damped angular kick

CSHARP
// Used for melee hits, explosions, etc.
class Punch : BaseCameraNoise
{
    public Punch( Vector3 target, float time, float frequency, float damp )
    {
        damping.Current = target * GamePreferences.Screenshake;
        damping.Target = 0;
        damping.Frequency = frequency;
        damping.Damping = damp;
        deathTime = time;
    }

    public override void ModifyCamera( CameraComponent cc )
    {
        cc.WorldRotation *= new Angles( damping.Current );
    }
}

// Usage in melee weapon:
new Sandbox.CameraNoise.Punch(
    new Vector3( Random.Shared.Float( -10, -15 ), Random.Shared.Float( -10, 0 ), 0 ),
    time: 1.0f, frequency: 3, damp: 0.5f
);

Shake — random spring-damped positional shake

CSHARP
class Shake : BaseCameraNoise
{
    public Shake( float amount, float time )
    {
        this.amount = amount * GamePreferences.Screenshake;
        deathTime = time;
    }
    // Applies random angular offsets via spring damping
}

// Usage:
new Sandbox.CameraNoise.Shake( 0.3f, 1.2f );

Recoil — sinusoidal roll shake

CSHARP
// Recoil creates a RollShake internally
class Recoil : BaseCameraNoise
{
    public Recoil( float amount, float speed = 1 )
    {
        new RollShake() { Size = 0.5f * amount * GamePreferences.Screenshake, Waves = 3 * speed };
    }
}

class RollShake : BaseCameraNoise
{
    public float Size { get; set; } = 3.0f;
    public float Waves { get; set; } = 3.0f;

    public RollShake() { LifeTime = 0.3f; }

    public override void ModifyCamera( CameraComponent cc )
    {
        var delta = Delta;
        var s = MathF.Sin( delta * MathF.PI * Waves * 2 );
        cc.WorldRotation *= new Angles( 0, 0, s * Size ) * Easing.EaseOut( DeltaInverse );
    }
}

// Usage in BaseBulletWeapon:
if ( !Owner.Controller.ThirdPerson && Owner.IsLocalPlayer )
    _ = new Sandbox.CameraNoise.Recoil( config.CameraRecoilStrength, config.CameraRecoilFrequency );

ICameraSetup interface

CSHARP
public interface ICameraSetup : ISceneEvent<ICameraSetup>
{
    void PreSetup( CameraComponent cc ) { }  // Before viewmodel placement
    void Setup( CameraComponent cc ) { }     // Viewmodel placement
    void PostSetup( CameraComponent cc ) { } // After viewmodel (includes viewmodel in effects)
}

Key points

Was this helpful?