Quick answer: Keep mesh vertex coordinates in sprite-local space (0..1 normalized). For screen-space effects, recompute vertices per tick based on camera offset.
A water sprite uses Mesh distortion to add a wavy surface. Looks great when the camera is centered; when you pan, the distortion shifts incorrectly. The vertex math used screen position rather than sprite UV.
Sprite-Local Coordinates
Mesh vertices are normalized [0, 1] within the sprite’s bounding box. A vertex at (0.5, 0.5) is the sprite’s center regardless of where the sprite is on screen. Use these for stable distortions:
Every tick:
For each mesh vertex (i, j):
var u = i / mesh_width
var v = j / mesh_height
var wave = sin(u * 6.28 + time) * 0.05
Sprite Mesh Set vertex at (i, j) to (u + wave, v)
Wave depends on u (sprite-local) and time, never on camera position. Distortion stays consistent regardless of pan.
Screen-Space Effects
For effects that should look like camera-relative ripples (e.g., heat haze that stays put on screen):
Every tick:
var cam_x = ViewportLeft("Gameplay")
var cam_y = ViewportTop("Gameplay")
For each vertex:
var world_x = sprite.X + u * sprite.Width
var screen_x = world_x - cam_x
var wave = sin(screen_x * 0.05 + time) * 0.03
Set vertex offset Y to wave
Now wave depends on screen position; updates correctly as camera pans because you recompute each tick.
Performance Note
Per-tick vertex updates cost CPU. For 100 distorted sprites with 64 vertices each = 6400 vertex updates/tick. If perf suffers, use an Effect (shader) on the sprite instead — GPU handles per-pixel distortion with no CPU cost.
Verifying
Pan the camera around the water sprite. Distortion should remain stable on the sprite (sprite-local) or move with the screen (screen-space) depending on your intent. Match the math to the desired behavior.
“Sprite-local for sprite-bound distortions; screen-space for screen-bound. Compute vertices in the right coordinate space.”
For heavy mesh effects, profile early — shaders are often the better choice for 60 Hz updates on many sprites.