My game is vector graphics from end to end: asteroids as polylines, ship as line segments, shots as pixels, white strokes on black throughout. A pixel font from the TEXT command sat in there like a sticker on a vector painting, and it didn’t scale freely either. So I built my own typeface, also drawn as line segments, so it matches the rest of the screen.
Type as line segments
Each character is a set of line segments on a 5×7 grid. At draw time the coordinates get multiplied by a scale factor and shifted to the anchor position. Scaling is essentially free.
I store the data in DATA statements, one entry per character with the ASCII code, the line count, and the x1,y1,x2,y2 coordinates:
font_data:
DATA 32, 0 ' Space
DATA 65, 4, 0,7,0,0, 0,0,5,0, 5,0,5,7, 0,3,5,3 ' A
DATA 66, 7, 0,0,0,7, 0,0,4,0, 4,0,5,1, 5,1,4,3, 4,3,0,3, 4,3,5,5, 5,5,0,7 ' B
DATA 79, 4, 0,0,5,0, 5,0,5,7, 5,7,0,7, 0,7,0,0 ' O
DATA 48, 4, 0,0,5,0, 5,0,5,7, 5,7,0,7, 0,7,0,0 ' 0
DATA -1
At startup I read it into two arrays, ASCII code as the index:
DIM INTEGER font_lines(127)
DIM FLOAT font_coords(127, 19, 3)
SUB InitVectorFont()
LOCAL INTEGER c, nl, i
RESTORE font_data
DO WHILE 1
READ c
IF c = -1 THEN EXIT DO
READ nl
font_lines(c) = nl
FOR i = 0 TO nl - 1
READ font_coords(c, i, 0), font_coords(c, i, 1), font_coords(c, i, 2), font_coords(c, i, 3)
NEXT i
LOOP
END SUB
Drawing a character
Pull the lines out, scale by size, offset by the anchor, done:
SUB DrawVectorChar(char$, x, y, size, col)
LOCAL INTEGER ascii_val, i, num_lines
LOCAL FLOAT x1, y1, x2, y2
ascii_val = ASC(char$)
IF ascii_val < 0 OR ascii_val > 127 THEN EXIT SUB
num_lines = font_lines(ascii_val)
IF num_lines = 0 THEN EXIT SUB
FOR i = 0 TO num_lines - 1
x1 = x + font_coords(ascii_val, i, 0) * size
y1 = y + font_coords(ascii_val, i, 1) * size
x2 = x + font_coords(ascii_val, i, 2) * size
y2 = y + font_coords(ascii_val, i, 3) * size
LINE x1, y1, x2, y2, 1, col
NEXT i
END SUB
A string routine on top draws each character and leaves space between them:
SUB DrawVectorText(text$, x, y, size, col)
LOCAL INTEGER i
LOCAL FLOAT current_x
current_x = x
FOR i = 1 TO LEN(text$)
DrawVectorChar(MID$(text$, i, 1), current_x, y, size, col)
current_x = current_x + 5 * size + 2 * size
NEXT i
END SUB
With size = 1 the type is 7 pixels tall, size = 3 gets you 21 pixels, size = 5 is huge. All from a single source.
What stuck with me: DATA/READ/RESTORE in MMBasic is a real lookup-table tool, not a relic. The ASCII-indexed array replaces every IF-chain with a single read. And the scalability, the actual reason for the whole exercise, is free — multiply by size, done. The initial setup took maybe an hour, and after that I had a typeface sharing the visual language of the rest of the game and looking identical everywhere on screen.