terminalCode Example
Sandbox: CameraNoiseSystem — screen shake, punch, and recoil with ICameraSetup
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
- All effects respect GamePreferences.Screenshake — set to 0 to disable all camera noise
- Effects auto-register on construction and auto-remove when IsDone returns true
- Punch uses a Vector3.SpringDamped for smooth spring-physics-based angular kick
- RollShake lifetime is fixed at 0.3s with EaseOut falloff
- Only apply camera noise when Owner.IsLocalPlayer — never on proxies
Was this helpful?