-
Notifications
You must be signed in to change notification settings - Fork 5
Graphics and Multimedia
← Language Reference | Next: Raycast Game Engine
All graphics functions are available in the full build (Windows GDI or Linux/X11). They are absent in the console build, which targets minimal environments.
The coordinate system has its origin at the top-left corner of the working window client area, with X increasing to the right and Y increasing downward. All coordinates are in pixels.
Colors are packed RGB integers in GDI byte order (BGR stored in the low three bytes), so the
layout is &hBBGGRR:
| Color | nuBASIC constant |
|---|---|
| Red | &h0000FF |
| Green | &h00FF00 |
| Blue | &hFF0000 |
| Yellow | &h00FFFF |
| Cyan | &hFFFF00 |
| Magenta | &hFF00FF |
| White | &hFFFFFF |
| Black | &h000000 |
The Rgb(r, g, b) function computes a color from separate 0–255 components:
skyBlue% = Rgb(135, 206, 235)
sunYellow% = Rgb(255, 220, 50)Line x1, y1, x2, y2, color% ' straight line
Rect x1, y1, x2, y2, color% ' rectangle outline
FillRect x1, y1, x2, y2, color% ' filled rectangle
Ellipse x1, y1, x2, y2, color% ' ellipse outline (bounding box)
FillEllipse x1, y1, x2, y2, color% ' filled ellipseSetPixel x%, y%, color% ' write a pixel
c% = GetPixel(x%, y%) ' read a pixel's colorTextOut draws a text string at a pixel coordinate. Unlike Print, it does not move the
text cursor or scroll the buffer.
TextOut x%, y%, text$, color%
TextOut 10, 10, "Score: " + Str$(score%), &hFFFFFF
TextOut 200, 240, "GAME OVER", Rgb(255, 50, 50)Two APIs are available. Use the cached one whenever the same image is drawn more than once (sprites, tiles, animation frames): the file is decoded only on the first call and subsequent draws hit a GDI/GPU handle.
| Function | Returns | Description |
|---|---|---|
BitmapLoad(path$) |
Integer | Load a BMP/PNG/JPEG and return an integer handle (0 on failure) |
BitmapDraw(id, x, y) |
Integer | Draw a cached bitmap at the given top-left pixel |
BitmapFree(id) |
Integer | Release the cached bitmap |
Dim sprite As Integer
sprite = BitmapLoad("hero.bmp")
For frame% = 1 To 240
Cls
BitmapDraw sprite, hero_x%, hero_y%
Refresh
Next frame%
BitmapFree spriteBacked by GDI+ on Windows and stb_image on Linux (R/B channels are pre-swapped at load time so the per-frame cost matches Windows).
PlotImage reloads the file from disk on every call. Fine for one-off
splashes, wasteful inside animation loops.
PlotImage "background.bmp", 0, 0
PlotImage "sprite.bmp", hero_x%, hero_y%Cls
MoveWindow GetWindowX(), GetWindowY(), 640, 480
FillRect 0, 0, 640, 480, Rgb(30, 30, 60) ' dark night sky
FillEllipse 520, 20, 600, 100, Rgb(255, 255, 200) ' moon
FillRect 0, 360, 640, 480, Rgb(0, 80, 0) ' green ground
For i% = 0 To 5
cx% = 80 + i% * 100
FillEllipse cx%, 200, cx%+60, 360, Rgb(20, 120, 20) ' trees
FillRect cx%+25, 330, cx%+35, 365, Rgb(80, 50, 20) ' trunks
Next i%
TextOut 10, 10, "nuBASIC Graphics Demo", &hFFFFFFThe Screen statement switches the console/graphics mode at runtime,
similar to GW-BASIC's SCREEN command.
| Mode | Instruction | Behaviour |
|---|---|---|
| 0 | Screen 0 |
Text / hybrid mode — text I/O via the real Windows console (stdout/stdin); all GDI drawing commands are silent no-ops. No GDI window is required. Ideal for scripts, CI pipelines and regression tests. |
| 1 | Screen 1 |
GDI console mode (default) — text I/O and graphics go through the custom GDI window, as in previous nuBASIC versions. |
The CLI flag -t / --text-mode pre-sets Screen 0 before the
interpreter starts, so programs that never call Screen 1 can run
fully headless:
nubasic.exe -t -e tests/test_math.bas
Example — switching modes at runtime:
Screen 0 ' text mode: Print/Input use the real console
Print "Hello from text mode"
Screen 1 ' back to GDI: drawing commands are active again
Line 0, 0, 200, 200, RGB(255, 0, 0)Tip for test files: add
Screen 0as the very first statement so the test runner can capture
When multiple drawing commands execute in sequence, each one immediately blits to the screen. The user sees each intermediate state — a cleared frame, a partial board, and so on — as visible flicker.
| Instruction | Effect |
|---|---|
ScreenLock |
Suspend automatic screen refresh; all drawing goes to the back buffer |
ScreenUnlock |
Present the back buffer to the screen in one blit; resume automatic refresh |
Refresh |
Force an immediate blit; lock state is unchanged |
While Not(game_over%)
ScreenLock
FillRect 0, 0, 640, 480, &h000000 ' clear previous frame
DrawBoard
DrawPlayer player_x%, player_y%
DrawEnemies
DrawHUD score%, lives%
ScreenUnlock
MDelay 16 ' pace to ~60 fps
WendFor x0 = -2 To 2 Step 0.013
ScreenLock
For y0 = -1.5 To 1.5 Step 0.013
' ... compute c ...
FillRect x0*d%+dx%, y0*d%+dy%, x0*d%+dx%+2, y0*d%+dy%+2, c%*16
Next y0
ScreenUnlock ' one column appears per iteration
Next x0x_old% = x% : y_old% = y%
x% = x% + dx% : y% = y% + dy%
ScreenLock
FillEllipse x_old%*10, y_old%*10, x_old%*10+10, y_old%*10+10, 0 ' erase old
FillEllipse x%*10, y%*10, x%*10+10, y%*10+10, &hffffff ' draw new
ScreenUnlockScreenLock
FillRect 150, 220, 490, 310, &hffff00
Rect 150, 220, 490, 310, &h000000
TextOut 180, 245, "Game over! Play again? (Y/N)", &h000000
ScreenUnlock
Refresh ' ensure dialog is visible before blocking
key$ = Input$(1)Mouse input is polled — there is no event queue.
GetMouse() captures the full pointer state in a single call and returns a Mouse struct:
Dim m As Mouse
m = GetMouse()
' m.x — cursor X in pixels from left edge
' m.y — cursor Y in pixels from top edge
' m.btn — 0=none, 1=left, 2=middle, 4=rightbtn% = GetMouseBtn() ' use GetMouse().btn instead
x% = GetMouseX() ' use GetMouse().x instead
y% = GetMouseY() ' use GetMouse().y insteadbx1% = 40 : by1% = 60
bx2% = 200 : by2% = 100
FillRect bx1%, by1%, bx2%, by2%, &hffff00
TextOut bx1%+20, by1%+15, "Click me", &h000000
Dim m As Mouse
While 1
m = GetMouse()
If m.btn = 1 And m.x >= bx1% And m.x <= bx2% And m.y >= by1% And m.y <= by2% Then
Print "Button clicked!"
MDelay 200
End If
MDelay 16
WendCls
FillRect 0, 0, 640, 480, &h000000
Dim m As Mouse
While 1
key$ = InKey$()
If key$ = "q" Or key$ = "Q" Then Exit While
m = GetMouse()
If m.btn = 1 Then
SetPixel m.x, m.y, &hffffff
End If
MDelay 5
WendMoveWindow GetWindowX(), GetWindowY(), 800, 600
Print "Position: "; GetWindowX(); ", "; GetWindowY()
Print "Size: "; GetWindowDx(); " x "; GetWindowDy()
Print "Canvas: "; GetSWidth(); " x "; GetSHeight()
SetTopMost() ' keep window above all othersTypical startup sequence:
Cls
MoveWindow 100, 100, 640, 480
FillRect 0, 0, 640, 480, &h000000PlaySound "background.wav", 1 ' async: returns immediately
PlaySound "explosion.wav", 0 ' sync: waits for completion
Beep ' system beepresult% = MsgBox("nuBASIC Demo", "Setup complete. Ready to play?")
If result% > 0 Then
Print "User confirmed."
End IfOn Windows, nuBASIC 2.0 ships the integrated WinRayCast engine for rendering
Wolfenstein 3D-style first-person scenes from BASIC. It builds on the GDI window and the
flicker-free rendering primitives above. See Raycast Game Engine
for the render loop and the full Ray… API reference.
← Language Reference | Next: Raycast Game Engine