diff --git a/src/core/contextType.ts b/src/core/contextType.ts index 8837cc2d1..2a6060182 100644 --- a/src/core/contextType.ts +++ b/src/core/contextType.ts @@ -4253,6 +4253,7 @@ export interface KAPLAYCtx { * Camera shake. * * @param intensity - The intensity of the shake. Default to 12. + * @param duration - The duration of the shake. If not passed, the intensity will determine the duration. * * @example * ```js @@ -4266,7 +4267,7 @@ export interface KAPLAYCtx { * @group Rendering * @subgroup Camera */ - shake(intensity?: number): void; + shake(intensity?: number | Vec2, duration?: number): void; /** * Camera flash. * diff --git a/src/game/camera.ts b/src/game/camera.ts index 4228853e5..650a5d261 100644 --- a/src/game/camera.ts +++ b/src/game/camera.ts @@ -53,8 +53,18 @@ export function flash( return fade; } -export function shake(intensity: number = 12) { - _k.game.cam.shake += intensity; +export function shake(intensity: number | Vec2 = 12, duration?: number = 1) { + _k.game.cam.shake += typeof intensity == "number" + ? intensity + : intensity.len(); + // shake visually "ends" when the intensity reaches 0.001 so we calculate the required alpha to get there in the requested duration + _k.game.cam.shakeSpeed = (Math.log2(_k.game.cam.shake / 0.001) / 7.3391) / duration; + // 7.3391 is 1/half-life if the duration is 1 since the alpha used is 5*dt*speed (see gfx/draw/drawFrame.ts) + + + _k.game.cam.shakeAxis = typeof intensity == "number" + ? undefined + : intensity.unit(); } export function toScreen(p: Vec2): Vec2 { diff --git a/src/game/game.ts b/src/game/game.ts index 5d1435a01..48e3f0549 100644 --- a/src/game/game.ts +++ b/src/game/game.ts @@ -131,6 +131,8 @@ type CamData = { scale: Vec2; angle: number; shake: number; + shakeSpeed?: number; + shakeAxis?: Vec2; transform: Mat23; }; diff --git a/src/gfx/draw/drawFrame.ts b/src/gfx/draw/drawFrame.ts index de329ac45..25b2aad39 100644 --- a/src/gfx/draw/drawFrame.ts +++ b/src/gfx/draw/drawFrame.ts @@ -13,9 +13,13 @@ export function transformFrame() { export function drawFrame() { // calculate camera matrix const cam = _k.game.cam; - const shake = Vec2.fromAngle(rand(0, 360)).scale(cam.shake); + let shake = Vec2.fromAngle(rand(0, 360)).scale(cam.shake); + if (cam.shakeAxis) { + shake = shake.project(cam.shakeAxis); + } + const shakeAlpha = 5 * _k.app.dt() * (cam.shakeSpeed ?? 1); - cam.shake = lerp(cam.shake, 0, 5 * _k.app.dt()); + cam.shake = lerp(cam.shake, 0, shakeAlpha); cam.transform.setIdentity() .translateSelfV(center()) .scaleSelfV(cam.scale)