Tiled

tiled is a free, open source level editor.

macroquad-tiled - is an optional part of macroquad responsible for tiled editor integration.

Designing the world

screenshot

This is how the big level made with very basic ideas to what we are going to use here looked in tiled

Loading in macroquad


#![allow(unused)]
fn main() {
// Load json file exported from tiled
let tiled_map = load_file("level.json").await;

// We used some png files in the tiled
// Those pngs are saved alongside with .json, so lets load them as well
let tileset_texture = load_texture("tileset.png").await;
let background_texture = load_texture("background.png").await;

// And feed all the data loaded to `macroquad-tiled`
let map = tiled::load_map(
    &tiled_map,
    &[
        ("tileset.png", tileset_texture),
        ("background.png", background_texture),
    ],
)?;
}

Drawing: Tilemaps


#![allow(unused)]
fn main() {
// Lets draw the whole map full screen
// Default macroquad camera is pixel perfect with (0, 0) in top left corner and (screen_width(), screen_height()) on bottom right
let dest_rect = Rect::new(0., 0., screen_width(), screen_height());

// We used only part of tiled canvas to create our first level
// So lets draw only that part of the canvas
// Area is hardcoded for now, but we will use the technique of drawing parts of tiled canvas
// to jump through level sections in the future
let source_rect = Rect::new(0, 0, 22, 11);

// And just draw our level!
map.draw_tiles("level", dest_rect, level.area);
}

#level

It works, we got our nice static background

Drawing: Sprites

Now we got the world rendered, let's add some characters. We can draw the player right into the tileset and use draw_tiles with different source/destination rects on this part of the tilemap right as we did it for the world.
But macroquad-tiled have some special functions dedicated for sprites drawing.


#![allow(unused)]
fn main() {
impl Player {
    // name of the tileset contains player sprites in the tiled map file
    const TILESET: &'static str = "tileset";
    // sprite number in that tileset
    const PLAYER_TILE: u32 = 11;

    fn draw(&mut self) {
        self.map.spr(
            "tileset",
            PLAYER_TILE,
            Rect::new(self.pos.x(), self.pos.y(), 8., 8.),
        );
    }
}
}

player_spr

Retro consoles vibe

Now we can draw sprites from tileset and draw the whole maps from the same tileset. This actually is pretty close to what we had for game development back into early video console days with hardware sprite controllers!
Also some fantasy consoles like pico-8 use very similar video memory model.

Paralax

In tiled we had two layers - with foreground and background.

First step: draw the background as well:


#![allow(unused)]
fn main() {
self.tiled_map.draw_tiles("back", dest_rect, source_rect);
self.tiled_map.draw_tiles("level", dest_rect, source_rect);
}

#level_with_background

There is really easy way to add some life to static tiled world: parallax effect.

TODO: figure out order of the chapters to not confuse with suddedenly appeared camera

There are various way to do parallax scrolling, but lets implement some simple hack to demonstrate the idea.

    
// this (with magically appeared camera) simple formula will give us 0..1 value
// with 0 on left side of the level and 1 on the right 
let parallax_offset = level.camera / vec2(deset_rect.w, dest_rect.h);

// we can just draw the background layer slightly bigger than foreground
// to make some room to move it around
let mut dest_rect_parallax = Rect::new(
    -PARALLAX_EXTRA_W / 2.,
    -PARALLAX_EXTRA_H / 2., 
    dest_rect.w + PARALLAX_EXTRA_W / 2., 
    dest_rect.h + PARALLAX_EXTRA_H / 2.);
// and now shift it by our offset
dest_rect_parallax += parallax_offset;
    
self.tiled_map.draw_tiles("back", dest_rect_parallax, level.area);
self.tiled_map.draw_tiles("level", dest_rect, source_rect);

Now the background is moving slightly different than the foreground.

parallax