18. Camera Effects

# 18 - Camera Effects The camera isn't just for framing the world — it's a game-feel tool. Subtle screen shakes, zooms, and trauma effects communicate impact, danger, and excitement to the player. LunaEngine's camera effect system handles all of this with a stackable, extensible architecture. --- ## Camera Shake The simplest effect. Use `camera.shake()` after an explosion, a hit, or a boss slam. ```python from lunaengine.graphics.camera import CameraShakeType class GameScene(Scene): def explosion_at(self, pos): # Positional shake: random offset added to camera position each frame self.camera.shake( intensity=1.0, # 0.0 (none) to 1.0 (max) duration=0.5, # Lasts half a second shake_type=CameraShakeType.POSITIONAL ) def boss_stomp(self): # Rotational shake: randomly rotates the camera instead self.camera.shake( intensity=0.6, duration=0.8, shake_type=CameraShakeType.ROTATIONAL ) def catastrophic_event(self): # Trauma shake: combines positional AND rotational shaking self.camera.shake( intensity=1.0, duration=1.5, shake_type=CameraShakeType.TRAUMA ) ``` All shake effects **decay linearly** over their duration — starting strong and fading out naturally. --- ## Trauma System For systems where multiple events stack (e.g. getting hit repeatedly), the **Trauma system** is better than calling `shake()` repeatedly. Trauma accumulates and decays over time. ```python def player_hit(self): # Add 0.4 trauma. Trauma is clamped to 1.0. # Each hit stacks, so 3 rapid hits = max shake self.camera.add_trauma(0.4) ``` The `TraumaEffect` inside the camera automatically decays trauma at `1.5 units/second` by default, so heavy hits produce prolonged shaking. --- ## Custom Camera Effects You can create your own stackable effects by subclassing `CameraEffect`: ```python from lunaengine.graphics.camera import CameraEffect import math class PulseZoomEffect(CameraEffect): """Rapidly pulses the camera zoom in and out.""" def __init__(self, amplitude=0.1, frequency=10.0, duration=1.0): self.amplitude = amplitude self.frequency = frequency self.duration = duration self.time_left = duration def update(self, dt: float) -> bool: self.time_left -= dt return self.time_left > 0 # Returns False when finished def apply(self, camera) -> None: t = (self.duration - self.time_left) / self.duration pulse = math.sin(t * self.frequency * 2 * math.pi) * self.amplitude camera.zoom += pulse # Usage in your scene class GameScene(Scene): def player_leveled_up(self): self.camera.add_effect(PulseZoomEffect(amplitude=0.05, frequency=8, duration=0.8)) ``` --- ## Smooth Zoom Transitions ```python def zoom_in_on_boss(self): # Smoothly zoom to 2x over the next frames (interpolated by smooth_speed) self.camera.set_zoom(2.0, smooth=True) def zoom_back_out(self): self.camera.set_zoom(1.0, smooth=True) ``` The zoom is interpolated using the camera's `smooth_speed` and `interpolation_type`, so the transition is gradual and natural. --- ## Combining Effects The real power of the camera effect system is in **combining** effects for complex moments: ```python def final_boss_death(self): # A dramatic sequence: trauma shake + zoom out + sepia tone (from OpenGL filter) self.camera.add_trauma(1.0) self.camera.set_zoom(0.7, smooth=True) from lunaengine.backend import FilterType self.engine.renderer.add_sepia(intensity=0.5) self.engine.renderer.add_vignette(intensity=0.9) ``` These small technical details create the cinematic moments that players remember.