macroquad
macroquad copied to clipboard
glitches with tilemap graphics in some zoom levels
I adjusted camera and zoom level from examples/platformer.rs and noticed a graphic glitch in the background tiles with some zoom levels, as can be seen in this video clip. The same issue is seen both on Windows and macOS. The issue goes away at zoom level 2 and 3.
https://user-images.githubusercontent.com/181531/113839861-5f024080-9790-11eb-972a-f35e880f5e53.mp4
source
use macroquad::prelude::*;
use macroquad_tiled as tiled;
use physics_platformer::*;
enum Direction {
Left,
Right,
}
struct Player {
collider: Actor,
speed: Vec2,
facing: Direction,
}
struct Platform {
collider: Solid,
speed: f32,
}
const MAP_WIDTH: f32 = 320.;
const MAP_HEIGHT: f32 = 152.;
#[macroquad::main("reprod_bug")]
async fn main() {
let tileset = load_texture("examples/tileset.png").await;
set_texture_filter(tileset, FilterMode::Nearest);
let tiled_map_json = load_string("examples/map.json").await.unwrap();
let tiled_map = tiled::load_map(&tiled_map_json, &[("tileset.png", tileset)], &[]).unwrap();
let mut static_colliders = vec![];
for (_x, _y, tile) in tiled_map.tiles("main layer", None) {
static_colliders.push(tile.is_some());
}
let mut world = World::new();
world.add_static_tiled_layer(static_colliders, 8., 8., 40, 1);
let mut player = Player {
collider: world.add_actor(vec2(200.0, 80.0), 8, 8),
speed: vec2(0., 0.),
facing: Direction::Right,
};
let mut platform = Platform {
collider: world.add_solid(vec2(170.0, 130.0), 32, 8),
speed: 50.,
};
let mut zoom = 1.;
let mut camera = Camera2D::from_display_rect(Rect::new(0.0, 0.0, MAP_WIDTH, MAP_HEIGHT));
loop {
clear_background(DARKBLUE);
let frame_time = get_frame_time();
// camera follow player
{
let pos = world.actor_pos(player.collider);
camera.target = vec2(pos.x, pos.y);
camera.zoom = vec2(1. / MAP_WIDTH * zoom, -1. / MAP_HEIGHT * zoom);
set_camera(camera);
}
tiled_map.draw_tiles("main layer", Rect::new(0.0, 0.0, MAP_WIDTH, MAP_HEIGHT), None);
// draw platform
{
let pos = world.solid_pos(platform.collider);
tiled_map.spr_ex(
"tileset",
Rect::new(6.0 * 8.0, 0.0, 32.0, 8.0),
Rect::new(pos.x, pos.y, 32.0, 8.0),
)
}
// draw player
{
// sprite id from tiled
const PLAYER_SPRITE: u32 = 120;
let pos = world.actor_pos(player.collider);
match player.facing {
Direction::Right => tiled_map.spr("tileset", PLAYER_SPRITE, Rect::new(pos.x, pos.y, 8.0, 8.0)),
Direction::Left => tiled_map.spr("tileset", PLAYER_SPRITE, Rect::new(pos.x + 8.0, pos.y, -8.0, 8.0)),
}
}
// platform movement
{
world.solid_move(platform.collider, platform.speed * frame_time, 0.0);
let pos = world.solid_pos(platform.collider);
if platform.speed > 1. && pos.x >= 220. {
platform.speed *= -1.;
}
if platform.speed < -1. && pos.x <= 150. {
platform.speed *= -1.;
}
}
// player movement control
{
let pos = world.actor_pos(player.collider);
let on_ground = world.collide_check(player.collider, pos + vec2(0., 1.));
if on_ground == false {
player.speed.y += 500. * frame_time;
}
if is_key_down(KeyCode::D) {
player.speed.x = 100.0;
player.facing = Direction::Right;
} else if is_key_down(KeyCode::A) {
player.speed.x = -100.0;
player.facing = Direction::Left;
} else {
player.speed.x = 0.;
}
if is_key_pressed(KeyCode::Space) {
if on_ground {
player.speed.y = -120.;
}
}
world.move_h(player.collider, player.speed.x * frame_time);
world.move_v(player.collider, player.speed.y * frame_time);
}
// zoom
{
let (_, mouse_wheel_y) = mouse_wheel();
if mouse_wheel_y < 0. {
zoom -= 0.5;
if zoom < 0.5 {
zoom = 0.5;
}
}
if mouse_wheel_y > 0. {
zoom += 0.5;
if zoom > 5. {
zoom = 5.;
}
}
}
// Back to screen space, render some text
set_default_camera();
draw_text(&format!("zoom {}", zoom), 10.0, 20.0, 30.0, WHITE);
next_frame().await
}
}
Could be caused by aliasing? All artifacts seem appear on the same corner tile.
I have the same issue and I noticed that it can be fixed by tweaking the size of the source rectangle, as is done in macroquad tiled:
fn sprite_rect(&self, ix: u32) -> Rect {
let sw = self.tilewidth as f32;
let sh = self.tileheight as f32;
let sx = (ix % self.columns) as f32 * (sw + self.spacing as f32) + self.margin as f32;
let sy = (ix / self.columns) as f32 * (sh + self.spacing as f32) + self.margin as f32;
// TODO: configure tiles margin
Rect::new(sx + 1.1, sy + 1.1, sw - 2.2, sh - 2.2)
}
[...]
draw_texture_ex(
tileset.texture,
dest.x,
dest.y,
WHITE,
DrawTextureParams {
dest_size: Some(vec2(dest.w, dest.h)),
source: Some(Rect::new(
spr_rect.x - 1.0,
spr_rect.y - 1.0,
spr_rect.w + 2.0,
spr_rect.h + 2.0,
)),
..Default::default()
},
);``
This will cut off ~one pixel around your tile, however, so creating tilesheets with padding around the tiles, might be an idea.
I believe it is due to float rounding and nearest neighbor filtering, but I am not sure....
my fix was
camera.target = vec2(pos.x.round(), pos.y.round());
so everything is drawn pixel accurate and there are no sub-pixel problems
my fix was
camera.target = vec2(pos.x.round(), pos.y.round());so everything is drawn pixel accurate and there are no sub-pixel problems
Beautifully simple. I will try this as well. Had some way more intricate solutions in mind, but simple is better. Thanks for the tip!
my fix was
camera.target = vec2(pos.x.round(), pos.y.round());so everything is drawn pixel accurate and there are no sub-pixel problems
Thanks, but applying this to the original snippet does not fix the issue :-(

I posted another reply, based on some incorrect assumptions, so I deleted it. I would go through my code, step by step, and look for anything that might cause off-pixel positioning.
I posted another reply, based on some incorrect assumptions, so I deleted it. I would go through my code, step by step, and look for anything that might cause off-pixel positioning.
The complete code in order to reproduce this issue is in the first post.
This problem is happening with my own game as well. It appears the more zoomed in you are the more prevalent it is. Rounding it helps but causes camera stuttering and does not completely rid it of the issue. I am making an 8x8 pixel art game. Should I scale up the game? Should I put a gap between tiles in my atlas?