Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Pictures from files

Drawing with circles and lines takes you a long way, but sooner or later you’ll want a picture somebody painted: a hero, a skyline, a big orange explosion. Every game in this book has a few of those in its folder, and I’ll load them by name.

Loading a picture

LOADIMG takes a file from the game’s folder and gives it a name. After that, the name is all I need. imagewidth and imageheight say how big it is.

SCREEN 320, 240
LOADIMG "hero.png", "tan"
LOADIMG "city.png", "city"
PRINT "the hero:", imagewidth("tan"), "by", imageheight("tan")
PRINT "the city:", imagewidth("city"), "by", imageheight("city")
PUTIMAGE (0, 0), "city"
PUTIMAGE (200, 150), "tan"
the hero: 40 by 40
the city: 256 by 256

That’s the hero in the corner, in front of my city. PUTIMAGE stamps a picture onto the layer with its top-left corner where you say, and the parts of the file that are see-through stay see-through.

Putting it down your way

PUTIMAGE takes modifiers after !!. scale stretches it, scale_to makes it an exact size in pixels, flip_x mirrors it, and alpha makes it partly see-through, from 0 (gone) to 1 (solid).

SCREEN 320, 240
LOADIMG "hero.png", "tan"
LOADIMG "city.png", "city"
PUTIMAGE (0, 0), "city" !! scale_to((320, 240))
PUTIMAGE (10, 160), "tan"
PUTIMAGE (80, 160), "tan" !! flip_x
PUTIMAGE (150, 160), "tan" !! alpha(0.4)
PUTIMAGE (210, 40), "tan" !! scale((2, 2))

The city is 256 pixels square and the screen isn’t, so scale_to((320, 240)) stretches it to fill the screen.

rotate turns a picture by a number of degrees, about its middle. origin says which point of the picture goes where you put it, from (0, 0), its top left, to (1, 1), its bottom right. With origin((0.5, 0.5)), the place you give is the picture’s middle, so a picture that spins stays put:

SCREEN 320, 240
LOADIMG "hero.png", "tan"
LOADIMG "city.png", "city"
angle = 0
DO
    IF COOLDOWN(1)
        fade = 0.6 + sin(angle / 20) * 0.4
        FRAME
            CLS
            PUTIMAGE (0, 0), "city" !! scale_to((320, 240))
            PUTIMAGE (160, 120), "tan" !! origin((0.5, 0.5)) rotate(angle) alpha(fade)
        END FRAME
        angle = angle + 3
    END IF
LOOP

There are no shapes here, so there’s no MEMORYBUFFER: the pictures go straight into the FRAME.

A sheet of frames

explosion.png is one picture with sixteen little pictures in it, four rows of four, each 32 pixels square. !! spritesheet((32, 32)) loads it cut into frames, numbered from 0 at the top left, across and then down. PUTIMAGE takes the frame to show after the name.

SCREEN 320, 240
LOADIMG "explosion.png", "boom" !! spritesheet((32, 32))
PRINT "a frame is", imagewidth("boom"), "by", imageheight("boom")
FOR f = 0 ... 15
    PUTIMAGE (8 + f * 19, 104), "boom", f
NEXT
a frame is 32 by 32

Once a picture is a sheet, imagewidth and imageheight give the size of one frame. All sixteen in a row are the whole bang, from the first spark to the last wisp of smoke.

A sprite shows a sheet the same way, with frame. Here are four explosions, each a few frames ahead of the next. When one is done, it moves somewhere else and goes off again.

SCREEN 320, 240
RANDOMIZE
LOADIMG "city.png", "city"
LOADIMG "explosion.png", "boom" !! spritesheet((32, 32))
PUTIMAGE (0, 0), "city" !! scale_to((320, 240))

frames = [0, 4, 8, 12]
FOR n = 0 ... 3
    ENABLE SPRITE n
    SET SPRITE n !! image("boom") frame(frames(n)) x(rnd * 256) y(rnd * 176)
        !! scale((2, 2))
    SHOW SPRITE n
NEXT

DO
    IF COOLDOWN(3)
        FOR n = 0 ... 3
            frames(n) = frames(n) + 1
            IF frames(n) = 16
                frames(n) = 0
                SET SPRITE n !! x(rnd * 256) y(rnd * 176)
            END IF
            SET SPRITE n !! frame(frames(n))
        NEXT
    END IF
LOOP

COOLDOWN(3) moves each one on a frame every three ticks, so a whole explosion takes 48 ticks: a little under a second.

Crash-tan’s Tip: Try COOLDOWN(1) for a quick pop, or COOLDOWN(6) for a slow, lazy cloud.

Cutting out a piece

GETIMAGE copies part of the layer into a new picture. It takes two corners with -> between them. The second corner is just past the piece, so (96, 0) -> (160, 128) is 64 pixels across and 128 down.

SCREEN 320, 240
LOADIMG "city.png", "city"
PUTIMAGE (0, 0), "city"
GETIMAGE (96, 0) -> (160, 128), "tower"
PRINT "the tower is", imagewidth("tower"), "by", imageheight("tower")
CLS
PUTIMAGE (96, 0), "tower" !! scale((2, 1.75))
the tower is 64 by 128

I drew the whole city, cut the middle out, cleared the screen and put the piece back, bigger. It’s handy for a snapshot of the level behind a pause menu, or for keeping a picture you built up from lots of others.

Pixel by pixel

For something tiny, you can skip the file and write the pixels yourself. ASIMAGE takes rows of palette colors, with -1 for see-through, then a name and the picture’s width and height.

SCREEN 320, 240
heart = [[-1, 4, -1, 4, -1], [4, 12, 4, 12, 4], [4, 12, 12, 12, 4], [-1, 4, 12, 4, -1], [-1, -1, 4, -1, -1]]
ASIMAGE heart, "heart", 5, 5
FOR n = 0 ... 2
    PUTIMAGE (8 + n * 48, 8), "heart" !! scale((8, 8))
NEXT
PRINT "three lives"
three lives

Tidying up

A picture stays loaded until you free it. FREE IMAGE lets it go, which is worth doing with big pictures once a level is over. exists says whether something is loaded under a name.

SCREEN 320, 240
LOADIMG "city.png", "level one"
PRINT "loaded?", exists("level one")
FREE IMAGE "level one"
PRINT "still loaded?", exists("level one"), " width now:", imagewidth("level one")
loaded? 1
still loaded? 0  width now: 0

Once it’s gone, the name is free for the next level’s picture.