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, orCOOLDOWN(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.