Lesson 13 of 16

Graphics & Games

terminal + pixel graphics, input, sound

download this lesson (.md) ↓ all lessons

13. Graphics & Games

STencil has two graphics layers: terminal (character cells) and pixel window. Plus sound and timing.

Terminal graphics

game_init();                     // enter game screen (alt screen, raw input)
let x = 0;
let f = 0;
while (f < 60) {
    clear();
    draw(0, 0, "Frame " + f);          // draw(col, row, text)  — 0-based
    draw(x, 2, color("*", "green"));   // colored text
    x = x + 1;
    if (x > 20) { x = 0; }
    let k = key();                     // non-blocking: "up"/"down"/.../"q" or ""
    if (k == "q") { f = 60; }
    sleep(50);                         // milliseconds (~20 FPS)
    f = f + 1;
}
game_end();                      // restore the terminal

Helpers: clear(), draw(x,y,text), color(text, name), sleep(ms), ticks() (ms since start), screen_size()[w, h], key() (non-blocking key). A full Snake game ships in examples/snake.st.

Pixel window graphics

Open a real window and draw pixels/rectangles:

let ok = window_open(400, 300, "My Game");
let x = 180;
let y = 130;
while (window_update()) {                  // false when the window is closed
    if (window_key("left"))  { x = x - 5; }
    if (window_key("right")) { x = x + 5; }
    if (window_key("up"))    { y = y - 5; }
    if (window_key("down"))  { y = y + 5; }
    if (window_key("esc"))   { window_close(); }

    fill("black");                         // clear the frame
    rect(x, y, 20, 20, "green");           // rect(x, y, w, h, color)
    pixel(200, 150, "red");                // single pixel
}
print("done");
  • Color is a name (red,green,blue,white,black,yellow,cyan,magenta,gray,orange) or an int 0xRRGGBB.
  • window_key(name) is true while the key is held.
  • A full game ships in examples/pixel_oyun.st.

Mouse — make clickable buttons

let m = window_mouse();          // [x, y]
let mx = m[0];
let my = m[1];
let clicked = window_mouse_down();   // left button held?

// a button is just a rect + a hit test
func inside(px, py, bx, by, bw, bh) {
    return px >= bx && px <= bx + bw && py >= by && py <= by + bh;
}
if (clicked && inside(mx, my, 50, 50, 100, 30)) {
    print("Button clicked!");
}

Sound

beep(440, 200);   // frequency (Hz), duration (ms)
beep(880, 120);

Game loop pattern

  1. window_open(...) (or game_init() for terminal)
  2. loop while open: read input → update state → draw everything → (terminal: sleep)
  3. window_close() (or game_end())

Next: Database