Quick answer: Collision is computed in room coordinates and is correct — the issue is visual. Snap the camera to whole pixels with floor() and use tilemap_get_at_pixel for collision tests.

Zoom the camera in to 4× for a closer view of the pixel art. The player visibly walks off the edge of a platform but doesn’t fall — or, worse, gets stopped by an invisible wall. At default 1× zoom, everything was fine.

What’s Actually Wrong

GameMaker collision logic runs in room coordinates and doesn’t know or care about camera zoom. A 4× zoom doesn’t magnify or distort the collision shapes — they remain pixel-accurate in the room. What zoom does is amplify two visual issues:

Fix 1: Snap the Camera

/// In your camera controller’s Step
var target_x = obj_player.x - camera_get_view_width(view_camera[0]) / 2;
var target_y = obj_player.y - camera_get_view_height(view_camera[0]) / 2;

// Smoothly approach, then snap
camera_x = lerp(camera_x, target_x, 0.1);
camera_y = lerp(camera_y, target_y, 0.1);

camera_set_view_pos(view_camera[0], floor(camera_x), floor(camera_y));

The lerp produces a smooth follow; the floor at the end ensures the camera ends up at integer coordinates each frame. Visual stability returns.

Fix 2: Snap Sprite Positions for Rendering

If sprite positions are fractional (very common when using a velocity vector), draw at the rounded position:

/// obj_player Draw
draw_sprite(sprite_index, image_index, floor(x), floor(y));

Collision still uses the precise x and y; only the visual is snapped. The player’s movement remains smooth in the underlying numbers, but renders to whole-pixel positions every frame.

Fix 3: Use tilemap_get_at_pixel

For custom tile-based collision (instead of place_meeting against a wall object), the canonical function is:

var tile_id = tilemap_get_at_pixel(tilemap_id, x + hsp, y);
if (tile_id != 0) {
    // solid tile at that position
}

This handles the tilemap’s position offset internally. Rolling your own math — tile_x = (x - tilemap.x) div tile_w — is correct only if you remember to subtract the tilemap origin and account for negative coordinates.

Fix 4: Use Application Surface Scaling

Instead of camera zoom, render at native resolution to the application surface and scale the surface for display:

/// Room Start
surface_resize(application_surface, 320, 180);
display_set_gui_size(1280, 720);   // 4x

The game renders to a small surface that’s upscaled by 4× for display. No fractional coordinates in the render path; pixel art stays crisp. Camera operates at native 320×180 and never zooms.

Verifying

Draw a debug rectangle at the player’s collision bbox using the precise coordinates, and another at the snapped render position. Move the player and watch the offset. After the fixes, both rectangles overlap perfectly each frame.

“Collision is correct. Visuals are lying. Snap camera and sprite to whole pixels.”

For pixel-art games, prefer surface upscaling over camera zoom — eliminates this entire class of bug.