Static helper that applies a decaying camera shake. Add() raises a 0 to 1 trauma value, and Apply() offsets the camera's rotation and position each frame using Perlin noise scaled by trauma squared.
namespace HorizonRP;
/// <summary>
/// L'écran qui tremble, chez ce client : un choc en voiture secoue la vue de ceux qui sont à
/// bord, d'autant plus fort que le choc est violent. Une « secousse » de 0 à 1 qui retombe
/// en un peu moins d'une seconde ; la vue bouge selon son carré (un petit choc à peine, un mur
/// à fond franchement). Du bruit lissé et non du hasard image par image : la vue tremble, elle
/// ne saute pas.
///
/// Appliquée après le placement de la caméra (RoleplayPlayer.OnPreRender), en décalage : la
/// caméra est reposée à chaque image par le PlayerController, rien ne s'accumule.
/// </summary>
public static class CameraShake
{
static float trauma;
static RealTimeSince sinceApplied;
/// <summary>Décroissance par seconde.</summary>
public static float Decay { get; set; } = 1.3f;
/// <summary>À pleine secousse : tangage et lacet (°), roulis (°), déplacement (pouces).</summary>
public static float MaxAngle { get; set; } = 3.5f;
public static float MaxRoll { get; set; } = 5f;
public static float MaxOffset { get; set; } = 3f;
/// <summary>Un choc, de 0 (léger) à 1 (violent) : la secousse ne fait que monter, jusqu'à 1.</summary>
public static void Add( float amount ) => trauma = MathF.Min( 1, trauma + MathF.Max( 0, amount ) );
public static void Apply( CameraComponent camera )
{
var dt = MathF.Min( sinceApplied, 0.1f );
sinceApplied = 0;
if ( trauma <= 0 || !camera.IsValid() )
return;
trauma = MathF.Max( 0, trauma - Decay * dt );
var shake = trauma * trauma;
var t = RealTime.Now * 22f;
// Le bruit de Perlin reste presque toujours entre 0,3 et 0,7 : étiré pour aller de -1 à 1.
float N( float seed ) => Math.Clamp( (Sandbox.Utility.Noise.Perlin( t, seed * 37.1f ) - 0.5f) * 4f, -1f, 1f );
camera.WorldRotation *= new Angles( N( 1 ) * MaxAngle * shake, N( 2 ) * MaxAngle * shake, N( 3 ) * MaxRoll * shake ).ToRotation();
camera.WorldPosition += camera.WorldRotation * new Vector3( 0, N( 4 ), N( 5 ) ) * MaxOffset * shake;
}
}