Layers in depth
You met layers back in Drawing on the screen: sheets of glass, one above the other, each with its own drawing. They can do a lot more than sit there. A layer can fade, hide, show only a window of itself, slide, repeat forever, and even lie down flat like a floor.
Making layers
Layer 0 is always there. CREATE LAYER makes another, with a number and a
size, and a bigger number sits above a smaller one. Sprites go on top of them
all.
SET LAYER says which layer drawing goes to from now on. For a few lines of
drawing on another layer, WITH LAYER is tidier: inside the block, drawing
goes to that layer, and after it, I go back to the one you were on.
SCREEN 320, 240
LOADIMG "city.png", "city"
LOADIMG "hero.png", "tan"
CLS 1
CREATE LAYER 1, 320, 240
CREATE LAYER 2, 320, 240
WITH LAYER 1
PUTIMAGE (32, 0), "city"
END WITH LAYER
WITH LAYER 2
PUTIMAGE (140, 184), "tan"
END WITH LAYER
PRINT "blue on 0, city on 1, the hero on 2"
blue on 0, city on 1, the hero on 2
A new layer starts out see-through, so the blue of layer 0 shows down the sides of the city, and the city shows around the hero.
Crash-tan’s Tip:
CLSon a layer clears it to solid black, which hides everything under it, sprites included if it’s the top one. Clear the bottom layer, and leave the ones above for things that stay put or that you draw once.
How a layer shows
SET LAYER takes modifiers that change how a layer shows, without touching
what’s drawn on it. alpha makes the whole layer see-through, from 0 to 1.
visible(FALSE) hides it and visible shows it again. clip shows only the
part of it inside a rectangle of the screen, given as two corners with ->
between them.
Here, the city on layer 1 shows only through a window that slides back and forth, and a red warning stripe on layer 2 blinks on and off:
SCREEN 320, 240
LOADIMG "city.png", "city"
MEMORYBUFFER "stripe", 320, 16
LINE (0, 0) -> (319, 15), 4 !! box fill
END MEMORYBUFFER
CREATE LAYER 1, 320, 240
WITH LAYER 1
PUTIMAGE (0, 0), "city" !! scale_to((320, 240))
END WITH LAYER
CREATE LAYER 2, 320, 240
WITH LAYER 2
PUTIMAGE (0, 200), "stripe"
END WITH LAYER
SET LAYER 2 !! alpha(0.7)
x = 0
dx = 2
shown = TRUE
DO
IF COOLDOWN(1)
x = x + dx
IF x <= 0 OR x >= 220 THEN dx = -dx
SET LAYER 1 !! clip((x, 40) -> (x + 100, 190))
END IF
IF COOLDOWN(30)
shown = NOT shown
SET LAYER 2 !! visible(shown)
END IF
LOOP
SET LAYER 1 !! clip(...) makes layer 1 the one drawing goes to as well, so
if your loop draws too, say which layer you mean first. SET LAYER 1 !! clear_clip shows the whole layer again.
Sliding along
SET SCROLL slides the current layer, by x across and y down, and
getscroll gives back where a layer has been slid to: the current one, or
the one whose number you give it.
SCREEN 320, 240
CREATE LAYER 1, 320, 240
WITH LAYER 1
SET SCROLL 40, 10
END WITH LAYER
PRINT "layer 0:", getscroll(0), " layer 1:", getscroll(1)
layer 0: (0, 0) layer 1: (40, 10)
Slide a layer far enough and you run off the end of its picture. wrap_x
fixes that: the layer repeats, side by side, forever. The city is 256 pixels
across, so here it repeats every 256 pixels and never runs out.
SCREEN 320, 240
LOADIMG "city.png", "city"
CREATE LAYER 1, 256, 256
SET LAYER 1 !! wrap_x
PUTIMAGE (0, 0), "city"
x = 0
DO
IF COOLDOWN(1)
x = x + 1
SET SCROLL x, 0
END IF
LOOP
The layer is the city’s size, not the screen’s, so it wraps right where the
picture ends. wrap_y does the same up and down.
Far away and close by
Look out of a train window: the hills far away hardly move, and the posts by the track whip past. Scroll layers at different speeds and you get the same thing. That’s parallax, and it’s the cheapest way I know to make a flat game look deep.
The city goes on layer 1 and moves at a quarter of the speed. A street of my own, with a lamp post, goes on layer 2 at full speed. Both wrap. The hero stands on the street as a sprite, and the arrow keys walk them along.
SCREEN 320, 240
LOADIMG "city.png", "city"
LOADIMG "hero.png", "tan"
MEMORYBUFFER "street", 160, 240
LINE (0, 200) -> (159, 239), 8 !! box fill
LINE (0, 200) -> (159, 203), 7 !! box fill
LINE (78, 110) -> (81, 199), 7 !! box fill
CIRCLE (80, 106), 6, 14, 14
END MEMORYBUFFER
CREATE LAYER 1, 256, 256
SET LAYER 1 !! wrap_x
PUTIMAGE (0, 0), "city"
CREATE LAYER 2, 160, 240
SET LAYER 2 !! wrap_x
PUTIMAGE (0, 0), "street"
ENABLE SPRITE 0
SET SPRITE 0 !! image("tan") x(140) y(160)
SHOW SPRITE 0
x = 0
DO
IF COOLDOWN(1)
IF keydown("Right") THEN x = x + 3
IF keydown("Left") THEN x = x - 3
WITH LAYER 1
SET SCROLL x / 4, 0
END WITH LAYER
WITH LAYER 2
SET SCROLL x, 0
END WITH LAYER
END IF
LOOP
Click the screen and hold an arrow key. The hero stays in the middle and the world goes by: the lamp posts quickly, the city slowly. Put another layer in between at half the speed, and it looks deeper still.
Lying down flat
A layer can be a plane: a picture I turn, zoom, and even lay down like a floor going off into the distance. The old consoles called it Mode 7.
SET LAYER 1 !! plane("affine") makes layer 1 a flat plane that turns and
zooms. SET SCROLL then says which point of the layer is in the middle of
the screen, and its !! angle turns the layer that many degrees about it.
zoom is how much of the layer fits on the screen: zoom(2) shows twice as
much, so everything looks half the size.
SCREEN 320, 240
LOADIMG "city.png", "city"
CREATE LAYER 1, 256, 256
SET LAYER 1 !! plane("affine") zoom(1) wrap_x wrap_y
PUTIMAGE (0, 0), "city"
turn = 0
DO
IF COOLDOWN(1)
turn = turn + 1
SET LAYER 1 !! plane("affine") zoom(1.5 + sin(turn / 40))
SET SCROLL (128, 128) !! angle(turn)
END IF
LOOP
With wrap_x and wrap_y, the city repeats in every direction, so there’s
never an edge to see however far out it zooms.
plane("perspective") lays the layer down as a floor. horizon is the screen
row where the floor meets the sky, and above it the layer is see-through.
height is how high the camera is over the floor: higher, and you see more
of it at once. SET SCROLL is now where the camera stands on the layer, and
angle is which way it faces.
Here’s a checkered floor, built in a MEMORYBUFFER and repeated forever, with
the city behind it on layer 0. Up drives forward, and Left and Right turn.
SCREEN 320, 240
LOADIMG "city.png", "city"
PUTIMAGE (0, 0), "city" !! scale_to((320, 240))
MEMORYBUFFER "checks", 64, 64
LINE (0, 0) -> (31, 31), 5 !! box fill
LINE (32, 32) -> (63, 63), 5 !! box fill
LINE (32, 0) -> (63, 31), 13 !! box fill
LINE (0, 32) -> (31, 63), 13 !! box fill
END MEMORYBUFFER
CREATE LAYER 1, 64, 64
SET LAYER 1 !! plane("perspective") horizon(100) height(64) wrap_x wrap_y
PUTIMAGE (0, 0), "checks"
heading = 0
cx = 0
cy = 0
DO
IF COOLDOWN(1)
IF keydown("Left") THEN heading = heading + 2
IF keydown("Right") THEN heading = heading - 2
IF keydown("Up")
cx = cx - sin(heading * pi / 180) * 3
cy = cy + cos(heading * pi / 180) * 3
END IF
SET SCROLL (cx, cy) !! angle(heading)
END IF
LOOP
angle is in degrees, and sin and cos work in radians, so pi / 180
turns one into the other. The camera faces down the layer when heading is
0, and the two lines under keydown("Up") move it a step whichever way it’s
facing.
Crash-tan’s Tip: Give
SET SCROLLitsangleevery time on a plane. Leave it out and the angle goes back to 0.plane("flat")makes the layer an ordinary one again.
Putting them away
FREE LAYER throws a layer away, drawing and all. RESET LAYERS throws
away every layer but 0, and goes back to drawing on 0: handy between levels,
when the next one wants layers of its own.
SCREEN 320, 240
CREATE LAYER 1, 320, 240
CREATE LAYER 2, 320, 240
CREATE LAYER 3, 320, 240
FREE LAYER 3
PRINT "layer 3 is gone"
RESET LAYERS
PRINT "only layer 0 is left"
layer 3 is gone
only layer 0 is left