Source code for mini_arcade_core.engine.render.effects.crt
"""
CRT screen-space post effect.
"""
# Justification: PoC code for v1.
# pylint: disable=duplicate-code
from __future__ import annotations
from dataclasses import dataclass
from math import sin
from mini_arcade_core.backend import Backend
from mini_arcade_core.engine.render.context import RenderContext
from mini_arcade_core.engine.render.effects.base import EffectParams
[docs]
@dataclass
class CRTEffect:
"""
CRT screen-space post effect.
Simulates CRT scanlines with optional wobble.
"""
effect_id: str = "crt"
# Justification: This is PoC code for v1.
# pylint: disable=too-many-locals
[docs]
def apply(self, backend: Backend, ctx: RenderContext):
"""Apply the CRT effect to the current render context."""
vp = ctx.viewport
x0, y0 = vp.offset_x, vp.offset_y
w, h = vp.viewport_w, vp.viewport_h
stack = ctx.meta.get("effects_stack")
params: EffectParams = (
stack.params.get(self.effect_id, EffectParams())
if stack
else EffectParams()
)
intensity = max(0.0, min(1.0, params.intensity))
if intensity <= 0.0:
return
# Use a time value from ctx.meta (added in Game.run below)
t = float(ctx.meta.get("time_s", 0.0))
wobble = float(params.wobble_speed)
# Clip to viewport so it works with all viewport modes/resolutions
backend.render.set_clip_rect(x0, y0, w, h)
# Scanlines: draw every N lines with low alpha
# Note: assumes Backend supports alpha in color tuples.
spacing = 2 # tweakable
base_alpha = 120 # int(40 * intensity) # subtle
line_color = (255, 255, 255, base_alpha)
# "Wobble": tiny horizontal shift that animates over time
# Keep it tiny to avoid looking like a bug.
for y in range(y0, y0 + h, spacing):
# shift in pixels, -2..2-ish
shift = int(2.0 * intensity * sin((y * 0.05) + (t * wobble)))
backend.render.draw_line(
x0 + shift, y, x0 + w + shift, y, color=line_color
)
backend.render.clear_clip_rect()