Quick answer: Float equality is unreliable. Use (vec - target).length_squared() < 1e-6 or math.isclose per component.
A platformer checks if the player reached a checkpoint at (100, 200). After moving via Vector2 arithmetic, position.xy looks like (100, 200) in debug print but the equality check fails. Float precision strikes again.
The Issue
pos = pygame.math.Vector2(0, 0)
for _ in range(100):
pos += pygame.math.Vector2(1, 2)
print(pos) # Vector2(100, 200) ish
print(pos == (100, 200)) # sometimes False!
Repeated addition accumulates error. Result close to (100, 200) but not exactly.
Fix: Distance-Based Compare
def approx_equal(a, b, eps=1e-4):
return (a - b).length_squared() < eps ** 2
if approx_equal(pos, pygame.math.Vector2(100, 200)):
reached_checkpoint()
length_squared avoids the sqrt cost. Squaring eps gives the equivalent threshold.
Per-Component Tolerance
import math
def vec_isclose(a, b, rel_tol=1e-6):
return (math.isclose(a.x, b[0], rel_tol=rel_tol) and
math.isclose(a.y, b[1], rel_tol=rel_tol))
Uses math.isclose with relative + absolute tolerance. Robust around zero too.
Snap to Grid
For tile-based games, snap position to integer grid for comparison:
tile = (round(pos.x / TILE_SIZE), round(pos.y / TILE_SIZE))
if tile == checkpoint_tile:
reached_checkpoint()
Eliminates float compare entirely by quantizing to discrete cells.
Verifying
Repeat the addition path; checkpoint triggers reliably. Spawn near-misses (off by 0.001) within tolerance to confirm.
“Float == is broken in general. Game code should compare positions with tolerance or snap to grid.”
A 1-pixel tolerance is usually safe for game checkpoints. Tighter tolerance only matters for physics solvers and constraint stability.