Images and sprites
Drawing everything again every frame works, but most of a game is things that move: the hero, the bad guys, the bullets. Those are sprites. You tell me where a sprite is and what it looks like, and I draw it every frame, on top of the layers, without your loop lifting a finger.
An image to show
A sprite shows an image. A game usually loads its images from files, with
LOADIMG "hero.png", "hero". Here we’ll draw one. Everything drawn inside a
MEMORYBUFFER goes into an image of its own instead of onto the screen: give
it a name, and how big it is.
SCREEN 320, 240
MEMORYBUFFER "ball", 16, 16
CIRCLE (8, 8), 7, 14, 14
CIRCLE (5, 5), 2, 15, 15
END MEMORYBUFFER
PUTIMAGE (40, 40), "ball"
PUTIMAGE (80, 40), "ball" !! scale((3, 3))
PRINT "a ball,", imagewidth("ball"), "pixels across"
a ball, 16 pixels across
PUTIMAGE stamps an image onto the layer, once. A sprite is the image that
stays.
A sprite
A sprite has a number. ENABLE SPRITE gets it ready, SET SPRITE says
everything about it after !!, and SHOW SPRITE puts it on the screen.
SCREEN 320, 240
MEMORYBUFFER "ball", 16, 16
CIRCLE (8, 8), 7, 14, 14
END MEMORYBUFFER
ENABLE SPRITE 0
SET SPRITE 0 !! image("ball") x(160) y(120) origin((0.5, 0.5)) scale((2, 2))
SHOW SPRITE 0
x = 160
DO
IF COOLDOWN(1)
IF keydown("Left") THEN x = x - 2
IF keydown("Right") THEN x = x + 2
SET SPRITE 0 !! x(x)
END IF
LOOP
There’s no CLS and no drawing in the loop: the loop only says where the
sprite is now. Click the screen and use the arrow keys.
origin((0.5, 0.5)) says that the sprite’s place is its middle. Without it,
x and y are its top-left corner.
Crash-tan’s Tip: A sprite needs an image before it can show anything. After
ENABLE SPRITEit’s already at (0, 0), soimagealone will do, andxandycan follow. One that was never enabled has no place yet: give itx,yandimagetogether. After that,SET SPRITEchanges only what you name:x(20)moves it across and leaves its height, its image and the rest as they were.MOVE SPRITE 0, (x, y)moves it both ways at once.
When there’s a lot to say about a sprite, say it over a few lines. A line
that starts with !! carries on the one above it:
SET SPRITE 0 !! image("ball") x(160) y(120)
!! origin((0.5, 0.5)) scale((2, 2))
!! behavior("bounce")
Where a sprite says it is
spritex(n) and spritey(n) say where a sprite’s top-left corner is,
whatever its origin. A second number picks another point across it: 0.5 is
the middle, 1 the far side.
SCREEN 320, 240
NEWIMAGE 32, 32, 14, "box"
ENABLE SPRITE 0
SET SPRITE 0 !! image("box") x(160) y(120) origin((0.5, 0.5))
SHOW SPRITE 0
PRINT spritex(0), spritex(0, 0.5), spritex(0, 1)
144 160 176
So to move a sprite from where it is, read it back at its origin:
SET SPRITE 0 !! x(spritex(0, 0.5) + 1)
Crash-tan’s Tip: With
origin((0.5, 0.5)),x(spritex(0) + 1)doesn’t move it one pixel right. It moves it half its width left, every time!
Many sprites, one image
A sprite sheet is one image cut into frames, and a sprite shows one frame of
it at a time. A file loads as one with !! spritesheet and the size of a
frame. TOSPRITESHEET makes one from images you’ve drawn, a frame each, in
order.
SCREEN 320, 240
MEMORYBUFFER "red", 16, 16
CIRCLE (8, 8), 7, 4, 4
END MEMORYBUFFER
MEMORYBUFFER "green", 16, 16
CIRCLE (8, 8), 7, 10, 10
END MEMORYBUFFER
TOSPRITESHEET "lights", ["red", "green"]
FOR n = 0 ... 9
ENABLE SPRITE n
SET SPRITE n !! image("lights") frame(n MOD 2) x(20 + n * 30) y(120)
SHOW SPRITE n
NEXT
DO
IF COOLDOWN(12)
FOR n = 0 ... 9
SET SPRITE n !! frame(int(rnd * 2))
NEXT
END IF
LOOP
Frame 0 is the red light and frame 1 the green one. Changing a sprite’s frame is how a character walks: the frames of the walk, one after another.
Finding a sprite a number
Numbering sprites yourself is fine for a hero and a few bad guys. For bullets
and sparks that come and go, reserve_sprite() claims a number nobody is
using and gives it to you. When you’re done with one, DISABLE SPRITE stops it
drawing and colliding, and lets its number go.
SCREEN 320, 240
first = reserve_sprite()
second = reserve_sprite()
PRINT "got", first, "and", second
DISABLE SPRITE first
PRINT "is", first, "still in use?", spriteenabled(first)
got 0 and 1
is 0 still in use? 0
Moving lots of sprites from your loop works, but it isn’t how I like it best. I can move them myself, every tick, from a description of how they move. That’s a behavior, and it’s next.