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

Frames

The screen you see is redrawn about sixty times a second. Each of those is a frame, and a game’s whole job is to have the next one ready, at the right pace.

The frame you draw

Everything you draw is sent to me as you draw it. If I showed it straight away, you’d catch the screen half drawn: cleared, with nothing on it yet. So put a frame’s drawing inside FRAME, and it reaches the screen all at once, when the block ends.

Shapes have one more step. Each CIRCLE or LINE on its own is a trip to the renderer, and a frame can have hundreds. A MEMORYBUFFER records every shape drawn inside it into one image instead, and draws it all in one go; PUTIMAGE then puts that image on the screen.

SCREEN 320, 240
angle = 0
DO
    IF COOLDOWN(1)
        x = 160 + cos(angle) * 80
        y = 120 + sin(angle) * 80
        MEMORYBUFFER "scene"
            CIRCLE (x, y), 10, 14, 14
            CIRCLE (160, 120), 4, 15, 15
        END MEMORYBUFFER
        FRAME
            CLS
            PUTIMAGE (0, 0), "scene"
        END FRAME
        angle = angle + 0.05
    END IF
LOOP

The order matters. The shapes go into the MEMORYBUFFER first, before the frame starts. Then one FRAME holds everything that changes the screen: the CLS, the PUTIMAGE, and any PRINT. The screen is never seen cleared and waiting, so nothing flickers.

A MEMORYBUFFER is the screen’s size unless you give it one, and it starts out see-through, so only the shapes show.

From 1.x: There’s no SCREENLOCK and SCREENUNLOCK to pair up. A FRAME block can’t be left unlocked.

Keeping time

That IF COOLDOWN(1) is what keeps the circle going round at a steady speed. I count time in ticks, sixty to a second, and COOLDOWN(n) is TRUE once every n ticks and FALSE in between. So the loop goes round as often as it likes, but the circle only moves sixty times a second, on a fast machine or a slow one.

Each COOLDOWN keeps its own time, so one loop can do things at different paces:

SCREEN 320, 240
x = 0
blink = FALSE
DO
    IF COOLDOWN(1) THEN x = (x + 2) MOD 320      // sixty times a second
    IF COOLDOWN(30) THEN blink = NOT blink       // twice a second
    IF COOLDOWN(1)
        MEMORYBUFFER "scene"
            CIRCLE (x, 120), 10, 14, 14
            IF blink THEN CIRCLE (160, 60), 6, 12, 12
        END MEMORYBUFFER
        FRAME
            CLS
            PUTIMAGE (0, 0), "scene"
        END FRAME
    END IF
LOOP

Try COOLDOWN(120) for the blink: once every two seconds.

Crash-tan’s Tip: Pace a loop with COOLDOWN. DELAY stops your program for a while, and a game that’s stopped can’t notice a key or a collision until it starts again.

How fast is it going?

fps is how many frames a second the screen is getting, and timer is the time in seconds, with fractions. Moving things by how much time went by keeps them at the same speed even when a frame comes late.

SCREEN 320, 240
x = 0
last = timer
DO
    IF COOLDOWN(1)
        now = timer
        x = x + 100 * (now - last)   // 100 pixels a second
        last = now
        IF x > 320 THEN x = 0
        MEMORYBUFFER "scene"
            LINE (x, 110) -> (x + 20, 130), 10 !! box fill
        END MEMORYBUFFER
        FRAME
            CLS
            PUTIMAGE (0, 0), "scene"
            LOCATE 1, 1
            PRINT "fps", round(fps)
        END FRAME
    END IF
LOOP

Drawing every frame for you

Sometimes the drawing doesn’t belong in your loop at all. CALL RENDER gives me a function to call once every frame, all by myself, and your program goes on doing whatever it likes.

SCREEN 320, 240
ATOMIC ticks = 0

FUNCTION draw()
    MEMORYBUFFER "pulse"
        CIRCLE (160, 120), 20 + (ticks MOD 60), 12
    END MEMORYBUFFER
    FRAME
        CLS
        PUTIMAGE (0, 0), "pulse"
        LOCATE 1, 1
        PRINT "tick", ticks
    END FRAME
END FUNCTION

CALL RENDER draw, "pulse"
DO
    IF COOLDOWN(1) THEN ticks = ticks + 1
LOOP

draw builds its picture and shows it in one FRAME, the same as a loop would. It runs on my render thread while your loop runs on its own, and the two share ticks. ATOMIC makes that safe: whichever side looks at it sees a whole number, never one caught half-changed.

Crash-tan’s Tip: STOP RENDER "pulse" stops calling it. Give each one a name you’ll remember, because a game can have several.