Demos — complete games
Cavern Dash - an OpenDBC example for the DarkBASIC Classic profile dbcExample
examples/dbc/demos/cavern_dash/cavern_dash.dba · 144 lines (109 code)
Fly through an endless cave carved by two scrolling matrices. Arrow keys steer and climb, SPACE boosts, ESCAPE quits. Code and media are part of OpenDBC (MIT licence).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 3 GOSUB subroutine(s):
new_tunnel, carve_row, crash. - Assets. Loads 3 media file(s) from the example folder:
textures/tiles.jpg, sounds/engine.wav, sounds/crash.wav.
Commands used (45)
View source (144 lines)
rem ==================================================================
rem Cavern Dash - an OpenDBC example for the DarkBASIC Classic profile
rem Fly through an endless cave carved by two scrolling matrices.
rem Arrow keys steer and climb, SPACE boosts, ESCAPE quits.
rem Code and media are part of OpenDBC (MIT licence).
rem ==================================================================
sync on : sync rate 60
hide mouse
randomize 7
autocam off
backdrop on
color backdrop rgb(10, 8, 16)
set camera range 1, 900
rem --- Tunnel layout: a floor and a ceiling matrix of 10 x 20 tiles
tiles = 10 : rows = 20 : tile# = 40.0
width# = tiles * tile# : depth# = rows * tile#
ceiling# = 150.0
rem --- Texture atlas: four 1024 pixel tiles in one 2048 x 2048 image
load bitmap "textures/tiles.jpg", 1
get image 1, 0, 0, 2048, 2048
delete bitmap 1
make matrix 1, width#, depth#, tiles, rows
make matrix 2, width#, depth#, tiles, rows
prepare matrix texture 1, 1, 2, 2
prepare matrix texture 2, 1, 2, 2
rem The ceiling is seen from below, so it must not be back-face culled
set matrix 2, 0, 0, 0, 1, 1, 1, 1
gosub new_tunnel
rem --- Fog hides the far edge of the matrices
if fog available() = 1
fog on
fog color rgb(10, 8, 16)
fog distance 700
endif
load sound "sounds/engine.wav", 1
load sound "sounds/crash.wav", 2
loop sound 1
ink rgb(255, 220, 160), 0
best = 0
do
rem --- Steering, climbing and boost
steer# = 0.0
if leftkey() = 1 then steer# = -1.0
if rightkey() = 1 then steer# = 1.0
if upkey() = 1 then y# = y# + 1.5
if downkey() = 1 then y# = y# - 1.5
x# = x# + steer# * 2.5
boost# = 0.0
if spacekey() = 1 then boost# = 2.5
speed# = curvevalue(3.0 + distance / 400.0 + boost#, speed#, 20)
roll# = curvevalue(steer# * -20.0, roll#, 8)
rem --- Scroll the cave towards the camera one tile row at a time
scroll# = scroll# + speed#
if scroll# >= tile#
scroll# = scroll# - tile#
shift matrix up 1
shift matrix up 2
row = rows
gosub carve_row
update matrix 1
update matrix 2
distance = distance + 1
endif
position matrix 1, 0, 0, 0 - scroll#
position matrix 2, 0, ceiling#, 0 - scroll#
rem --- Collision against the floor, the ceiling and the side walls
local_z# = z# + scroll#
ground# = get ground height(1, x#, local_z#)
roof# = ceiling# + get ground height(2, x#, local_z#)
if y# < ground# + 6 or y# > roof# - 6 then gosub crash
position camera x#, y#, z#
xrotate camera 8
zrotate camera roll#
set sound speed 1, 8000 + speed# * 1200
center text screen width() / 2, 12, "DISTANCE " + str$(distance) + " BEST " + str$(best)
sync
loop
rem ------------------------------------------------------------------
rem Rebuild the whole tunnel and put the ship back at its entrance
new_tunnel:
pathx = tiles / 2
for row = 0 to rows
gosub carve_row
next row
update matrix 1
update matrix 2
x# = (tiles / 2) * tile# : y# = 50.0 : z# = 60.0
scroll# = 0.0 : speed# = 2.0 : roll# = 0.0 : distance = 0
return
rem ------------------------------------------------------------------
rem Carve one row of vertices around a path that wanders sideways
carve_row:
rem The first rows stay straight so a new run starts in open space
if row > 3 then pathx = pathx + rnd(2) - 1
if pathx < 3 then pathx = 3
if pathx > tiles - 3 then pathx = tiles - 3
tile_row = row
if tile_row > rows - 1 then tile_row = rows - 1
for col = 0 to tiles
gap = abs(col - pathx)
if gap > 2
floor_height# = 60 + rnd(40)
roof_height# = 0 - 60 - rnd(40)
tile = 3 + rnd(1)
else
floor_height# = rnd(12)
roof_height# = 0 - rnd(12)
tile = 1 + rnd(1)
endif
set matrix height 1, col, row, floor_height#
set matrix height 2, col, row, roof_height#
if col < tiles
set matrix tile 1, col, tile_row, tile
set matrix tile 2, col, tile_row, tile
endif
next col
return
rem ------------------------------------------------------------------
rem Flash the cave red, then restart
crash:
play sound 2
for flash = 1 to 20
color backdrop rgb(255 - flash * 12, 40, 20)
sync
next flash
color backdrop rgb(10, 8, 16)
if distance > best then best = distance
gosub new_tunnel
return
Dune Armour - an OpenDBC example for the DarkBASIC Classic profile dbcExample
examples/dbc/demos/dune_armour/dune_armour.dba · 361 lines (310 code)
Command a tank across the dunes and hold the outpost against a patrol of six-wheeled raiders. The radar in the corner shows them. UP/DOWN drive, LEFT/RIGHT steer, Z/X turn the turret, SPACE fires. Without the keys the tank drives and fights by itself. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 user function(s):
ground_at; 16 GOSUB subroutine(s): build_desert, build_outpost, build_acacias, build_tank, build_raiders, spawn_raider, build_effects, drive_tank (+8 more). - Data. 4 array(s) declared with
DIM. - Assets. Loads 14 media file(s) from the example folder:
sounds/rumble.wav, sounds/cannon.wav, sounds/blast.wav, sounds/ping.wav, sounds/raider_gun.wav, textures/desert.jpg (+8 more).
Commands used (73)
View source (361 lines)
rem ==================================================================
rem Dune Armour - an OpenDBC example for the DarkBASIC Classic profile
rem Command a tank across the dunes and hold the outpost against a
rem patrol of six-wheeled raiders. The radar in the corner shows them.
rem UP/DOWN drive, LEFT/RIGHT steer, Z/X turn the turret, SPACE fires.
rem Without the keys the tank drives and fights by itself.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
hide mouse
autocam off
randomize timer()
backdrop on
sky = rgb(200, 190, 160)
color backdrop sky
set camera range 1, 2500
set ambient light 55
fog_ok = fog available()
if fog_ok = 1 then fog on : fog color sky : fog distance 900
rem Limbs of the tank and of the raiders (from their .x files)
track_l = 2 : track_r = 3 : turret = 4 : dish = 7
wheel_first = 2 : wheel_last = 7 : gun_ring = 9
gosub build_desert
gosub build_outpost
gosub build_acacias
gosub build_tank
gosub build_raiders
gosub build_effects
load sound "sounds/rumble.wav", 1
load sound "sounds/cannon.wav", 2
load sound "sounds/blast.wav", 3
load sound "sounds/ping.wav", 4
load sound "sounds/raider_gun.wav", 5
loop sound 1
do
First 40 lines shown — 321 more in examples/dbc/demos/dune_armour/dune_armour.dba.
Flooded Vault - an OpenDBC example for the DarkBASIC Classic profile dbcExample
examples/dbc/demos/flooded_vault/flooded_vault.dba · 158 lines (116 code)
Walk a mechanical crab through a flooded cistern and catch the drops that fall from the vault before they hit the water. Arrow keys walk and turn, ESCAPE quits. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 2 GOSUB subroutine(s):
new_drop, new_ripple. - Data. 1 array(s) declared with
DIM. - Assets. Loads 8 media file(s) from the example folder:
models/room.x, textures/water.jpg, models/crab_idle.x, models/crab_walk.x, textures/ripple.jpg, sounds/drip.wav (+2 more).
Commands used (58)
View source (158 lines)
rem ==================================================================
rem Flooded Vault - an OpenDBC example for the DarkBASIC Classic profile
rem Walk a mechanical crab through a flooded cistern and catch the drops
rem that fall from the vault before they hit the water.
rem Arrow keys walk and turn, ESCAPE quits.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
hide mouse
autocam off
randomize timer()
backdrop on
color backdrop rgb(4, 10, 12)
set camera range 1, 600
set ambient light 45
if fog available() = 1
fog on
fog color rgb(4, 10, 12)
fog distance 260
endif
rem --- The cistern: one object whose limbs carry their own textures
load object "models/room.x", 1
perform checklist for object limbs 1
limbs = checklist quantity()
rem --- Water: an alpha-blended plain whose texture slowly drifts
load image "textures/water.jpg", 1
make object plain 2, 62, 62
texture object 2, 1
scale object texture 2, 3, 3
xrotate object 2, 90
position object 2, 0, 1.2, 0
ghost object on 2, 1
fade object 2, 60
rem --- The crab: idle frames 0 to 20, walk cycle appended at frame 100
load object "models/crab_idle.x", 3
First 40 lines shown — 118 more in examples/dbc/demos/flooded_vault/flooded_vault.dba.
Frost Outpost - an OpenDBC example for the DarkBASIC Classic profile dbcExample
examples/dbc/demos/frost_outpost/frost_outpost.dba · 324 lines (275 code)
Hold the snowy outpost against ice imps. Distant imps are cheap camera-facing impostors; close ones are animated 3D models. Mouse looks, W A S D or the arrow keys move, the mouse button fires. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 9 GOSUB subroutine(s):
build_terrain, build_outpost, build_sky, build_weapon, build_hud, build_imps, reset_imps, spawn_imp (+1 more). - Data. 1 array(s) declared with
DIM. - Assets. Loads 20 media file(s) from the example folder:
sounds/pulse.wav, sounds/impact.wav, sounds/shatter.wav, sounds/punch.wav, sounds/wind.wav, sounds/howl.wav (+14 more).
Commands used (80)
View source (324 lines)
rem ==================================================================
rem Frost Outpost - an OpenDBC example for the DarkBASIC Classic profile
rem Hold the snowy outpost against ice imps. Distant imps are cheap
rem camera-facing impostors; close ones are animated 3D models.
rem Mouse looks, W A S D or the arrow keys move, the mouse button fires.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
hide mouse
autocam off
randomize timer()
backdrop on
sky_colour = rgb(150, 170, 195)
color backdrop sky_colour
set camera range 1, 1600
set ambient light 60
if fog available() = 1
fog on
fog color rgb(170, 185, 205)
fog distance 700
endif
rem Clip ranges inside every imp object (each clip is appended after the idle one)
idle_start = 0 : idle_end = 20
walk_start = 30 : walk_end = 54
charge_start = 60 : charge_end = 76
punch_start = 80 : punch_end = 104
diea_start = 110 : diea_end = 140
dieb_start = 150 : dieb_end = 180
gosub build_terrain
gosub build_outpost
gosub build_sky
gosub build_weapon
gosub build_hud
gosub build_imps
load sound "sounds/pulse.wav", 1
load sound "sounds/impact.wav", 2
First 40 lines shown — 284 more in examples/dbc/demos/frost_outpost/frost_outpost.dba.
Hive Defender - an OpenDBC example for the DarkBASIC Classic profile dbcExample
examples/dbc/demos/hive_defender/hive_defender.dba · 305 lines (260 code)
Hold the line against a swarm of hive drones. They sway in formation, then break off one by one to dive at your ship; now and then their mothership crosses the sky for a bonus. LEFT/RIGHT move, SPACE fires. Without the keys the ship fights alone. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 13 GOSUB subroutine(s):
build_scene, new_wave, move_player, move_formation, launch_dive, update_drone, drone_fires, move_bolts (+5 more). - Data. 4 array(s) declared with
DIM. - Assets. Loads 13 media file(s) from the example folder:
textures/nebula.jpg, models/ship.x, models/mothership.x, textures/drone_amber.jpg, textures/drone_violet.jpg, textures/drone_green.jpg (+7 more).
Commands used (49)
View source (305 lines)
rem ==================================================================
rem Hive Defender - an OpenDBC example for the DarkBASIC Classic profile
rem Hold the line against a swarm of hive drones. They sway in formation,
rem then break off one by one to dive at your ship; now and then their
rem mothership crosses the sky for a bonus.
rem LEFT/RIGHT move, SPACE fires. Without the keys the ship fights alone.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
hide mouse
autocam off
randomize timer()
color backdrop 0
set ambient light 70
position camera 0, -12, -115
point camera 0, 4, 0
cols = 8 : rows = 4 : drones = cols * rows
dim slotx(drones) : dim sloty(drones) : dim state(drones) : dim dx#(drones) : dim dy#(drones)
dim divet#(drones) : dim divedir(drones)
dim boltx#(8) : dim bolty#(8) : dim boltlife(8)
dim boom(8)
gosub build_scene
gosub new_wave
lives = 3 : score = 0 : wave = 1
px# = 0.0 : shot = 0 : invulnerable = 120
do
t = t + 1
gosub move_player
gosub move_formation
gosub launch_dive
for d = 1 to drones
if state(d) > 0 then gosub update_drone
next d
gosub move_bolts
gosub move_mothership
gosub update_explosions
First 40 lines shown — 265 more in examples/dbc/demos/hive_defender/hive_defender.dba.
Wave Racer - an OpenDBC example for the DarkBASIC Classic profile dbcExample
examples/dbc/demos/wave_racer/wave_racer.dba · 262 lines (211 code)
Race a jet scooter through eight buoy gates on a rolling sea, two laps against the clock. Hit the ramp for a jump. Up accelerates, Down brakes, Left and Right steer, SPACE restarts. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 2 user function(s):
smaller#, sea_height; 6 GOSUB subroutine(s): new_race, light_gates, animate_sea, ramp_check, update_spray, draw_hud. - Data. 2 array(s) declared with
DIM. - Assets. Loads 12 media file(s) from the example folder:
textures/sea.jpg, textures/sky.jpg, models/scooter.x, models/rider.x, models/buoy.x, models/ramp.x (+6 more).
Commands used (73)
View source (262 lines)
rem ==================================================================
rem Wave Racer - an OpenDBC example for the DarkBASIC Classic profile
rem Race a jet scooter through eight buoy gates on a rolling sea, two
rem laps against the clock. Hit the ramp for a jump.
rem Up accelerates, Down brakes, Left and Right steer, SPACE restarts.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
hide mouse
autocam off
backdrop on
color backdrop rgb(204, 219, 235)
set camera range 1, 4000
set ambient light 70
fog_ok = fog available()
if fog_ok = 1
fog on
fog color rgb(204, 219, 235)
fog distance 900
endif
rem --- Sea: a matrix whose heights are recomputed every frame
segments = 30 : sea_size# = 2400.0 : half# = sea_size# / 2
load image "textures/sea.jpg", 1
make matrix 1, sea_size#, sea_size#, segments, segments
prepare matrix texture 1, 1, 1, 1
position matrix 1, 0 - half#, 0, 0 - half#
rem --- Sky dome that follows the camera; it is seen from inside
load image "textures/sky.jpg", 2
make object sphere 3, 3000
texture object 3, 2
set object 3, 1, 1, 0, 1, 0, 0, 1
rem --- Scooter and rider (limb 1 of the scooter is the hull, limb 5 the flag);
rem the glued rider inherits the scooter scale
load object "models/scooter.x", 10
load object "models/rider.x", 11
scale object 10, 300, 300, 300
First 40 lines shown — 222 more in examples/dbc/demos/wave_racer/wave_racer.dba.
Tutorials — 2D drawing
Colour Picker - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/2d/colour_picker.dba · 74 lines (54 code)
Paints a palette with RGB colours, then reads the pixel under the mouse back with POINT and splits it with RGBR, RGBG and RGBB. Move the mouse over the palette; without a mouse a probe wanders. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 user function(s):
palette_colour.
Commands used (19)
View source (74 lines)
rem ==================================================================
rem Colour Picker - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Paints a palette with RGB colours, then reads the pixel under the
rem mouse back with POINT and splits it with RGBR, RGBG and RGBB.
rem Move the mouse over the palette; without a mouse a probe wanders.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
w = screen width() : h = screen height()
palette_h = h - 120
rem The palette: hue changes across, pale at the top and dark at the bottom
for y = 0 to palette_h - 1 step 6
for x = 0 to w - 1 step 6
ink palette_colour(x * 360.0 / w, y * 1.0 / palette_h), 0
box x, y, x + 5, y + 5
next x
next y
do
mx = mousex() : my = mousey()
rem No mouse movement yet: let a probe trace a slow loop over the palette
if mx = 0 and my = 0
t# = t# + 0.6
mx = w / 2 + sin(t#) * (w / 2 - 20)
my = palette_h / 2 + cos(t# * 1.7) * (palette_h / 2 - 10)
endif
if my > palette_h - 1 then my = palette_h - 1
rem POINT reads the screen, so read before drawing anything new
c = point(mx, my)
red = rgbr(c) : green = rgbg(c) : blue = rgbb(c)
rem The panel under the palette is redrawn every frame
ink rgb(24, 24, 30), 0
box 0, palette_h, w - 1, h - 1
ink c, 0
box 16, palette_h + 14, 116, h - 16
ink rgb(230, 230, 230), 0
text 136, palette_h + 10, "POINT(" + str$(mx) + "," + str$(my) + ")"
text 136, palette_h + 34, "RGBR " + str$(red)
text 136, palette_h + 56, "RGBG " + str$(green)
text 136, palette_h + 78, "RGBB " + str$(blue)
ink rgb(red, 0, 0), 0 : box 230, palette_h + 38, 230 + red, palette_h + 48
ink rgb(0, green, 0), 0 : box 230, palette_h + 60, 230 + green, palette_h + 70
ink rgb(0, 0, blue), 0 : box 230, palette_h + 82, 230 + blue, palette_h + 92
sync
loop
rem Hue in degrees, height from 0 (top, pale) to 1 (bottom, dark)
function palette_colour(hue#, height#)
sector = int(hue# / 60.0)
f# = hue# / 60.0 - sector
r# = 0.0 : g# = 0.0 : b# = 0.0
select sector
case 0 : r# = 1.0 : g# = f# : endcase
case 1 : r# = 1.0 - f# : g# = 1.0 : endcase
case 2 : g# = 1.0 : b# = f# : endcase
case 3 : g# = 1.0 - f# : b# = 1.0 : endcase
case 4 : r# = f# : b# = 1.0 : endcase
case default : r# = 1.0 : b# = 1.0 - f# : endcase
endselect
if height# < 0.5
rem Mix towards white
mix# = (0.5 - height#) * 2.0
r# = r# + (1.0 - r#) * mix# : g# = g# + (1.0 - g#) * mix# : b# = b# + (1.0 - b#) * mix#
else
rem Mix towards black
mix# = 1.0 - (height# - 0.5) * 2.0
r# = r# * mix# : g# = g# * mix# : b# = b# * mix#
endif
colour = rgb(int(r# * 255), int(g# * 255), int(b# * 255))
endfunction colour
Night Town - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/2d/night_town.dba · 95 lines (71 code)
Paints a town at night with the 2D drawing commands only: LINE for the sky and for filling shapes, DOT for the stars, CIRCLE and ELLIPSE for outlines, BOX for the buildings and windows. Press any key to build another town. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame with automatic screen refresh. - Structure. 1 GOSUB subroutine(s):
draw_town.
Commands used (16)
View source (95 lines)
rem ==================================================================
rem Night Town - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Paints a town at night with the 2D drawing commands only:
rem LINE for the sky and for filling shapes, DOT for the stars, CIRCLE
rem and ELLIPSE for outlines, BOX for the buildings and windows.
rem Press any key to build another town.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
randomize timer()
do
gosub draw_town
suspend for key
loop
draw_town:
w = screen width() : h = screen height()
cls
rem A sky gradient: one horizontal LINE per row, a little bluer each time
for y = 0 to h - 1
ink rgb(8 + y / 24, 10 + y / 10, 40 + y / 5), 0
line 0, y, w - 1, y
next y
rem Stars are single DOTs of random brightness, thinner near the horizon
for s = 1 to 220
sx = rnd(w - 1) : sy = rnd(h / 2)
c = 120 + rnd(135) - sy / 2
ink rgb(c, c, c + 20), 0
dot sx, sy
next s
rem The moon: a disc filled with one LINE per row, shaded from the lit
rem side, then CIRCLE outlines for its rim and a few craters
moonx = w - 130 : moony = 90 : moonr = 44
for dy = 0 - moonr to moonr
half = sqrt(moonr * moonr - dy * dy)
c = 235 - (dy + moonr) / 2
ink rgb(c, c - 6, c - 50), 0
line moonx - half, moony + dy, moonx + half, moony + dy
next dy
ink rgb(255, 250, 210), 0
circle moonx, moony, moonr
ink rgb(190, 180, 130), 0
circle moonx - 14, moony - 10, 9
circle moonx + 12, moony + 14, 6
circle moonx + 18, moony - 16, 4
rem Clouds: overlapping puffs filled row by row, each with an ELLIPSE rim
for cloud = 1 to 4
cx = rnd(w) : cy = 70 + rnd(110)
for puff = 0 to 3
px = cx + puff * 34 - 50 : py = cy - (puff mod 2) * 10
rx = 40 + rnd(20) : ry = 14 + rnd(6)
ink rgb(48 + puff * 4, 54 + puff * 4, 92 + puff * 4), 0
for dy = 0 - ry to ry
half = rx * sqrt(1.0 - (dy * dy * 1.0) / (ry * ry))
line px - half, py + dy, px + half, py + dy
next dy
ink rgb(90, 98, 140), 0
ellipse px, py, rx, ry
next puff
next cloud
rem Buildings: a filled BOX each, then small boxes for the windows
x = 0
while x < w
bw = 36 + rnd(40) : bh = 70 + rnd(170)
top = h - 30 - bh
shade = 18 + rnd(18)
ink rgb(shade, shade, shade + 12), 0
box x, top, x + bw - 2, h - 30
for wy = top + 10 to h - 48 step 16
for wx = x + 6 to x + bw - 14 step 12
if rnd(3) = 0
ink rgb(250, 210 + rnd(40), 110), 0
box wx, wy, wx + 5, wy + 8
endif
next wx
next wy
x = x + bw
endwhile
rem The street
ink rgb(20, 20, 26), 0
box 0, h - 30, w - 1, h - 1
ink rgb(220, 200, 90), 0
for x = 10 to w step 60
line x, h - 15, x + 30, h - 15
next x
ink rgb(255, 255, 255), 0
text 12, 10, "NIGHT TOWN - press a key for another town"
return
Spirograph - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/2d/spirograph.dba · 51 lines (36 code)
Draws rolling-circle curves with short LINE segments whose colour drifts through the spectrum. Click the mouse (or wait for the curve to close) to start a new pattern. SPACE clears the screen. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 GOSUB subroutine(s):
new_pattern.
Commands used (17)
View source (51 lines)
rem ==================================================================
rem Spirograph - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Draws rolling-circle curves with short LINE segments whose colour
rem drifts through the spectrum. Click the mouse (or wait for the
rem curve to close) to start a new pattern. SPACE clears the screen.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
randomize timer()
w = screen width() : h = screen height()
cls
gosub new_pattern
do
rem Forty short segments per frame make the curve grow smoothly
for step = 1 to 40
t# = t# + 0.5
x# = cx + (big - small) * cos(t#) + pen * cos(t# * (big - small) / small)
y# = cy + (big - small) * sin(t#) - pen * sin(t# * (big - small) / small)
ink rgb(128 + sin(t# * 0.5) * 127, 128 + sin(t# * 0.5 + 120) * 127, 128 + sin(t# * 0.5 + 240) * 127), 0
line lastx#, lasty#, x#, y#
lastx# = x# : lasty# = y#
next step
if spacekey() = 1 then cls
if mouseclick() = 1 and clicked = 0 then gosub new_pattern
clicked = mouseclick()
if t# > turns * 360.0 then gosub new_pattern
ink rgb(255, 255, 255), 0
text 10, 10, "SPIROGRAPH - click for a new pattern, SPACE clears"
sync
loop
new_pattern:
rem A fixed ring rolls a smaller wheel; the pen sits on the wheel's spoke
big = 120 + rnd(80)
small = 20 + rnd(70)
pen = 20 + rnd(90)
cx = w / 4 + rnd(w / 2) : cy = h / 3 + rnd(h / 3)
rem The curve closes after small / gcd(big, small) turns
a = big : b = small
while b > 0
r = a mod b : a = b : b = r
endwhile
turns = small / a
if turns > 12 then turns = 12
t# = 0.0
lastx# = cx + (big - small) + pen : lasty# = cy
return
Tutorials — 3D
Animation - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/animation.dba · 80 lines (59 code)
Left: an imp whose idle and walk clips sit one after the other in a single object (APPEND OBJECT), played, looped, slowed down and blended with interpolation. Right: a rider animated by hand: each pose is stored with SET OBJECT KEYFRAME, the keyframes are saved to a file and loaded onto a second rider with APPEND OBJECT ANIMATION. 1 idle, 2 walk, 3 stops, 4 shows frame 10, 5 walks once, S slows down, I toggles interpolation, C clears the rider's middle keyframe. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Assets. Loads 3 media file(s) from the example folder:
imp_idle.x, imp_walk.x, rider.x.
Commands used (39)
View source (80 lines)
rem ==================================================================
rem Animation - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Left: an imp whose idle and walk clips sit one after the other in a
rem single object (APPEND OBJECT), played, looped, slowed down and
rem blended with interpolation. Right: a rider animated by hand: each
rem pose is stored with SET OBJECT KEYFRAME, the keyframes are saved to
rem a file and loaded onto a second rider with APPEND OBJECT ANIMATION.
rem 1 idle, 2 walk, 3 stops, 4 shows frame 10, 5 walks once, S slows down, I toggles
rem interpolation, C clears the rider's middle keyframe.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
color backdrop rgb(50, 60, 80)
set ambient light 60
position camera 0, 6, -22
point camera 0, 3, 0
rem --- The imp: idle frames first, then the walk appended from frame 30
load object "imp_idle.x", 1
idle_end = total object frames(1)
append object "imp_walk.x", 1, 30
walk_end = total object frames(1)
scale object 1, 200, 200, 200
position object 1, -6, 0, 0
loop object 1, 30, walk_end
set object speed 1, 100
rem --- The rider: three poses of the right arm become keyframes 0, 20 and 40
load object "rider.x", 2
scale object 2, 300, 300, 300
position object 2, 5, 0, 0
for pose = 0 to 2
rotate limb 2, 6, -150 + pose * 50, 0, 30
set object keyframe 2, pose * 20
next pose
loop object 2, 0, 40
rem The keyframes go to a file and back onto a second rider
save object animation "wave.anim", 2
load object "rider.x", 3
append object animation "rider.x", "wave.anim", 3, 0
delete file "wave.anim"
scale object 3, 300, 300, 300
position object 3, 11, 0, 0
loop object 3, 0, 40
speed = 100
do
key$ = lower$(inkey$())
if key$ <> lastkey$
select key$
case "1" : loop object 1, 0, idle_end : endcase
case "2" : loop object 1, 30, walk_end : endcase
case "3" : stop object 1 : endcase
case "5" : play object 1, 30, walk_end : endcase
case "4" : stop object 1 : set object frame 1, 10 : endcase
case "s"
if speed = 100 then speed = 25 else speed = 100
set object speed 1, speed
endcase
case "i"
rem Interpolation blends smoothly into a new clip instead of jumping
if object interpolation(1) = 0 then set object interpolation 1, 100 else set object interpolation 1, 0
endcase
case "c" : clear object keyframe 2, 20 : endcase
case "x" : clear all object keyframes 2 : endcase
endselect
endif
lastkey$ = key$
yrotate object 1, 180
ink rgb(255, 255, 255), 0
text 10, 10, "ANIMATION - 1 idle 2 walk 3 stop 4 frame 10 S speed I interpolation C/X keyframes"
text 10, 34, "IMP: FRAME " + str$(int(object frame(1))) + " of " + str$(total object frames(1)) + " PLAYING " + str$(object playing(1)) + " LOOPING " + str$(object looping(1))
text 10, 54, " SPEED " + str$(object speed(1)) + " INTERPOLATION " + str$(object interpolation(1))
text 10, 78, "RIDERS: frame " + str$(int(object frame(2))) + " and " + str$(int(object frame(3))) + " (the second loaded the saved keyframes)"
sync
loop
Cameras - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/cameras.dba · 96 lines (72 code)
Five ways to drive the camera, one after the other every four seconds (or with keys 1 to 5): circling with POSITION and POINT CAMERA, walking with MOVE, TURN, PITCH and ROLL, following a ball with SET CAMERA TO FOLLOW, turning on each axis, and riding a spinning object. The view is letterboxed with SET CAMERA VIEW. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 GOSUB subroutine(s):
enter_mode. - Data. 1 array(s) declared with
DIM. - Assets. Loads 1 media file(s) from the example folder:
crystal.jpg.
Commands used (54)
View source (96 lines)
rem ==================================================================
rem Cameras - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Five ways to drive the camera, one after the other every four
rem seconds (or with keys 1 to 5): circling with POSITION and POINT
rem CAMERA, walking with MOVE, TURN, PITCH and ROLL, following a ball
rem with SET CAMERA TO FOLLOW, turning on each axis, and riding a
rem spinning object. The view is letterboxed with SET CAMERA VIEW.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
rem With AUTOCAM ON, the camera moves to frame each new object
autocam on
make object sphere 1, 4
autocam off
color object 1, rgb(255, 220, 60)
load image "crystal.jpg", 1
for p = 2 to 9
make object box p, 4, 10 + p * 2, 4
position object p, sin(p * 45) * 30, 5 + p, cos(p * 45) * 30
texture object p, 1
next p
make object plain 10, 120, 120
xrotate object 10, 90
color object 10, rgb(50, 60, 80)
make object cone 11, 4
position object 11, 0, 25, 0
color object 11, rgb(255, 90, 90)
set camera range 1, 500
rem Letterbox: draw the 3D only between y = 60 and y = 420
set camera view 0, 60, screen width(), screen height() - 60
dim mode$(5)
mode$(1) = "POSITION + POINT CAMERA" : mode$(2) = "MOVE, TURN, PITCH, ROLL" : mode$(3) = "SET CAMERA TO FOLLOW"
mode$(4) = "XROTATE, YROTATE, ZROTATE, ROTATE" : mode$(5) = "SET CAMERA TO OBJECT ORIENTATION"
mode = 1 : changed = timer()
do
t = t + 1
for k = 1 to 5
if keystate(k + 1) = 1 and mode <> k then mode = k : changed = timer() : manual = 1 : gosub enter_mode
next k
if manual = 0 and timer() - changed > 4000 then mode = (mode mod 5) + 1 : changed = timer() : gosub enter_mode
rem The ball runs a circle; the cone spins on top
bx# = sin(t) * 20 : bz# = cos(t * 1.3) * 20
position object 1, bx#, 2, bz#
yrotate object 11, wrapvalue(t * 2)
select mode
case 1
position camera sin(t * 0.5) * 60, 30, cos(t * 0.5) * 60
point camera 0, 8, 0
endcase
case 2
rem Walk forwards, turn, nod and lean
move camera 0.3
if (t / 120) mod 2 = 0 then turn camera right 0.4 else turn camera left 0.4
if (t / 60) mod 2 = 0 then pitch camera down 0.1 else pitch camera up 0.1
if (t / 90) mod 2 = 0 then roll camera left 0.05 else roll camera right 0.05
endcase
case 3
rem SET CAMERA TO FOLLOW x, y, z, angle, distance, height, smoothing, collision
set camera to follow bx#, 2, bz#, wrapvalue(t + 90), 25, 10, 15, 0
endcase
case 4
position camera 0, 15, -50
if (t / 120) mod 2 = 0 then set camera rotation xyz else set camera rotation zyx
xrotate camera 10
yrotate camera sin(t) * 30
zrotate camera sin(t * 2) * 10
if (t / 60) mod 4 = 3 then rotate camera 20, 0, 0
endcase
case 5
position camera 0, 25, 0
set camera to object orientation 11
move camera -1
endcase
endselect
rem SET CAMERA FOV zooms: a narrow angle magnifies
set camera fov 60 + sin(t * 0.7) * 15
clear camera view rgb(20, 24, 34)
ink rgb(255, 255, 255), 0
text 10, 8, "CAMERAS - mode " + str$(mode) + ": " + mode$(mode) + " (keys 1-5)"
text 10, 28, "POSITION " + str$(int(camera position x())) + ", " + str$(int(camera position y())) + ", " + str$(int(camera position z())) + " ANGLE " + str$(int(camera angle x())) + ", " + str$(int(camera angle y())) + ", " + str$(int(camera angle z()))
sync
loop
rem Each mode starts from a fresh camera
enter_mode:
set camera rotation xyz
rotate camera 0, 0, 0
if mode = 2 then position camera 0, 6, -40
return
Collisions - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/collisions.dba · 96 lines (73 code)
A ball bounces around an arena of obstacles. Each obstacle tests collisions in its own way: by box, by sphere or polygon by polygon, and one has a separate collision box. OBJECT COLLISION tells whether the ball touches something now, OBJECT HIT only reports a new touch. G switches all collisions off and on, B removes the collision box, W makes the walls solid. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM.
Commands used (41)
View source (96 lines)
rem ==================================================================
rem Collisions - an OpenDBC tutorial for the DarkBASIC Classic profile
rem A ball bounces around an arena of obstacles. Each obstacle tests
rem collisions in its own way: by box, by sphere or polygon by polygon,
rem and one has a separate collision box. OBJECT COLLISION tells whether
rem the ball touches something now, OBJECT HIT only reports a new touch.
rem G switches all collisions off and on, B removes the collision box,
rem W makes the walls solid.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
randomize 3
color backdrop rgb(30, 30, 40)
position camera 0, 55, -45
point camera 0, 0, 0
rem The floor and the ball
make object box 100, 80, 1, 60
position object 100, 0, -1, 0
color object 100, rgb(60, 70, 60)
make object sphere 1, 4
color object 1, rgb(255, 220, 60)
set object collision to spheres 1
rem Obstacles 2 to 7, each with its own collision shape
dim kind$(7) : dim flash(7)
for o = 2 to 7
select o mod 3
case 0 : make object cube o, 8 : set object collision to boxes o : kind$(o) = "box" : endcase
case 1 : make object sphere o, 8 : set object collision to spheres o : kind$(o) = "sphere" : endcase
case 2 : make object cone o, 9 : set object collision to polygons o : kind$(o) = "polygon" : endcase
endselect
position object o, -27 + (o - 2) * 11, 3, sin(o * 70) * 15
color object o, rgb(90, 120, 200)
next o
rem Obstacle 7 gets a collision box larger than its own shape
make object collision box 7, -7, -4, -7, 7, 8, 7, 0
kind$(7) = "collision box"
rem The arena walls do not collide: the ball turns round at the edges
for w = 8 to 9
make object box w, 80, 4, 1
position object w, 0, 1, (w - 8.5) * 61
set object collision off w
next w
x# = 0 : z# = -20 : dx# = 0.35 : dz# = 0.27
do
key$ = lower$(inkey$())
if key$ <> lastkey$
if key$ = "g"
all_off = 1 - all_off
if all_off = 1 then set global collision off else set global collision on
endif
if key$ = "b" then delete object collision box 7 : kind$(7) = "sphere (box removed)"
rem W makes the walls solid: the ball then turns at them too
if key$ = "w"
walls = 1 - walls
for w = 8 to 9
if walls = 1 then set object collision on w else set object collision off w
next w
endif
endif
lastkey$ = key$
x# = x# + dx# : z# = z# + dz#
if abs(x#) > 38 then dx# = 0 - dx#
if abs(z#) > 28 then dz# = 0 - dz#
position object 1, x#, 2, z#
rem A new hit flashes the obstacle red and sends the ball back
hit = object hit(1, 0)
if hit > 1 and hit < 10
color object hit, rgb(255, 60, 60)
flash(hit) = 30
dx# = 0 - dx# : dz# = 0 - dz#
x# = x# + dx# * 3 : z# = z# + dz# * 3
lastx# = get object collision x() : lasty# = get object collision y() : lastz# = get object collision z()
endif
for o = 2 to 7
if flash(o) > 0
dec flash(o)
if flash(o) = 0 then color object o, rgb(90, 120, 200)
endif
next o
ink rgb(255, 255, 255), 0
text 10, 10, "COLLISIONS - G switches collisions off/on, B removes the collision box"
text 10, 30, "touching now: " + str$(object collision(1, 0)) + " last hit: " + str$(hit) + " all collisions off: " + str$(all_off)
text 10, 50, "last collision adjustment: " + str$(int(lastx# * 10) / 10.0) + ", " + str$(int(lasty# * 10) / 10.0) + ", " + str$(int(lastz# * 10) / 10.0)
for o = 2 to 7
if object in screen(o) = 1 then center text object screen x(o), object screen y(o) + 28, kind$(o)
next o
sync
loop
Flight - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/flight.dba · 123 lines (89 code)
Moving and turning objects: a glider flies with MOVE OBJECT, TURN, PITCH and ROLL; a chaser follows it with POINT OBJECT; a shadow copies its orientation; a sign always faces the camera; and the camera trails behind using NEWXVALUE, CURVEVALUE and CURVEANGLE. LEFT/RIGHT turn, UP/DOWN pitch, Q/E roll, L frees the compass; otherwise it flies itself. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Assets. Loads 1 media file(s) from the example folder:
sea.jpg.
Commands used (63)
View source (123 lines)
rem ==================================================================
rem Flight - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Moving and turning objects: a glider flies with MOVE OBJECT, TURN,
rem PITCH and ROLL; a chaser follows it with POINT OBJECT; a shadow
rem copies its orientation; a sign always faces the camera; and the
rem camera trails behind using NEWXVALUE, CURVEVALUE and CURVEANGLE.
rem LEFT/RIGHT turn, UP/DOWN pitch, Q/E roll, L frees the compass;
rem otherwise it flies itself.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
backdrop on
color backdrop rgb(150, 190, 230)
set camera range 1, 3000
rem The sea: a large plain laid flat
load image "sea.jpg", 1
make object plain 1, 2000, 2000
texture object 1, 1
scale object texture 1, 20, 20
xrotate object 1, 90
position object 1, 0, -20, 0
rem The glider: a cone made to point along +Z. FIX OBJECT PIVOT keeps the
rem tilt as the object's resting shape, so later turns start from it.
make object cone 2, 8
scale object 2, 100, 220, 100
xrotate object 2, 90
fix object pivot 2
color object 2, rgb(240, 90, 60)
rem The chaser, the shadow and a sign
make object sphere 4, 2
color object 4, rgb(60, 60, 200)
make object cone 5, 4
scale object 5, 60, 200, 60
xrotate object 5, 90
fix object pivot 5
color object 5, rgb(20, 40, 60)
make object plain 6, 12, 4
color object 6, rgb(255, 230, 120)
position object 6, -70, 12, 90
rem A compass needle locked to the screen, like part of a dashboard
make object cone 7, 0.6
scale object 7, 40, 120, 40
lock object on 7
position object 7, 4.2, 3.0, 10
color object 7, rgb(255, 60, 60)
rem Two cones turned by the same angles in a different order
for o = 8 to 9
make object cone o, 3
position object o, 40 + (o - 8.5) * 16, 5, 70
color object o, rgb(120, 200, 120)
next o
set object rotation xyz 8
set object rotation zyx 9
position object 2, 0, 10, 0
camx# = 0 : camy# = 20 : camz# = -40 : camyaw# = 0
do
rem Controls, or an autopilot that weaves in long curves
if leftkey() + rightkey() + upkey() + downkey() + keystate(16) + keystate(18) > 0 then manual = 1
if manual = 1
if leftkey() = 1 then turn object left 2, 1.5
if rightkey() = 1 then turn object right 2, 1.5
if upkey() = 1 then pitch object down 2, 1
if downkey() = 1 then pitch object up 2, 1
if keystate(16) = 1 then roll object left 2, 2
if keystate(18) = 1 then roll object right 2, 2
else
t = t + 1
turn object right 2, sin(t * 0.7) * 0.9
if object position y(2) > 25 then pitch object down 2, 0.3
if object position y(2) < 5 then pitch object up 2, 0.3
endif
move object 2, 0.8
rem The chaser aims a little above the glider and moves a little slower
point object 4, object position x(2), object position y(2) + 10, object position z(2)
move object 4, 0.7
rem The shadow sits on the water and copies the glider's orientation
position object 5, object position x(2), -19, object position z(2)
set object to object orientation 5, 2
rem The sign turns to face the camera
set object to camera orientation 6
rem L unlocks the compass needle from the screen and locks it again
if lower$(inkey$()) = "l" and lheld = 0
locked = 1 - locked
if locked = 1 then lock object off 7 else lock object on 7
endif
lheld = (lower$(inkey$()) = "l")
rem The compass needle shows the glider's heading
zrotate object 7, 0 - object angle y(2)
xrotate object 8, wrapvalue(t) : zrotate object 8, 40
xrotate object 9, wrapvalue(t) : zrotate object 9, 40
rem The camera eases towards a point behind the glider
heading# = object angle y(2)
wantx# = newxvalue(object position x(2), heading#, -22)
wantz# = newzvalue(object position z(2), heading#, -22)
wanty# = newyvalue(object position y(2), object angle x(2), -6) + 9
camx# = curvevalue(wantx#, camx#, 8)
camy# = curvevalue(wanty#, camy#, 8)
camz# = curvevalue(wantz#, camz#, 8)
camyaw# = curveangle(heading#, camyaw#, 8)
position camera camx#, camy#, camz#
point camera object position x(2), object position y(2), object position z(2)
ink rgb(20, 30, 50), 0
text 10, 10, "FLIGHT - arrows turn and pitch, Q/E roll"
text 10, 30, "GLIDER ANGLES " + str$(int(object angle x(2))) + ", " + str$(int(object angle y(2))) + ", " + str$(int(object angle z(2))) + " CAMERA YAW " + str$(int(camyaw#))
rem OBJECT SCREEN X and Y place a label on the chaser when it is visible
if object in screen(4) = 1
text object screen x(4) + 10, object screen y(4) - 10, "chaser"
endif
if object in screen(8) = 1 then text object screen x(8) - 20, object screen y(8) + 20, "XYZ"
if object in screen(9) = 1 then text object screen x(9) - 20, object screen y(9) + 20, "ZYX"
sync
loop
Landscape - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/landscape.dba · 112 lines (84 code)
A matrix is a grid of tiles whose corners can be raised and lowered: the quickest way to build ground in DarkBASIC. This one never ends: as the rover drives on, SHIFT MATRIX UP moves the rows along and a new row of hills is made at the far edge. A second, ghosted matrix is the lake. W wireframe, R random hills, F flattens, D drains, G makes the lake solid. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 GOSUB subroutine(s):
make_row. - Data. 1 array(s) declared with
DIM. - Assets. Loads 2 media file(s) from the example folder:
terrain.jpg, sea.jpg.
Commands used (55)
View source (112 lines)
rem ==================================================================
rem Landscape - an OpenDBC tutorial for the DarkBASIC Classic profile
rem A matrix is a grid of tiles whose corners can be raised and lowered:
rem the quickest way to build ground in DarkBASIC. This one never ends:
rem as the rover drives on, SHIFT MATRIX UP moves the rows along and a
rem new row of hills is made at the far edge. A second, ghosted matrix
rem is the lake. W wireframe, R random hills, F flattens, D drains,
rem G makes the lake solid.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
color backdrop rgb(160, 190, 220)
set camera range 1, 2000
if fog available() = 1 then fog on : fog color rgb(160, 190, 220) : fog distance 600
rem --- Matrix 1: 40 x 40 tiles over 800 x 800 units, textured from a 2 x 2 atlas
segments = 40 : size = 800 : cell = size / segments
make matrix 1, size, size, segments, segments
load image "terrain.jpg", 1
prepare matrix texture 1, 1, 2, 2
position matrix 1, 0 - size / 2, 0, 0 - size / 2
set matrix texture 1, 0, 1
dim row(segments)
for z = 0 to segments
gosub make_row
next z
update matrix 1
rem --- Matrix 2: the lake, a ghosted flat grid that ripples
make matrix 2, 300, 300, 12, 12
load image "sea.jpg", 2
prepare matrix texture 2, 2, 1, 1
position matrix 2, -150, -6, 60
ghost matrix on 2
set matrix 2, 1, 0, 1, 1, 1, 1, 1
rem The rover: a box riding the ground
make object box 1, 8, 4, 12
color object 1, rgb(220, 120, 40)
travel# = 0.0 : rows_made = segments
do
t = t + 1
rem Drive forward; every time the rover covers a whole tile, shift a row
travel# = travel# + 1.6
if travel# >= cell
travel# = travel# - cell
shift matrix up 1
inc rows_made
z = segments : gosub make_row
update matrix 1
endif
rem GET GROUND HEIGHT works in the matrix's own coordinates
gy# = get ground height(1, size / 2, size / 2 - 200 + travel#)
position object 1, 0, gy# + 2, -200 + travel#
position camera 0, gy# + 30, -260 + travel#
point camera 0, gy# + 5, -150 + travel#
rem The lake ripples: every corner of matrix 2 bobs on its own wave
for z = 0 to 12
for x = 0 to 12
set matrix height 2, x, z, sin(t * 3 + x * 30) * 1.5 + cos(t * 2 + z * 40) * 1.5
next x
next z
update matrix 2
key$ = lower$(inkey$())
if key$ <> lastkey$
if key$ = "w"
if matrix wireframe state(1) = 1 then set matrix wireframe off 1 else set matrix wireframe on 1
endif
if key$ = "r" then randomize matrix 1, 60 : update matrix 1
if key$ = "f" then fill matrix 1, 0, 1 : update matrix 1
if key$ = "d" and matrix exist(2) = 1 then delete matrix 2
if key$ = "g" and matrix exist(2) = 1
clear_lake = 1 - clear_lake
if clear_lake = 1 then ghost matrix off 2 else ghost matrix on 2
endif
rem Nudge the whole landscape sideways and back with the other shifts
if key$ = "q" then shift matrix left 1 : update matrix 1
if key$ = "e" then shift matrix right 1 : update matrix 1
if key$ = "s" then shift matrix down 1 : update matrix 1
endif
lastkey$ = key$
ink rgb(20, 30, 40), 0
text 10, 8, "LANDSCAPE - W wireframe, R random, F flat, D drains the lake, Q/E/S shift"
text 10, 28, "rows made " + str$(rows_made) + " ground under the rover " + str$(int(gy#)) + " corner height " + str$(int(get matrix height(1, 20, 20)))
text 10, 48, "MATRIX 1 at " + str$(int(matrix position x(1))) + ", " + str$(int(matrix position y(1))) + ", " + str$(int(matrix position z(1))) + " tiles " + str$(matrix tile count(1)) + " (" + str$(matrix tiles exist(1)) + ") lake exists " + str$(matrix exist(2))
sync
loop
rem Heights and tiles for row z: rolling hills that never repeat exactly
make_row:
for x = 0 to segments
wz = rows_made - segments + z
h# = 25 * sin(x * 17 + wz * 11) + 15 * cos(wz * 23) + 8 * sin((x - wz) * 31)
if abs(x - segments / 2) < 3 then h# = h# * 0.2
set matrix height 1, x, z, h#
rem Normals tilt with the slope so the light shades the hills
set matrix normal 1, x, z, 0 - cos(x * 17 + wz * 11) * 0.3, 1, 0 - cos(wz * 23) * 0.2
if x < segments and z < segments
tile = 1
if h# > 20 then tile = 4
if h# < -15 then tile = 3
if abs(x - segments / 2) < 2 then tile = 2
set matrix tile 1, x, z, tile
endif
next x
return
Lights - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/lights.dba · 90 lines (67 code)
Three kinds of light on a gallery of spheres: a point light that circles like a lamp, a spot light carried by a lantern object, and a directional light like the sun. Light 0 is the default light; lights 1 to 7 are made with MAKE LIGHT. Keys 1 to 3 hide and show the lights, A changes the colour of the ambient light. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Assets. Loads 1 media file(s) from the example folder:
bricks.jpg.
Commands used (53)
View source (90 lines)
rem ==================================================================
rem Lights - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Three kinds of light on a gallery of spheres: a point light that
rem circles like a lamp, a spot light carried by a lantern object, and
rem a directional light like the sun. Light 0 is the default light;
rem lights 1 to 7 are made with MAKE LIGHT. Keys 1 to 3 hide and show
rem the lights, A changes the colour of the ambient light.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
color backdrop rgb(10, 10, 16)
position camera 0, 20, -45
point camera 0, 0, 0
set ambient light 15
load image "bricks.jpg", 1
make object box 1, 80, 1, 50
position object 1, 0, -4, 0
texture object 1, 1
scale object texture 1, 4, 3
for s = 2 to 7
make object sphere s, 7
position object s, -25 + (s - 2) * 10, 0, 4
next s
make object cone 8, 2
color object 8, rgb(255, 255, 160)
set object 8, 1, 0, 1, 1, 0, 1, 1
rem The default light 0 becomes a dim blue directional light from above
set directional light 0, 0.3, -1, 0.4
color light 0, rgb(40, 50, 90)
rem Light 1: a warm point light, like a bulb
make light 1
set point light 1, 0, 8, 0
color light 1, 255, 180, 90
set light range 1, 40
rem Light 2: a spot light carried by the lantern (object 8)
make light 2
set spot light 2, 20, 40
color light 2, rgb(120, 255, 160)
set light range 2, 80
rem Light 3: a red directional light turned with ROTATE LIGHT
make light 3
set directional light 3, 1, 0, 0
color light 3, rgb(180, 30, 30)
ambient = 0
do
t = t + 1
position light 1, sin(t) * 25, 8, cos(t) * 12 - 4
rem The lantern swings; the spot light copies its place and direction
position object 8, sin(t * 0.7) * 20, 15, -10
point object 8, sin(t * 1.3) * 20, 0, 4
set light to object position 2, 8
set light to object orientation 2, 8
rotate light 3, 0, wrapvalue(t), 0
if t mod 300 = 150 then point light 3, 0, 0, 0
key$ = lower$(inkey$())
if key$ <> lastkey$
for l = 1 to 3
if key$ = str$(l)
if light visible(l) = 1 then hide light l else show light l
endif
next l
if key$ = "a" then ambient = (ambient + 1) mod 3
endif
lastkey$ = key$
if ambient = 0 then color ambient light rgb(255, 255, 255)
if ambient = 1 then color ambient light rgb(80, 120, 255)
if ambient = 2 then color ambient light rgb(255, 120, 60)
ink rgb(255, 255, 255), 0
text 10, 8, "LIGHTS - keys 1-3 hide and show lights, A ambient colour"
for l = 0 to 3
if light exist(l) = 1
line$ = "LIGHT " + str$(l) + ": type " + str$(light type(l)) + " visible " + str$(light visible(l)) + " range " + str$(int(light range(l)))
line$ = line$ + " at " + str$(int(light position x(l))) + "," + str$(int(light position y(l))) + "," + str$(int(light position z(l)))
line$ = line$ + " facing " + str$(int(light direction x(l) * 10) / 10.0) + "," + str$(int(light direction y(l) * 10) / 10.0) + "," + str$(int(light direction z(l) * 10) / 10.0)
text 10, 30 + l * 18, line$
endif
next l
if t = 600 then delete light 3
sync
loop
Limbs - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/limbs.dba · 73 lines (49 code)
A loaded model is a tree of limbs, each with its own position, angle, scale, texture and visibility. The rider is glued to the scooter's hull, waves an arm and turns a red helmet; the seat bounces, the handlebar grows and the hull gets a scrolling striped texture. The panel cycles through the rider's limbs. U unglues the rider. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 user function(s):
file_name$. - Data. 1 array(s) declared with
DIM. - Assets. Loads 3 media file(s) from the example folder:
scooter.x, rider.x, bands.jpg.
Commands used (58)
View source (73 lines)
rem ==================================================================
rem Limbs - an OpenDBC tutorial for the DarkBASIC Classic profile
rem A loaded model is a tree of limbs, each with its own position, angle,
rem scale, texture and visibility. The rider is glued to the scooter's
rem hull, waves an arm and turns a red helmet; the seat bounces, the
rem handlebar grows and the hull gets a scrolling striped texture. The
rem panel cycles through the rider's limbs. U unglues the rider.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
color backdrop rgb(60, 80, 110)
set ambient light 50
position camera 0, 9, -22
point camera 0, 3, 0
load object "scooter.x", 1
load object "rider.x", 2
rem The models are small: scale the scooter, and the glued rider follows
scale object 1, 300, 300, 300
rem GLUE OBJECT TO LIMB object, to object, limb: the rider rides limb 1
glue object to limb 2, 1, 1
load image "bands.jpg", 1
texture limb 1, 1, 1
scale limb texture 1, 1, 2, 2
rem The rider's limbs, listed by name
perform checklist for object limbs 2
limbs = checklist quantity()
dim limbname$(20)
for l = 1 to limbs : limbname$(l) = checklist string$(l) : next l
glued = 1
do
t = t + 1
yrotate object 1, wrapvalue(t * 0.5)
rem Limb commands take the object number, then the limb number
rotate limb 2, 6, 0 - 60 + sin(t * 6) * 50, 0, 20
rotate limb 2, 3, 0, sin(t * 2) * 40, 0
color limb 2, 3, rgb(220, 40, 40)
offset limb 1, 2, 0, 0.6 + abs(sin(t * 4)) * 0.2, -0.6
scale limb 1, 3, 100 + sin(t * 3) * 30, 100, 100
scroll limb texture 1, 1, 0.003, 0
rem The flag (limb 5) shows for one second in three
if (t / 60) mod 3 = 0 then show limb 1, 5 else hide limb 1, 5
if lower$(inkey$()) = "u" and glued = 1
rem UNGLUE OBJECT frees the rider, who stays where the scooter left him
unglue object 2
position object 2, 3, 0, -2
glued = 0
endif
rem The panel shows one limb of the rider at a time
l = 1 + (t / 90) mod limbs
ink rgb(0, 0, 0), 0
box 0, 0, screen width(), 112
ink rgb(255, 255, 255), 0
text 10, 4, "LIMBS - " + str$(limbs) + " limbs in the rider; showing " + str$(l) + ": " + limbname$(l) + " (U unglues the rider)"
text 10, 24, "EXIST " + str$(limb exist(2, l)) + " VISIBLE " + str$(limb visible(2, l)) + " TEXTURE " + str$(limb texture(2, l)) + " " + file_name$(limb texture name$(2, l))
text 10, 44, "POSITION " + str$(int(limb position x(2, l) * 10) / 10.0) + ", " + str$(int(limb position y(2, l) * 10) / 10.0) + ", " + str$(int(limb position z(2, l) * 10) / 10.0)
text 10, 64, "OFFSET " + str$(int(limb offset x(2, l) * 10) / 10.0) + ", " + str$(int(limb offset y(2, l) * 10) / 10.0) + ", " + str$(int(limb offset z(2, l) * 10) / 10.0) + " ANGLE " + str$(int(limb angle x(2, l))) + ", " + str$(int(limb angle y(2, l))) + ", " + str$(int(limb angle z(2, l)))
text 10, 84, "DIRECTION " + str$(int(limb direction x(2, l))) + ", " + str$(int(limb direction y(2, l))) + ", " + str$(int(limb direction z(2, l)))
sync
loop
rem Texture names are full paths: keep what follows the last slash
function file_name$(path$)
for i = len(path$) to 1 step -1
if mid$(path$, i) = "/" or mid$(path$, i) = "\" then exitfunction right$(path$, len(path$) - i)
next i
endfunction path$
Materials - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/materials.dba · 79 lines (55 code)
Eight cubes show the ways a surface can look: a texture, a texture repeated and scrolling, wireframe, black made transparent, ghosting, fading, no lighting, and drawing in front of everything. SET OBJECT switches wireframe, transparency, culling, filtering, light, fog and ambient light in one command. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM. - Assets. Loads 3 media file(s) from the example folder:
bricks.jpg, crystal.jpg, sea.jpg.
Commands used (41)
View source (79 lines)
rem ==================================================================
rem Materials - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Eight cubes show the ways a surface can look: a texture, a texture
rem repeated and scrolling, wireframe, black made transparent, ghosting,
rem fading, no lighting, and drawing in front of everything. SET OBJECT
rem switches wireframe, transparency, culling, filtering, light, fog and
rem ambient light in one command.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
color backdrop rgb(40, 44, 60)
set ambient light 40
position camera 0, 3, -42
point camera 0, 0, 0
set mipmap mode 2
set normalization on
load image "bricks.jpg", 1
load image "crystal.jpg", 2
load image "sea.jpg", 3
dim label$(8)
for c = 1 to 8
make object cube c, 7
position object c, -27 + ((c - 1) mod 4) * 18, 7 - ((c - 1) / 4) * 15, 0
texture object c, 1
next c
label$(1) = "TEXTURE OBJECT"
rem 2: the texture repeated three times, with mirrored tiles (mode 1)
scale object texture 2, 3, 3
set object texture 2, 1, 1
label$(2) = "SCALE + SET OBJECT TEXTURE"
rem 3: a scrolling sea
texture object 3, 3
label$(3) = "SCROLL OBJECT TEXTURE"
rem 4: SET OBJECT object, wireframe 0 shows only the edges
set object 4, 0, 0, 1
label$(4) = "SET OBJECT wireframe"
rem 5: transparency 1 makes black texels invisible
texture object 5, 2
set object 5, 1, 1, 0
label$(5) = "SET OBJECT transparency"
rem 6: a ghost adds its colour to what is behind it
texture object 6, 2
ghost object on 6
label$(6) = "GHOST OBJECT ON"
rem 7: fading darkens the object towards black
label$(7) = "FADE OBJECT"
rem 8: no lighting, and drawn over everything with z-depth off
set object 8, 1, 0, 1, 1, 0, 1, 1
disable object zdepth 8
label$(8) = "light off, ZDEPTH off"
rem A slab passing in front shows which cube ignores depth
make object box 9, 70, 3, 2
color object 9, rgb(200, 80, 60)
do
t = t + 1
for c = 1 to 8 : yrotate object c, wrapvalue(t + c * 20) : xrotate object c, 20 : next c
scroll object texture 3, 0.004, 0.002
fade object 7, 50 + sin(t * 2) * 50
position object 9, 0, sin(t) * 12, -6
rem The ghost flickers on and off every two seconds
if t mod 240 = 0 then ghost object off 6
if t mod 240 = 120 then ghost object on 6
rem Cube 8 takes turns ignoring depth and respecting it
if t mod 360 = 180 then enable object zdepth 8
if t mod 360 = 0 then disable object zdepth 8
ink rgb(255, 255, 255), 0
for c = 1 to 8
if object in screen(c) = 1 then center text object screen x(c), object screen y(c) + 44, str$(c)
text 10 + ((c - 1) mod 2) * 310, screen height() - 90 + ((c - 1) / 2) * 20, str$(c) + ". " + label$(c)
next c
text 10, 10, "MATERIALS filtering " + str$(filtering available()) + " alpha blending " + str$(alphablending available()) + " 3D blit " + str$(3dblit available())
sync
loop
Mesh Builder - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/mesh_builder.dba · 79 lines (54 code)
A mesh is the shape of an object without its position or texture. Meshes come from objects (MAKE MESH FROM OBJECT) or files (LOAD MESH) and build new objects: MAKE OBJECT starts one, ADD LIMB stacks more meshes on it, LINK LIMB chains them and CHANGE MESH swaps a limb's shape. On the right, a sphere mesh is copied into a memblock, its vertices pushed in and out, and turned back into a mesh. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM. - Assets. Loads 1 media file(s) from the example folder:
buoy.x.
Commands used (37)
View source (79 lines)
rem ==================================================================
rem Mesh Builder - an OpenDBC tutorial for the DarkBASIC Classic profile
rem A mesh is the shape of an object without its position or texture.
rem Meshes come from objects (MAKE MESH FROM OBJECT) or files (LOAD
rem MESH) and build new objects: MAKE OBJECT starts one, ADD LIMB stacks
rem more meshes on it, LINK LIMB chains them and CHANGE MESH swaps a
rem limb's shape. On the right, a sphere mesh is copied into a memblock,
rem its vertices pushed in and out, and turned back into a mesh.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
color backdrop rgb(30, 40, 50)
position camera 0, 8, -36
point camera 0, 4, 0
rem --- Meshes from shapes and from a file
make object sphere 1, 4 : make mesh from object 1, 1 : delete object 1
make object cube 2, 3 : make mesh from object 2, 2 : delete object 2
make object cylinder 3, 2 : make mesh from object 3, 3 : delete object 3
load mesh "buoy.x", 4
rem --- A totem built from meshes: a base and two limbs linked in a chain
make object 10, 2, 0
add limb 10, 1, 1
offset limb 10, 1, 0, 3.5, 0
add limb 10, 2, 3
rem LINK LIMB object, parent limb, child limb: limb 2 follows limb 1
link limb 10, 1, 2
offset limb 10, 2, 0, 3.5, 0
position object 10, -10, 0, 0
color object 10, rgb(220, 170, 80)
rem --- The blob: sphere mesh 1 copied into memblock 1
make object sphere 20, 8
make mesh from object 5, 20
delete object 20
make memblock from mesh 1, 5
vertices = memblock dword(1, 0)
offset = memblock dword(1, 4)
rem Keep the original vertex positions to deform from
dim vx#(vertices) : dim vy#(vertices) : dim vz#(vertices)
for v = 0 to vertices - 1
vx#(v) = memblock float(1, offset + v * 12)
vy#(v) = memblock float(1, offset + v * 12 + 4)
vz#(v) = memblock float(1, offset + v * 12 + 8)
next v
make mesh from memblock 6, 1
make object 21, 6, 0
position object 21, 10, 4, 0
color object 21, rgb(120, 200, 255)
do
t = t + 1
rotate limb 10, 1, 0, t, 0
rotate limb 10, 2, 0, t * 2, 0
rem Every three seconds the top limb changes shape: cylinder or buoy
if t mod 180 = 0 then change mesh 10, 2, 4
if t mod 180 = 90 then change mesh 10, 2, 3
rem Push every vertex along its own direction, then rebuild the mesh
for v = 0 to vertices - 1
bump# = 1.0 + 0.18 * sin(t * 4 + vy#(v) * 60) * cos(vx#(v) * 50 + t * 3)
write memblock float 1, offset + v * 12, vx#(v) * bump#
write memblock float 1, offset + v * 12 + 4, vy#(v) * bump#
write memblock float 1, offset + v * 12 + 8, vz#(v) * bump#
next v
change mesh from memblock 6, 1
rem The object keeps its own copy: CHANGE MESH gives it the new shape
change mesh 21, 0, 6
yrotate object 21, t
ink rgb(255, 255, 255), 0
text 10, 10, "MESH BUILDER meshes 1-6 exist: " + str$(mesh exist(1)) + str$(mesh exist(2)) + str$(mesh exist(3)) + str$(mesh exist(4)) + str$(mesh exist(5)) + str$(mesh exist(6))
text 10, 30, "blob: " + str$(vertices) + " vertices in a " + str$(get memblock size(1)) + "-byte memblock"
if t = 300 then delete mesh 2
sync
loop
Scenery - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/scenery.dba · 82 lines (61 code)
The world around the objects: a textured, scrolling backdrop for the sky, fog that hides the distance, and ambient light that follows a day and night cycle. F switches the fog, B the backdrop, and D draws the 2D panel behind or in front of the 3D scene. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Assets. Loads 1 media file(s) from the example folder:
sky.jpg.
Commands used (40)
View source (82 lines)
rem ==================================================================
rem Scenery - an OpenDBC tutorial for the DarkBASIC Classic profile
rem The world around the objects: a textured, scrolling backdrop for the
rem sky, fog that hides the distance, and ambient light that follows a
rem day and night cycle. F switches the fog, B the backdrop, and D draws
rem the 2D panel behind or in front of the 3D scene.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
randomize 7
set camera range 1, 1500
set normalization off
rem The sky is the backdrop with a cloud texture that scrolls
load image "sky.jpg", 1
backdrop on
texture backdrop 1
color backdrop rgb(120, 160, 210)
rem Ground and a forest of cone trees on cylinder trunks
make object plain 1, 1200, 1200
xrotate object 1, 90
color object 1, rgb(70, 110, 50)
for t = 0 to 59
x = rnd(800) - 400 : z = rnd(800) - 100
make object cone 10 + t, 12 + rnd(8)
position object 10 + t, x, 10, z
color object 10 + t, rgb(30, 90 + rnd(50), 40)
make object cylinder 100 + t, 3
scale object 100 + t, 60, 200, 60
position object 100 + t, x, 2, z
color object 100 + t, rgb(90, 60, 30)
next t
fogged = 1 : behind = 0
if fog available() = 1
fog on
fog color rgb(170, 190, 210)
endif
position camera 0, 14, -150
do
t# = t# + 0.3
rem The camera walks slowly through the forest
position camera sin(t#) * 60, 14, -150 + t# * 1.5
point camera sin(t# + 10) * 60, 12, -100 + t# * 1.5
if t# > 400 then t# = 0
scroll backdrop 0.2, 0
rem Day and night: ambient light and fog distance follow the sun
sun# = (sin(t# * 2) + 1) / 2
set ambient light 20 + sun# * 70
if fog available() = 1 then fog distance 150 + sun# * 450
key$ = lower$(inkey$())
if key$ <> lastkey$
if key$ = "f" and fog available() = 1
fogged = 1 - fogged
if fogged = 1 then fog on else fog off
endif
if key$ = "b"
sky_off = 1 - sky_off
if sky_off = 1 then backdrop off else backdrop on
endif
if key$ = "d"
behind = 1 - behind
if behind = 1 then draw to back else draw to front
endif
endif
lastkey$ = key$
rem A 2D panel: in front of the 3D by default, behind it after DRAW TO BACK
ink rgb(30, 30, 40), 0
box 10, 10, 330, 92
ink rgb(255, 255, 255), 0
text 20, 16, "SCENERY - F fog, B backdrop, D draw order"
text 20, 36, "ambient " + str$(int(20 + sun# * 70)) + "% fog " + str$(fogged) + " (available " + str$(fog available()) + ")"
if behind = 1 then text 20, 56, "2D drawn behind the 3D" else text 20, 56, "2D drawn in front of the 3D"
sync
loop
Shapes - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/shapes.dba · 62 lines (45 code)
The first steps in 3D: every MAKE OBJECT shape on a turntable, each turning on a different axis, with its position, angles and size read back. SPACE hides and shows the cone, D deletes the triangle. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame.
Commands used (47)
View source (62 lines)
rem ==================================================================
rem Shapes - an OpenDBC tutorial for the DarkBASIC Classic profile
rem The first steps in 3D: every MAKE OBJECT shape on a turntable, each
rem turning on a different axis, with its position, angles and size read
rem back. SPACE hides and shows the cone, D deletes the triangle.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
autocam off
backdrop on
color backdrop rgb(30, 34, 52)
position camera 0, 9, -50
point camera 0, 3, 0
rem One of each shape, side by side
make object cube 1, 6
make object box 2, 8, 4, 3
make object sphere 3, 6
make object cylinder 4, 6
make object cone 5, 6
make object plain 6, 7, 5
make object triangle 7, 0, 4, 0, -4, -3, 0, 4, -3, 0
for o = 1 to 7
position object o, (o - 4) * 9, 3, 0
color object o, rgb(80 + o * 24, 220 - o * 18, 120 + (o mod 3) * 50)
next o
rem A triangle has one side only: SET OBJECT with culling 0 shows both
set object 7, 1, 0, 0
rem A floor made from a flattened box
make object box 10, 90, 1, 30
position object 10, 0, -2, 0
color object 10, rgb(60, 70, 90)
do
a = wrapvalue(a + 1)
rem Each command turns one axis; ROTATE OBJECT sets all three at once
xrotate object 1, a
yrotate object 2, a
zrotate object 3, a
rotate object 4, a, a / 2, 0
yrotate object 5, a
yrotate object 6, a
yrotate object 7, a
rem The sphere breathes with SCALE OBJECT (percent on each axis)
scale object 3, 100, 70 + sin(a * 4) * 30, 100
key$ = lower$(inkey$())
if key$ = " " and lastkey$ <> " "
if object visible(5) = 1 then hide object 5 else show object 5
endif
if key$ = "d" and lastkey$ <> "d" and object exist(7) = 1 then delete object 7
lastkey$ = key$
ink rgb(255, 255, 255), 0
text 10, 10, "SHAPES - SPACE hides the cone, D deletes the triangle"
text 10, 34, "CUBE: POSITION " + str$(object position x(1)) + ", " + str$(object position y(1)) + ", " + str$(object position z(1)) + " ANGLE X " + str$(int(object angle x(1)))
text 10, 54, "BOX: SIZE " + str$(int(object size(2))) + " X " + str$(int(object size x(2))) + " Y " + str$(int(object size y(2))) + " Z " + str$(int(object size z(2)))
text 10, 74, "CYLINDER: ANGLES " + str$(int(object angle x(4))) + ", " + str$(int(object angle y(4))) + ", " + str$(int(object angle z(4)))
text 10, 94, "CONE VISIBLE " + str$(object visible(5)) + " TRIANGLE EXISTS " + str$(object exist(7))
sync
loop
Static World - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/3d/static_world.dba · 101 lines (74 code)
Static objects are scenery fixed in place: they draw fast and come with their own collision data. Here maze walls become static, a walker slides along them with GET STATIC COLLISION HIT, and a turret watches it with STATIC LINE OF SIGHT. The level is saved to a file, cleared and loaded back. O switches static occlusion, D the door. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. constant tables in
DATA blocks read with READ. - Assets. Loads 3 media file(s) from the example folder:
bricks.jpg, rock.jpg, level.dbo.
Commands used (50)
View source (101 lines)
rem ==================================================================
rem Static World - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Static objects are scenery fixed in place: they draw fast and come
rem with their own collision data. Here maze walls become static, a
rem walker slides along them with GET STATIC COLLISION HIT, and a turret
rem watches it with STATIC LINE OF SIGHT. The level is saved to a file,
rem cleared and loaded back. O switches static occlusion, D the door.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
autocam off
color backdrop rgb(40, 44, 56)
position camera 0, 90, -60
point camera 0, 0, 0
load image "bricks.jpg", 1
load image "rock.jpg", 2
rem --- Walls: cubes turned into static objects, then deleted
restore
for w = 1 to 9
read x, z, sx, sz
make object box 1, sx, 8, sz
position object 1, x, 4, z
texture object 1, 1
scale object texture 1, sx / 8, 1
make static object 1
delete object 1
next w
rem The floor joins the static scenery with the rock texture
make object box 1, 100, 1, 80
position object 1, 0, -0.5, 0
texture object 1, 2
make static object 1
delete object 1
rem An invisible collision-only block in the open: walls you cannot see
make static collision box 10, 0, -30, 20, 8, -20
rem --- Save the level, clear it and load it back from the file
save static objects "level.dbo"
delete static objects
load static objects "level.dbo"
delete file "level.dbo"
set static objects texture 0
enable static occlusion
rem The walker and the turret
make object sphere 2, 4
color object 2, rgb(255, 220, 60)
make object cylinder 3, 4
position object 3, 35, 2, -25
color object 3, rgb(200, 60, 60)
rem A sliding door that is not static until D attaches it
make object box 4, 14, 8, 1
position object 4, -10, 4, 15
color object 4, rgb(140, 100, 60)
wx# = -40 : wz# = -30 : heading# = 45
do
rem Move the walker; a static hit pushes it back and turns it
oldx# = wx# : oldz# = wz#
wx# = newxvalue(wx#, heading#, 0.6) : wz# = newzvalue(wz#, heading#, 0.6)
if get static collision hit(oldx# - 2, 0, oldz# - 2, oldx# + 2, 4, oldz# + 2, wx# - 2, 0, wz# - 2, wx# + 2, 4, wz# + 2) = 1
wx# = wx# - get static collision x() : wz# = wz# - get static collision z()
push$ = str$(int(get static collision x() * 10) / 10.0) + ", " + str$(int(get static collision y() * 10) / 10.0) + ", " + str$(int(get static collision z() * 10) / 10.0)
heading# = wrapvalue(heading# + 90 + rnd(90))
endif
if abs(wx#) > 48 or abs(wz#) > 38 then wx# = oldx# : wz# = oldz# : heading# = wrapvalue(heading# + 180)
position object 2, wx#, 2, wz#
rem The turret sees the walker when no static object is in between
blocked = static line of sight(35, 3, -25, wx#, 3, wz#, 1, 1)
if blocked = 0 then color object 3, rgb(255, 60, 60) else color object 3, rgb(90, 40, 40)
key$ = lower$(inkey$())
if key$ <> lastkey$
if key$ = "o"
occlusion = 1 - occlusion
if occlusion = 1 then disable static occlusion else enable static occlusion
endif
if key$ = "d"
attached = 1 - attached
if attached = 1 then attach object to static 4 else detach object from static 4
endif
endif
lastkey$ = key$
ink rgb(255, 255, 255), 0
text 10, 10, "STATIC WORLD - O occlusion, D attaches the door to the static world"
if blocked = 0
text 10, 30, "turret sees the walker"
else
text 10, 30, "line of sight blocked at " + str$(int(static line of sight x())) + ", " + str$(int(static line of sight y())) + ", " + str$(int(static line of sight z()))
endif
text 10, 50, "door static: " + str$(attached) + " occlusion off: " + str$(occlusion) + " last push back: " + push$
sync
loop
rem Wall centre x and z, then length along x and along z
data -20, 0, 40, 2, 20, 10, 2, 30, 0, -20, 2, 20, -30, 20, 30, 2, 30, 25, 30, 2
data -49, 0, 2, 80, 49, 0, 2, 80, 0, 39, 100, 2, 0, -39, 100, 2
Tutorials — Bitmaps
Parallax - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/bitmap/parallax.dba · 82 lines (62 code)
Draws two wide landscapes once into off-screen bitmaps, then scrolls them at different speeds with COPY BITMAP. The far mountains move slowly, the near hills quickly, which gives a sense of depth. SPACE squeezes the whole panorama onto the screen, ESCAPE quits. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame.
Commands used (23)
View source (82 lines)
rem ==================================================================
rem Parallax - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Draws two wide landscapes once into off-screen bitmaps, then scrolls
rem them at different speeds with COPY BITMAP. The far mountains move
rem slowly, the near hills quickly, which gives a sense of depth.
rem SPACE squeezes the whole panorama onto the screen, ESCAPE quits.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
w = screen width() : h = screen height()
band = h / 2
rem Bitmap 1: the sky and far mountains, two screens wide. The ridge
rem repeats every screen width, so any window of it can be copied.
create bitmap 1, w * 2, band
set current bitmap 1
for y = 0 to band - 1
ink rgb(70 + y / 3, 120 + y / 3, 200 - y / 6), 0
line 0, y, w * 2, y
next y
for x = 0 to w * 2 - 1
a# = x * 360.0 / w
ridge = band - 60 - sin(a# * 2) * 30 - sin(a# * 5 + 40) * 14
ink rgb(90, 100, 130), 0
line x, ridge, x, band
ink rgb(235, 240, 250), 0
if ridge < band - 80 then line x, ridge, x, ridge + 6
next x
rem Bitmap 2: the near hills with trees, also seamless across a screen width
create bitmap 2, w * 2, band
set current bitmap 2
for y = 0 to band - 1
ink rgb(90, 100, 130), 0
line 0, y, w * 2, y
next y
for x = 0 to w * 2 - 1
a# = x * 360.0 / w
hill = 50 + sin(a# * 3) * 25 + sin(a# * 7 + 90) * 8
ink rgb(40, 110 + hill / 2, 50), 0
line x, hill, x, band
next x
for t = 0 to 23
tx = t * w / 12 + 10
a# = tx * 360.0 / w
ty = 50 + sin(a# * 3) * 25 + sin(a# * 7 + 90) * 8
ink rgb(30, 70, 35), 0
for k = 0 to 24
line tx - k / 3, ty - 30 + k, tx + k / 3, ty - 30 + k
next k
ink rgb(80, 50, 30), 0
box tx - 2, ty - 6, tx + 2, ty + 4
next t
rem Back to the screen. CURRENT BITMAP() reports which bitmap draws now.
set current bitmap 0
screen_bitmap = current bitmap()
do
far = (far + 1) mod w
near = (near + 4) mod w
if spacekey() = 1
rem The ten-argument form stretches: the whole panorama fits the screen
copy bitmap 1, 0, 0, w * 2, band, 0, 0, 0, w, band
copy bitmap 2, 0, 0, w * 2, band, 0, 0, band, w, h
else
copy bitmap 1, far, 0, far + w, band, 0, 0, 0, w, band
copy bitmap 2, near, 0, near + w, band, 0, 0, band, w, h
endif
ink rgb(255, 255, 255), 0
text 10, 10, "BITMAP 1: " + str$(bitmap width(1)) + " x " + str$(bitmap height(1)) + " x " + str$(bitmap depth(1)) + " bits"
text 10, 28, "BITMAP 2 EXISTS: " + str$(bitmap exist(2)) + " CURRENT BITMAP: " + str$(screen_bitmap)
text 10, 46, "SPACE shows the whole panorama, ESCAPE quits"
if escapekey() = 1 then exit
sync
loop
delete bitmap 2
delete bitmap 1
end
Picture Effects - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/bitmap/picture_effects.dba · 46 lines (32 code)
Loads a picture into a bitmap and transforms it in place with MIRROR BITMAP, FLIP BITMAP, BLUR BITMAP and FADE BITMAP. A second copy of the picture restores the original between effects. Keys 1 to 5 pick an effect; otherwise they cycle by themselves. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Assets. Loads 1 media file(s) from the example folder:
harbour.bmp.
Commands used (20)
View source (46 lines)
rem ==================================================================
rem Picture Effects - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Loads a picture into a bitmap and transforms it in place with
rem MIRROR BITMAP, FLIP BITMAP, BLUR BITMAP and FADE BITMAP. A second
rem copy of the picture restores the original between effects.
rem Keys 1 to 5 pick an effect; otherwise they cycle by themselves.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 30
load bitmap "harbour.bmp", 1
load bitmap "harbour.bmp", 2
set current bitmap 0
effect = 0 : shown = -1
started = timer()
do
rem Pick the effect from the keyboard, or move on every three seconds
for k = 1 to 5
if keystate(k + 1) = 1 then effect = k - 1 : started = timer()
next k
if timer() - started > 3000 then effect = (effect + 1) mod 5 : started = timer()
if effect <> shown
rem Restore the untouched copy, then apply the new effect once
copy bitmap 2, 1
if bitmap mirrored(1) = 1 then mirror bitmap 1
if bitmap flipped(1) = 1 then flip bitmap 1
select effect
case 0 : name$ = "ORIGINAL (COPY BITMAP 2, 1)" : endcase
case 1 : mirror bitmap 1 : name$ = "MIRROR BITMAP 1" : endcase
case 2 : flip bitmap 1 : name$ = "FLIP BITMAP 1" : endcase
case 3 : blur bitmap 1, 6 : name$ = "BLUR BITMAP 1, 6" : endcase
case 4 : fade bitmap 1, 40 : name$ = "FADE BITMAP 1, 40" : endcase
endselect
shown = effect
endif
copy bitmap 1, 0
ink rgb(0, 0, 0), 0
box 0, 0, screen width(), 52
ink rgb(255, 255, 255), 0
text 10, 8, str$(effect + 1) + ". " + name$
text 10, 28, "MIRRORED " + str$(bitmap mirrored(1)) + " FLIPPED " + str$(bitmap flipped(1)) + " keys 1-5 pick an effect"
sync
loop
Postcards - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/bitmap/postcards.dba · 51 lines (34 code)
Loads a picture straight onto the screen, shrinks it into a small bitmap, writes a greeting on it and saves it with SAVE BITMAP. The saved file is loaded back with LOAD BITMAP and dealt out as cards. The temporary file is deleted again. Press a key to deal again. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame with automatic screen refresh. - Assets. Loads 2 media file(s) from the example folder:
harbour.bmp, postcard.bmp.
Commands used (22)
View source (51 lines)
rem ==================================================================
rem Postcards - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Loads a picture straight onto the screen, shrinks it into a small
rem bitmap, writes a greeting on it and saves it with SAVE BITMAP. The
rem saved file is loaded back with LOAD BITMAP and dealt out as cards.
rem The temporary file is deleted again. Press a key to deal again.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
randomize timer()
w = screen width() : h = screen height()
rem LOAD BITMAP without a number loads the picture onto the screen itself
load bitmap "harbour.bmp"
ink rgb(255, 255, 255), 0
text 10, 10, "LOAD BITMAP onto the screen"
sleep 1500
rem Shrink the screen into a half-size bitmap and decorate it there
create bitmap 1, w / 2, h / 2
copy bitmap 0, 0, 0, w, h, 1, 0, 0, w / 2, h / 2
set current bitmap 1
ink rgb(250, 245, 230), 0
for edge = 0 to 5
box edge, edge, w / 2 - 1 - edge, edge
box edge, h / 2 - 1 - edge, w / 2 - 1 - edge, h / 2 - 1 - edge
box edge, edge, edge, h / 2 - 1 - edge
box w / 2 - 1 - edge, edge, w / 2 - 1 - edge, h / 2 - 1 - edge
next edge
ink rgb(255, 255, 255), 0
text 16, h / 2 - 30, "Greetings from the lighthouse"
rem Save it, forget it, and read it back from the file
save bitmap "postcard.bmp", 1
delete bitmap 1
load bitmap "postcard.bmp", 2
delete file "postcard.bmp"
set current bitmap 0
do
cls rgb(40, 60, 80)
rem Each card is a stretched copy of bitmap 2 at a random place and size
for card = 1 to 6
cw = w / 4 + rnd(w / 5) : ch = cw * 3 / 4
x = rnd(w - cw) : y = 40 + rnd(h - 50 - ch)
copy bitmap 2, 0, 0, bitmap width(2), bitmap height(2), 0, x, y, x + cw, y + ch
next card
ink rgb(255, 255, 255), 0
text 10, 10, "postcard.bmp was " + str$(bitmap width(2)) + " x " + str$(bitmap height(2)) + " - press a key to deal again"
suspend for key
loop
Tutorials — Bobs
Aquarium - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/bobs/aquarium.dba · 108 lines (84 code)
Bobs are blitter objects: images drawn onto the screen that put the background back when they move. This program draws its fish with 2D commands in an off-screen bitmap, grabs them with GET IMAGE, round-trips one through SAVE IMAGE and LOAD IMAGE, then lets them swim as bobs. MIRROR BOB turns a fish when it changes direction. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM. - Assets. Loads 1 media file(s) from the example folder:
fish.bmp.
Commands used (40)
View source (108 lines)
rem ==================================================================
rem Aquarium - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Bobs are blitter objects: images drawn onto the screen that put the
rem background back when they move. This program draws its fish with
rem 2D commands in an off-screen bitmap, grabs them with GET IMAGE,
rem round-trips one through SAVE IMAGE and LOAD IMAGE, then lets them
rem swim as bobs. MIRROR BOB turns a fish when it changes direction.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
randomize timer()
w = screen width() : h = screen height()
rem --- Draw the artwork into bitmap 1. Black is transparent for bobs.
create bitmap 1, 256, 64
set current bitmap 1
cls 0
for f = 0 to 2
if f = 0 then body = rgb(255, 140, 30) : fin = rgb(255, 230, 200)
if f = 1 then body = rgb(60, 170, 255) : fin = rgb(255, 230, 60)
if f = 2 then body = rgb(220, 60, 140) : fin = rgb(255, 255, 255)
cx = f * 64 + 36 : cy = 20
rem Tail: a fan of lines behind the body
ink fin, 0
for k = -10 to 10
line cx - 18, cy, cx - 30, cy + k
next k
rem Body: an ellipse filled line by line, then a darker rim
ink body, 0
for dy = -12 to 12
half = 22 * sqrt(1.0 - dy * dy / 144.0)
line cx - half, cy + dy, cx + half, cy + dy
next dy
ink rgb(rgbr(body) / 2, rgbg(body) / 2, rgbb(body) / 2), 0
ellipse cx, cy, 22, 12
ink rgb(255, 255, 255), 0 : box cx + 10, cy - 5, cx + 14, cy - 1
ink rgb(1, 1, 1), 0 : dot cx + 12, cy - 3
get image 1 + f, f * 64, 0, f * 64 + 64, 40
next f
rem Image 4: a bubble
cls 0
ink rgb(200, 230, 255), 0
circle 8, 8, 7
ink rgb(255, 255, 255), 0
dot 5, 5 : dot 6, 5 : dot 5, 6
get image 4, 0, 0, 17, 17
rem --- Save the third fish to a file and read it back as image 5
save image "fish.bmp", 3
load image "fish.bmp", 5
delete file "fish.bmp"
delete bitmap 1
set current bitmap 0
rem --- The tank is drawn once on the screen; bobs restore it as they move
for y = 0 to h - 1
ink rgb(10, 60 + y / 6, 110 + y / 5), 0
line 0, y, w, y
next y
ink rgb(194, 170, 110), 0
box 0, h - 50, w, h
for weed = 1 to 14
wx = rnd(w)
ink rgb(30, 120 + rnd(60), 50), 0
for k = 0 to 90 + rnd(80)
dot wx + sin(k * 6) * 6, h - 50 - k
dot wx + 1 + sin(k * 6) * 6, h - 50 - k
next k
next weed
fish = 8
dim fishx#(fish) : dim fishy(fish) : dim fishspeed#(fish)
for f = 1 to fish
fishx#(f) = rnd(w - 64) : fishy(f) = 40 + rnd(h - 160)
fishspeed#(f) = (1 + rnd(20) / 10.0) * (rnd(1) * 2 - 1)
image = 1 + rnd(2) : if f = fish then image = 5
bob f, fishx#(f), fishy(f), image
rem The art faces right: mirror the fish that start swimming left
if fishspeed#(f) < 0 then mirror bob f
next f
for b = 1 to 6
bob 20 + b, rnd(w), h - 60 - rnd(h), 4
rem SET BOB bob, backsave, transparency: both on is the default
set bob 20 + b, 1, 1
next b
do
for f = 1 to fish
fishx#(f) = fishx#(f) + fishspeed#(f)
if fishx#(f) < 0 or fishx#(f) > w - 64
fishspeed#(f) = 0 - fishspeed#(f)
mirror bob f
endif
bob f, fishx#(f), fishy(f) + sin(fishx#(f) * 2) * 4, bob image(f)
next f
for b = 21 to 26
y = bob y(b) - 2
if y < -20 then y = h - 60
bob b, bob x(b) + sin(y * 3) * 1.2, y, 4
next b
ink rgb(0, 0, 0), 0
box 0, 0, w, 22
ink rgb(255, 255, 255), 0
text 8, 3, "BOB 1: X " + str$(bob x(1)) + " Y " + str$(bob y(1)) + " IMAGE " + str$(bob image(1)) + " " + str$(bob width(1)) + " x " + str$(bob height(1)) + " MIRRORED " + str$(bob mirrored(1)) + " EXIST " + str$(bob exist(1))
sync
loop
Jelly Lab - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/bobs/jelly_lab.dba · 79 lines (59 code)
Shows the bob transforms: OFFSET BOB moves the handle to the middle, SCALE BOB and STRETCH BOB resize, FLIP BOB turns a bob upside down, PASTE BOB stamps a lasting copy and SET BOB with backsave off leaves a painted trail behind a moving bob. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame.
Commands used (34)
View source (79 lines)
rem ==================================================================
rem Jelly Lab - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Shows the bob transforms: OFFSET BOB moves the handle to the middle,
rem SCALE BOB and STRETCH BOB resize, FLIP BOB turns a bob upside down,
rem PASTE BOB stamps a lasting copy and SET BOB with backsave off leaves
rem a painted trail behind a moving bob.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
w = screen width() : h = screen height()
rem --- Artwork: a jellyfish (image 1) and a starfish (image 2)
create bitmap 1, 128, 64
set current bitmap 1
cls 0
for dy = -20 to 0
half = 26 * sqrt(1.0 - dy * dy / 400.0)
ink rgb(230, 120 + dy * 3, 230), 0
line 32 - half, 28 + dy, 32 + half, 28 + dy
next dy
ink rgb(250, 200, 250), 0
for t = 0 to 5
tx = 12 + t * 8
for k = 0 to 28
dot tx + sin(k * 25 + t * 60) * 2, 29 + k
next k
next t
get image 1, 0, 0, 64, 60
cls 0
ink rgb(255, 150, 60), 0
for a = 0 to 359 step 2
r# = 10 + abs(sin(a * 2.5)) * 18
line 32, 32, 32 + cos(a) * r#, 32 + sin(a) * r#
next a
get image 2, 0, 0, 64, 64
delete bitmap 1
set current bitmap 0
rem --- Background
for y = 0 to h - 1
ink rgb(0, 30 + y / 8, 70 + y / 6), 0
line 0, y, w, y
next y
rem PASTE BOB draws a copy into the background itself: it stays there
bob 9, 0, 0, 2
for x = 20 to w - 64 step 90
paste bob 9, x, h - 70
next x
delete bob 9
rem 1: centre handle and SCALE BOB, 2: STRETCH BOB, 3: FLIP BOB,
rem 4: backsave off, so the bob is never erased and paints a trail
for b = 1 to 3
bob b, b * w / 4, 200, 1
offset bob b, 32, 30
next b
bob 4, 40, 330, 2
set bob 4, 0, 1
do
t = t + 1
scale bob 1, 100 + sin(t * 3) * 40
stretch bob 2, 100 - sin(t * 6) * 25, 100 + sin(t * 6) * 35
if t mod 90 = 0 then flip bob 3
bob 1, w / 4, 200 + sin(t * 2) * 30, 1
bob 2, w / 2, 200 + sin(t * 2 + 120) * 30, 1
bob 3, 3 * w / 4, 200 + sin(t * 2 + 240) * 30, 1
bob 4, 40 + (t * 2) mod (w - 100), 330 + sin(t * 4) * 20, 2
ink rgb(0, 0, 0), 0
box 0, 0, w, 64
ink rgb(255, 255, 255), 0
text 8, 4, "1 OFFSET " + str$(bob offset x(1)) + "," + str$(bob offset y(1)) + " SCALE " + str$(bob scale x(1)) + "%"
text 8, 24, "2 STRETCH " + str$(bob scale x(2)) + "% x " + str$(bob scale y(2)) + "% 3 FLIPPED " + str$(bob flipped(3))
text 8, 44, "4 has backsave off and paints a trail; the starfish below were PASTE BOB stamps"
sync
loop
Star Catch - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/bobs/star_catch.dba · 99 lines (77 code)
A small game with bobs: move the basket with the arrow keys and catch the falling stars. BOB HIT reports a new catch, DELETE BOB removes the star and BOB EXIST tells when it must fall again. Without a key press the basket moves by itself. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM.
Commands used (31)
View source (99 lines)
rem ==================================================================
rem Star Catch - an OpenDBC tutorial for the DarkBASIC Classic profile
rem A small game with bobs: move the basket with the arrow keys and
rem catch the falling stars. BOB HIT reports a new catch, DELETE BOB
rem removes the star and BOB EXIST tells when it must fall again.
rem Without a key press the basket moves by itself.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
randomize timer()
w = screen width() : h = screen height()
rem --- Artwork: a star (image 1) and a basket (image 2)
create bitmap 1, 128, 64
set current bitmap 1
cls 0
ink rgb(255, 230, 80), 0
for a = 0 to 359
r# = 6 + 10 * abs(cos(a * 2.5))
line 16, 16, 16 + sin(a) * r#, 16 - cos(a) * r#
next a
get image 1, 0, 0, 33, 33
cls 0
ink rgb(150, 90, 40), 0
box 0, 8, 79, 31
ink rgb(200, 140, 70), 0
for x = 2 to 77 step 6
line x, 9, x + 3, 30
next x
box 0, 6, 79, 9
get image 2, 0, 0, 80, 32
delete bitmap 1
set current bitmap 0
rem --- Night sky background
for y = 0 to h - 1
ink rgb(10, 10, 30 + y / 8), 0
line 0, y, w, y
next y
for s = 1 to 150
c = 80 + rnd(120)
ink rgb(c, c, c), 0
dot rnd(w), rnd(h)
next s
stars = 5
dim speed#(stars) : dim sy#(stars)
basketx# = w / 2 - 40
bob 1, basketx#, h - 50, 2
score = 0 : missed = 0
do
rem A missing star is dropped again from the top
for s = 1 to stars
if bob exist(10 + s) = 0
sy#(s) = -40 - rnd(300)
speed#(s) = 1.5 + rnd(25) / 10.0
bob 10 + s, rnd(w - 33), sy#(s), 1
endif
next s
rem Arrow keys, or follow the lowest star
move = rightkey() - leftkey()
if move = 0
target = 0 : lowest# = -1000
for s = 1 to stars
if sy#(s) > lowest# then lowest# = sy#(s) : target = s
next s
if target > 0
gap = bob x(10 + target) + 16 - (basketx# + 40)
if gap > 4 then move = 1
if gap < -4 then move = -1
endif
endif
basketx# = basketx# + move * 6
if basketx# < 0 then basketx# = 0
if basketx# > w - 80 then basketx# = w - 80
bob 1, basketx#, h - 50, 2
for s = 1 to stars
sy#(s) = sy#(s) + speed#(s)
bob 10 + s, bob x(10 + s), sy#(s), 1
if sy#(s) > h then delete bob 10 + s : missed = missed + 1
next s
rem BOB HIT returns the bob the basket has just touched (0 for none)
caught = bob hit(1, 0)
if caught > 10
delete bob caught
score = score + 1
endif
ink rgb(0, 0, 0), 0
box 0, 0, w, 22
ink rgb(255, 255, 255), 0
text 8, 3, "CAUGHT " + str$(score) + " MISSED " + str$(missed) + " BASKET TOUCHING " + str$(bob collision(1, 0))
sync
loop
Tutorials — Files
Explorer - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/files/explorer.dba · 78 lines (58 code)
Looks around the folders: DIR and DRIVELIST print listings, PERFORM CHECKLIST FOR FILES fills a list to read back, FIND FIRST and FIND NEXT walk through the files one at a time, and CD moves between folders. UP and DOWN select a file; E opens the selected file with EXECUTE FILE, which hands it to the program your system uses for it. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM.
Commands used (32)
View source (78 lines)
rem ==================================================================
rem Explorer - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Looks around the folders: DIR and DRIVELIST print listings,
rem PERFORM CHECKLIST FOR FILES fills a list to read back, FIND FIRST
rem and FIND NEXT walk through the files one at a time, and CD moves
rem between folders. UP and DOWN select a file; E opens the selected
rem file with EXECUTE FILE, which hands it to the program your system
rem uses for it.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
rem DIR and DRIVELIST print straight onto the screen, like PRINT
cls rgb(20, 24, 34)
ink rgb(220, 220, 230), 0
print "DIR of " + get dir$()
dir
print
print "DRIVELIST"
drivelist
sleep 2500
sync on : sync rate 30
home$ = get dir$()
rem The parent folder through CD, then back again
cd ".."
parent$ = get dir$()
perform checklist for files
parent_entries = checklist quantity()
cd home$
rem The files here, from FIND FIRST and FIND NEXT
dim name$(60) : dim kind(60) : dim date$(60)
count = 0
find first
while get file type() <> -1 and count < 60
inc count
name$(count) = get file name$() : kind(count) = get file type() : date$(count) = get file date$()
find next
endwhile
perform checklist for drives
drives = checklist quantity()
selected = 1
do
if downkey() = 1 and held = 0 and selected < count then inc selected
if upkey() = 1 and held = 0 and selected > 1 then dec selected
held = upkey() + downkey()
key$ = lower$(inkey$())
if key$ = "e" and lastkey$ <> "e" and kind(selected) = 0
rem EXECUTE FILE file$, command line$, folder$
execute file name$(selected), "", home$
endif
lastkey$ = key$
cls rgb(20, 24, 34)
ink rgb(255, 210, 120), 0
text 16, 10, "EXPLORER - " + home$
ink rgb(160, 160, 180), 0
text 16, 30, "PATH EXIST(""..""): " + str$(path exist("..")) + " parent has " + str$(parent_entries) + " entries " + str$(drives) + " drive(s)"
for i = 1 to count
y = 50 + i * 20
if y < screen height() - 30
if i = selected
ink rgb(60, 80, 130), 0 : box 12, y - 2, screen width() - 12, y + 17
endif
if kind(i) = 1 then ink rgb(120, 200, 255), 0 else ink rgb(230, 230, 230), 0
if kind(i) = 1 then label$ = "[" + name$(i) + "]" else label$ = name$(i)
text 20, y, label$
ink rgb(150, 150, 170), 0
if kind(i) = 0 then text 300, y, str$(file size(name$(i))) + " bytes"
text 430, y, date$(i)
endif
next i
ink rgb(150, 150, 170), 0
text 16, screen height() - 24, "UP/DOWN select, E opens a file with EXECUTE FILE"
sync
loop
Notebook - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/files/notebook.dba · 83 lines (62 code)
Works with files in a scratch folder: writes a record of bytes, words, longs, floats and strings, reads it back value by value, then copies, renames, moves and deletes files. Each step is logged on the screen. The scratch folder is removed at the end. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 2 GOSUB subroutine(s):
add_line, tidy_up. - Data. 1 array(s) declared with
DIM.
Commands used (41)
View source (83 lines)
rem ==================================================================
rem Notebook - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Works with files in a scratch folder: writes a record of bytes,
rem words, longs, floats and strings, reads it back value by value,
rem then copies, renames, moves and deletes files. Each step is logged
rem on the screen. The scratch folder is removed at the end.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
dim log$(40) : lines = 0
rem --- A scratch folder next to the program, entered with SET DIR
if path exist("notebook_tmp") = 1 then gosub tidy_up
make directory "notebook_tmp"
set dir "notebook_tmp"
note$ = "MAKE DIRECTORY and SET DIR: now in ..." + right$(get dir$(), 30) : gosub add_line
rem --- Writing: each WRITE command appends one value to the open file
open to write 1, "record.dat"
write byte 1, 200
write word 1, 1999
write long 1, 123456789
write float 1, 3.25
write string 1, "lighthouse"
write file 1, -42
note$ = "FILE OPEN(1) while writing = " + str$(file open(1)) : gosub add_line
close file 1
note$ = "record.dat written: FILE EXIST = " + str$(file exist("record.dat")) + ", FILE SIZE = " + str$(file size("record.dat")) + " bytes" : gosub add_line
rem --- Reading: the values come back in the order they were written
open to read 2, "record.dat"
read byte 2, b
read word 2, wd
read long 2, l
read float 2, f#
read string 2, s$
read file 2, q
note$ = "READ BYTE " + str$(b) + " WORD " + str$(wd) + " LONG " + str$(l) + " FLOAT " + str$(f#) : gosub add_line
note$ = "READ STRING """ + s$ + """ READ FILE " + str$(q) + " FILE END = " + str$(file end(2)) : gosub add_line
close file 2
note$ = "CLOSE FILE: FILE OPEN(2) = " + str$(file open(2)) : gosub add_line
rem --- Managing files
make file "empty.txt"
note$ = "MAKE FILE empty.txt: size " + str$(file size("empty.txt")) : gosub add_line
copy file "record.dat", "backup.dat"
note$ = "COPY FILE record.dat to backup.dat: " + str$(file exist("backup.dat")) : gosub add_line
rename file "backup.dat", "old_record.dat"
note$ = "RENAME FILE to old_record.dat: backup.dat exists " + str$(file exist("backup.dat")) : gosub add_line
make directory "archive"
move file "old_record.dat", "archive/old_record.dat"
note$ = "MOVE FILE into archive: " + str$(file exist("archive/old_record.dat")) : gosub add_line
gosub tidy_up
note$ = "DELETE FILE and DELETE DIRECTORY: notebook_tmp exists " + str$(path exist("notebook_tmp")) : gosub add_line
shown = 0
do
if shown < lines and timer() - last > 250 then inc shown : last = timer()
cls rgb(250, 246, 232)
ink rgb(70, 60, 50), 0
center text screen width() / 2, 14, "NOTEBOOK - files step by step"
for i = 1 to shown
if i = shown then ink rgb(180, 60, 30), 0 else ink rgb(50, 50, 60), 0
text 20, 30 + i * 22, str$(i) + ". " + log$(i)
next i
sync
loop
add_line:
inc lines
log$(lines) = note$
return
rem Leave the scratch folder and remove everything in it
tidy_up:
if right$(get dir$(), 12) = "notebook_tmp" then set dir ".."
if file exist("notebook_tmp/archive/old_record.dat") = 1 then delete file "notebook_tmp/archive/old_record.dat"
if path exist("notebook_tmp/archive") = 1 then delete directory "notebook_tmp/archive"
if file exist("notebook_tmp/record.dat") = 1 then delete file "notebook_tmp/record.dat"
if file exist("notebook_tmp/empty.txt") = 1 then delete file "notebook_tmp/empty.txt"
if path exist("notebook_tmp") = 1 then delete directory "notebook_tmp"
return
Packer - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/files/packer.dba · 117 lines (93 code)
Packs files and folders into one archive and unpacks them again: WRITE FILEBLOCK and READ FILEBLOCK store a whole file, WRITE DIRBLOCK and READ DIRBLOCK a whole folder, WRITE MEMBLOCK and READ MEMBLOCK a memblock, and MAKE MEMBLOCK FROM FILE loads raw bytes for a hex view. K copies a line to the clipboard and V reads the clipboard back. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 2 GOSUB subroutine(s):
add_line, tidy_up. - Data. 1 array(s) declared with
DIM.
Commands used (39)
View source (117 lines)
rem ==================================================================
rem Packer - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Packs files and folders into one archive and unpacks them again:
rem WRITE FILEBLOCK and READ FILEBLOCK store a whole file, WRITE DIRBLOCK
rem and READ DIRBLOCK a whole folder, WRITE MEMBLOCK and READ MEMBLOCK a
rem memblock, and MAKE MEMBLOCK FROM FILE loads raw bytes for a hex view.
rem K copies a line to the clipboard and V reads the clipboard back.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
dim log$(20) : lines = 0
if path exist("packer_tmp") = 1 then gosub tidy_up
make directory "packer_tmp"
make directory "packer_tmp/photos"
rem Some files to pack: a note and two tiny "photos"
open to write 1, "packer_tmp/note.txt" : write string 1, "Pack me with the photos" : close file 1
for p = 1 to 2
open to write 1, "packer_tmp/photos/photo" + str$(p) + ".raw"
for b = 1 to 32 : write byte 1, p * 40 + b : next b
close file 1
next p
make memblock 1, 16
for b = 0 to 15 : write memblock byte 1, b, b * 16 : next b
rem --- Pack: one archive file holds a file, a folder and a memblock
open to write 2, "packer_tmp/archive.pak"
write fileblock 2, "packer_tmp/note.txt"
write dirblock 2, "packer_tmp/photos"
write memblock 2, 1
close file 2
note$ = "archive.pak: " + str$(file size("packer_tmp/archive.pak")) + " bytes" : gosub add_line
rem --- Unpack into another folder
make directory "packer_tmp/out"
open to read 3, "packer_tmp/archive.pak"
read fileblock 3, "packer_tmp/out/note.txt"
read dirblock 3, "packer_tmp/out/photos"
read memblock 3, 2
close file 3
note$ = "READ FILEBLOCK: out/note.txt " + str$(file size("packer_tmp/out/note.txt")) + " bytes" : gosub add_line
note$ = "READ DIRBLOCK: out/photos/photo2.raw exists " + str$(file exist("packer_tmp/out/photos/photo2.raw")) : gosub add_line
note$ = "READ MEMBLOCK: memblock 2 has " + str$(get memblock size(2)) + " bytes, byte 15 = " + str$(memblock byte(2, 15)) : gosub add_line
rem --- Raw bytes of the archive for the hex view
open to read 4, "packer_tmp/archive.pak"
make memblock from file 3, 4
close file 4
gosub tidy_up
note$ = "Scratch folder removed: " + str$(path exist("packer_tmp")) : gosub add_line
do
key$ = lower$(inkey$())
if key$ = "k" and lastkey$ <> "k" then write to clipboard "Packed by OpenDBC" : note$ = "WRITE TO CLIPBOARD done" : gosub add_line
if key$ = "v" and lastkey$ <> "v" then note$ = "GET CLIPBOARD$ = """ + left$(get clipboard$(), 40) + """" : gosub add_line
lastkey$ = key$
cls rgb(24, 30, 26)
ink rgb(150, 255, 170), 0
text 16, 10, "PACKER - the first bytes of archive.pak"
rem Hex dump: sixteen bytes per row, then the printable characters
for row = 0 to 7
line$ = right$("000" + hex$(row * 16), 4) + " "
chars$ = ""
for col = 0 to 15
offset = row * 16 + col
if offset < get memblock size(3)
value = memblock byte(3, offset)
line$ = line$ + right$("0" + hex$(value), 2) + " "
if value >= 32 and value < 127 then chars$ = chars$ + chr$(value) else chars$ = chars$ + "."
endif
next col
ink rgb(200, 230, 200), 0
text 16, 40 + row * 20, line$
text 470, 40 + row * 20, chars$
next row
ink rgb(230, 230, 230), 0
for i = 1 to lines
text 16, 220 + i * 20, log$(i)
next i
ink rgb(150, 170, 150), 0
text 16, screen height() - 24, "K writes to the clipboard, V reads it back"
sync
loop
add_line:
rem Keep the last ten lines: drop the oldest when the log is full
if lines = 10
for i = 1 to 9 : log$(i) = log$(i + 1) : next i
lines = 9
endif
inc lines
log$(lines) = note$
return
tidy_up:
for p = 1 to 2
for folder = 0 to 1
if folder = 0 then f$ = "packer_tmp/photos/photo" + str$(p) + ".raw" else f$ = "packer_tmp/out/photos/photo" + str$(p) + ".raw"
if file exist(f$) = 1 then delete file f$
next folder
next p
for i = 1 to 4
if i = 1 then f$ = "packer_tmp/note.txt"
if i = 2 then f$ = "packer_tmp/out/note.txt"
if i = 3 then f$ = "packer_tmp/archive.pak"
if i = 4 then f$ = ""
if f$ <> ""
if file exist(f$) = 1 then delete file f$
endif
next i
if path exist("packer_tmp/out/photos") = 1 then delete directory "packer_tmp/out/photos"
if path exist("packer_tmp/out") = 1 then delete directory "packer_tmp/out"
if path exist("packer_tmp/photos") = 1 then delete directory "packer_tmp/photos"
if path exist("packer_tmp") = 1 then delete directory "packer_tmp"
return
Tutorials — Input
Joystick Lab - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/input/joystick_lab.dba · 80 lines (59 code)
Lists the game controllers, picks the first one with SET CONTROL DEVICE and shows every axis, button, slider and hat it reports. Buttons A to D play force-feedback effects on controllers that have them; without a controller every reading stays at zero. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM.
Commands used (53)
View source (80 lines)
rem ==================================================================
rem Joystick Lab - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Lists the game controllers, picks the first one with SET CONTROL
rem DEVICE and shows every axis, button, slider and hat it reports.
rem Buttons A to D play force-feedback effects on controllers that have
rem them; without a controller every reading stays at zero.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
w = screen width() : h = screen height()
perform checklist for control devices
devices = checklist quantity()
dim device$(8)
for d = 1 to devices
if d <= 8 then device$(d) = checklist string$(d)
next d
if devices > 0 then set control device device$(1)
force auto center on
do
rem Force feedback, one effect per button
if joystick fire a() = 1 and fired = 0 then force shoot 100, 200 : fired = 1
if joystick fire b() = 1 and fired = 0 then force impact 100, 300 : fired = 1
if joystick fire c() = 1 and fired = 0 then force chainsaw 60, 500 : fired = 1
if joystick fire d() = 1 and fired = 0 then force water effect 80, 800 : fired = 1
if joystick fire a() + joystick fire b() + joystick fire c() + joystick fire d() = 0 and fired = 1
force no effect : fired = 0
endif
rem The stick pushes back against the direction it is held
if joystick left() = 1 then force right 40
if joystick right() = 1 then force left 40
if joystick up() = 1 then force down 40
if joystick down() = 1 then force up 40
if joystick x() = 0 and joystick y() = 0 and joystick fire x(4) = 1 then force angle 50, 90, 300
rem Buttons 5 and 6 release and restore the stick's self-centring spring
if joystick fire x(5) = 1 then force auto center off
if joystick fire x(6) = 1 then force auto center on
cls rgb(26, 28, 36)
ink rgb(255, 210, 120), 0
center text w / 2, 12, "JOYSTICK LAB"
ink rgb(220, 220, 230), 0
if devices = 0
text 30, 40, "No game controller found - plug one in and run the program again."
else
text 30, 40, str$(devices) + " controller(s); using " + control device name$()
endif
rem The stick: X and Y from -1000 to 1000 move the dot inside the square
ink rgb(60, 64, 84), 0
box 30, 80, 230, 280
ink rgb(255, 200, 60), 0
cx = 130 + joystick x() / 10 : cy = 180 + joystick y() / 10
for r = 1 to 8 : circle cx, cy, r : next r
ink rgb(220, 220, 230), 0
text 30, 290, "X " + str$(joystick x()) + " Y " + str$(joystick y()) + " Z " + str$(joystick z())
text 30, 310, "DEVICE X " + str$(control device x()) + " Y " + str$(control device y()) + " Z " + str$(control device z())
text 30, 330, "UP " + str$(joystick up()) + " DOWN " + str$(joystick down()) + " LEFT " + str$(joystick left()) + " RIGHT " + str$(joystick right())
rem Buttons: the four named ones and the first sixteen by number
ink rgb(220, 220, 230), 0
text 280, 80, "FIRE A " + str$(joystick fire a()) + " B " + str$(joystick fire b()) + " C " + str$(joystick fire c()) + " D " + str$(joystick fire d())
for b = 0 to 15
if joystick fire x(b) = 1 then ink rgb(255, 120, 60), 0 else ink rgb(60, 64, 84), 0
box 280 + (b mod 8) * 40, 110 + (b / 8) * 40, 312 + (b mod 8) * 40, 142 + (b / 8) * 40
ink rgb(255, 255, 255), 0
text 286 + (b mod 8) * 40, 118 + (b / 8) * 40, str$(b)
next b
rem Sliders, twists and the hat
text 280, 210, "SLIDER A " + str$(joystick slider a()) + " B " + str$(joystick slider b())
text 280, 230, "SLIDER C " + str$(joystick slider c()) + " D " + str$(joystick slider d())
text 280, 250, "TWIST X " + str$(joystick twist x()) + " Y " + str$(joystick twist y()) + " Z " + str$(joystick twist z())
text 280, 270, "HAT ANGLE " + str$(joystick hat angle(0)) + " (-1 when centred)"
ink rgb(150, 150, 170), 0
text 30, h - 40, "Buttons A-D: shoot, impact, chainsaw and water forces; the stick pushes back"
sync
loop
Keyboard Lab - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/input/keyboard_lab.dba · 85 lines (62 code)
Every way of reading the keyboard: KEYSTATE for any key by scancode, the named key functions, INKEY$ and SCANCODE for the key held now, ENTRY$ for everything typed since the last CLEAR ENTRY BUFFER, and INPUT, which stops the program until RETURN. ESCAPE is disabled here so that it can be shown as a key: TAB asks your name with INPUT, F10 quits. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM; constant tables in DATA blocks read with READ.
Commands used (34)
View source (85 lines)
rem ==================================================================
rem Keyboard Lab - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Every way of reading the keyboard: KEYSTATE for any key by scancode,
rem the named key functions, INKEY$ and SCANCODE for the key held now,
rem ENTRY$ for everything typed since the last CLEAR ENTRY BUFFER, and
rem INPUT, which stops the program until RETURN.
rem ESCAPE is disabled here so that it can be shown as a key: TAB asks
rem your name with INPUT, F10 quits.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
disable escapekey
w = screen width() : h = screen height()
rem Three rows of a keyboard: a label and its scancode for each key
dim label$(40) : dim code(40) : dim rowkeys(3)
keys = 0
restore
for row = 1 to 3
read count
rowkeys(row) = count
for k = 1 to count
inc keys
read label$(keys), code(keys)
next k
next row
name$ = "nobody yet"
clear entry buffer
do
if keystate(68) = 1 then exit
if keystate(15) = 1
rem INPUT waits for RETURN; everything else stops meanwhile
sync off
cls rgb(20, 20, 30)
set cursor 40, 200
input "What is your name? ", name$
sync on
clear entry buffer
endif
if returnkey() = 1 then clear entry buffer
cls rgb(30, 32, 44)
rem The keyboard: each key lights up while KEYSTATE(scancode) is 1
k = 1
for row = 0 to 2
for col = 0 to rowkeys(row + 1) - 1
x = 40 + col * 56 + row * 18 : y = 90 + row * 56
if keystate(code(k)) = 1 then ink rgb(255, 190, 60), 0 else ink rgb(70, 76, 100), 0
box x, y, x + 48, y + 48
ink rgb(255, 255, 255), 0
text x + 6, y + 4, label$(k)
ink rgb(170, 170, 190), 0
text x + 6, y + 28, str$(code(k))
inc k
next col
next row
rem The named key functions
y = 270
ink rgb(230, 230, 230), 0
text 40, y, "UPKEY " + str$(upkey()) + " DOWNKEY " + str$(downkey()) + " LEFTKEY " + str$(leftkey()) + " RIGHTKEY " + str$(rightkey())
text 40, y + 20, "SPACEKEY " + str$(spacekey()) + " RETURNKEY " + str$(returnkey()) + " SHIFTKEY " + str$(shiftkey()) + " CONTROLKEY " + str$(controlkey()) + " ESCAPEKEY " + str$(escapekey())
text 40, y + 50, "INKEY$ = """ + inkey$() + """ SCANCODE() = " + str$(scancode())
rem ENTRY$ collects the typing; RETURN empties it with CLEAR ENTRY BUFFER
ink rgb(20, 20, 26), 0
box 40, y + 80, w - 40, y + 110
ink rgb(120, 230, 160), 0
text 48, y + 86, entry$() + "_"
ink rgb(170, 170, 190), 0
text 40, y + 120, "Type anything: ENTRY$ keeps it until RETURN clears the buffer"
text 40, y + 150, "Your name: " + name$ + " (TAB asks with INPUT, F10 quits)"
ink rgb(255, 210, 120), 0
center text w / 2, 30, "KEYBOARD LAB"
sync
loop
enable escapekey
end
rem Row: number of keys, then label and scancode pairs
data 10, "Q", 16, "W", 17, "E", 18, "R", 19, "T", 20, "Y", 21, "U", 22, "I", 23, "O", 24, "P", 25
data 9, "A", 30, "S", 31, "D", 32, "F", 33, "G", 34, "H", 35, "J", 36, "K", 37, "L", 38
data 7, "Z", 44, "X", 45, "C", 46, "V", 47, "B", 48, "N", 49, "M", 50
Mouse Lab - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/input/mouse_lab.dba · 72 lines (53 code)
A painting program: the left button paints, the right button paints white, both together clear the canvas. MOUSEMOVEX and MOUSEMOVEY tell how fast the mouse moves, and the brush grows with speed. The system pointer is hidden and replaced by a drawn crosshair; M shows it again, C puts it back in the centre with POSITION MOUSE. Until the mouse moves, an invisible pen paints by itself. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame.
Commands used (24)
View source (72 lines)
rem ==================================================================
rem Mouse Lab - an OpenDBC tutorial for the DarkBASIC Classic profile
rem A painting program: the left button paints, the right button paints
rem white, both together clear the canvas. MOUSEMOVEX and MOUSEMOVEY
rem tell how fast the mouse moves, and the brush grows with speed.
rem The system pointer is hidden and replaced by a drawn crosshair;
rem M shows it again, C puts it back in the centre with POSITION MOUSE.
rem Until the mouse moves, an invisible pen paints by itself.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
w = screen width() : h = screen height()
hide mouse
cls rgb(250, 248, 240)
lastx = -1
hue = 0
do
mx = mousex() : my = mousey()
dx = mousemovex() : dy = mousemovey()
if dx <> 0 or dy <> 0 then moved = 1
rem The pen: the mouse, or a slow figure of eight until the mouse moves
if moved = 0
t# = t# + 3.0
penx = w / 2 + sin(t#) * 220 : peny = h / 2 + sin(t# * 2) * 120
button = 1
speed = 3
else
penx = mx : peny = my
button = mouseclick()
speed = sqrt(dx * dx + dy * dy)
endif
rem Button values add up: 1 left, 2 right, 3 both
if button = 3 then cls rgb(250, 248, 240)
if (button = 1 or button = 2) and lastx >= 0
hue = (hue + 2) mod 360
if button = 1
ink rgb(128 + sin(hue) * 127, 128 + sin(hue + 120) * 127, 128 + sin(hue + 240) * 127), 0
else
ink rgb(250, 248, 240), 0
endif
size = 2 + speed / 3
if size > 20 then size = 20
for r = 0 to size
line lastx, lasty + r - size / 2, penx, peny + r - size / 2
next r
endif
lastx = penx : lasty = peny
key$ = lower$(inkey$())
if key$ = "m" and lastkey$ <> "m"
shown = 1 - shown
if shown = 1 then show mouse else hide mouse
endif
if key$ = "c" and lastkey$ <> "c" then position mouse w / 2, h / 2
lastkey$ = key$
rem The status bar and a crosshair drawn where the mouse is
ink rgb(40, 40, 50), 0
box 0, 0, w, 22
ink rgb(255, 255, 255), 0
text 6, 3, "MOUSEX " + str$(mx) + " MOUSEY " + str$(my) + " MOVE " + str$(dx) + "," + str$(dy) + " CLICK " + str$(mouseclick()) + " M pointer, C centre"
if moved = 1
ink rgb(0, 0, 0), 0
line mx - 8, my, mx - 3, my : line mx + 3, my, mx + 8, my
line mx, my - 8, mx, my - 3 : line mx, my + 3, mx, my + 8
endif
sync
loop
Tutorials — Language
High Scores - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/language/high_scores.dba · 82 lines (57 code)
Keeps a table of ten scores in arrays: reads the starting table from DATA, slots in a new score, saves the scores to a file, wipes them and loads them back. UNDIM frees a temporary array. Formatting functions come from score_helpers.dba through #INCLUDE. Start it with a name on the command line (opendbc run high_scores.dba --dialect dbc --command-line ADA) to see that name in the table; CL$() returns it. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 2 array(s) declared with
DIM; constant tables in DATA blocks read with READ; pulls in score_helpers.dba via #INCLUDE.
Commands used (26)
View source (82 lines)
rem ==================================================================
rem High Scores - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Keeps a table of ten scores in arrays: reads the starting table from
rem DATA, slots in a new score, saves the scores to a file, wipes them
rem and loads them back. UNDIM frees a temporary array. Formatting
rem functions come from score_helpers.dba through #INCLUDE.
rem Start it with a name on the command line
rem (opendbc run high_scores.dba --dialect dbc --command-line ADA) to
rem see that name in the table; CL$() returns it.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
#include "score_helpers.dba"
sync on : sync rate 30
randomize timer()
player$ = upper$(cl$())
if player$ = "" then player$ = "YOU"
rem --- The starting table, read from DATA
dim names$(9) : dim scores(9)
for i = 0 to 9
read names$(i), scores(i)
next i
rem --- A new score slides into place, pushing lower scores down. A copy
rem of the old table tells how many players were pushed down.
dim previous(9)
for i = 0 to 9 : previous(i) = scores(i) : next i
new_score = 4000 + rnd(5000)
place = 10
for i = 9 to 0 step -1
if new_score > scores(i) then place = i
next i
if place < 10
for i = 9 to place + 1 step -1
names$(i) = names$(i - 1) : scores(i) = scores(i - 1)
next i
names$(place) = player$ : scores(place) = new_score
endif
pushed = 0
for i = 0 to 9
if scores(i) <> previous(i) then inc pushed
next i
if place < 10 then dec pushed
rem The copy is no longer needed: UNDIM frees it
undim previous(0)
rem --- SAVE ARRAY writes a numeric array to a file: save the scores,
rem wipe them, and load them back
save array "scores.dat", scores(0)
for i = 0 to 9 : scores(i) = 0 : next i
load array "scores.dat", scores(0)
delete file "scores.dat"
do
cls rgb(16, 18, 40)
set text font "Courier New" : set text size 22
ink rgb(255, 210, 80), 0
center text screen width() / 2, 20, "HALL OF FAME"
for i = 0 to 9
if i = place and (timer() / 300) mod 2 = 0
ink rgb(255, 255, 255), 0
else
ink rgb(120 + i * 12, 180, 255 - i * 10), 0
endif
text 120, 70 + i * 32, pad$(ordinal$(i + 1), 6) + pad$(names$(i), 10) + zeros$(scores(i), 6)
next i
set text font "Arial" : set text size 14
ink rgb(160, 160, 190), 0
if place < 10
text 20, screen height() - 46, player$ + " scored " + str$(new_score) + ", placed " + ordinal$(place + 1) + " and pushed " + str$(pushed) + " players down"
else
text 20, screen height() - 46, player$ + " scored " + str$(new_score) + ", not enough for the table"
endif
text 20, screen height() - 26, "Table saved and reloaded on " + get date$() + " at " + get time$()
sync
loop
data "ROSA", 9800, "MAX", 9100, "INES", 8400, "TOM", 7700, "LEA", 6900
data "OMAR", 6100, "JUNE", 5200, "KAI", 4300, "NOOR", 3500, "ELI", 2600
Maze Walker - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/language/maze_walker.dba · 139 lines (108 code)
A tour of the language: the maze is stored in DATA lines and READ into an array, a FUNCTION answers questions about it, SELECT CASE picks a direction, FOR, WHILE, REPEAT and DO loops do the work and GOSUB and GOTO move between parts of the program. The walker keeps its right hand on the wall until it finds the exit. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 user function(s):
is_open; 4 GOSUB subroutine(s): restart, read_maze, draw_maze, celebrate. - Data. 3 array(s) declared with
DIM; constant tables in DATA blocks read with READ.
Commands used (17)
View source (139 lines)
rem ==================================================================
rem Maze Walker - an OpenDBC tutorial for the DarkBASIC Classic profile
rem A tour of the language: the maze is stored in DATA lines and READ
rem into an array, a FUNCTION answers questions about it, SELECT CASE
rem picks a direction, FOR, WHILE, REPEAT and DO loops do the work and
rem GOSUB and GOTO move between parts of the program.
rem The walker keeps its right hand on the wall until it finds the exit.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
remstart
The maze is 21 x 15 characters: # is a wall, . a corridor, S the
start and E the exit. Every corridor joins every other one by a
single path, so following the right-hand wall always reaches E.
remend
sync on : sync rate 20
columns = 21 : rows = 15 : cell = 28
originx = (screen width() - columns * cell) / 2 : originy = 50
dim maze(columns - 1, rows - 1)
rem A function only sees its own variables, but arrays are shared:
rem the maze size travels to is_open() through this one
dim size(1) : size(0) = columns : size(1) = rows
rem Directions 0 to 3: up, right, down, left
dim dx(3) : dim dy(3)
dx(1) = 1 : dx(3) = -1 : dy(0) = -1 : dy(2) = 1
gosub read_maze
restart:
x = startx : y = starty : heading = 1 : steps = 0
gosub draw_maze
do
rem Try right, ahead, left, then back, and take the first open way
for attempt = 0 to 3
select attempt
case 0 : try = (heading + 1) mod 4 : endcase
case 1 : try = heading : endcase
case 2 : try = (heading + 3) mod 4 : endcase
case default : try = (heading + 2) mod 4 : endcase
endselect
if is_open(x + dx(try), y + dy(try)) = 1 then heading = try : exit
next attempt
rem Repaint the cell the walker leaves with a breadcrumb, then move
ink rgb(32, 36, 48), 0
box originx + x * cell, originy + y * cell, originx + x * cell + cell - 1, originy + y * cell + cell - 1
ink rgb(90, 140, 200), 0
box originx + x * cell + 11, originy + y * cell + 11, originx + x * cell + cell - 12, originy + y * cell + cell - 12
x = x + dx(heading) : y = y + dy(heading)
inc steps
ink rgb(255, 210, 60), 0
for r = 1 to 9
circle originx + x * cell + cell / 2, originy + y * cell + cell / 2, r
next r
ink rgb(0, 0, 0), 0
box 0, 0, screen width(), 40
ink rgb(255, 255, 255), 0
text 10, 10, "STEPS " + str$(steps) + " right-hand rule"
if maze(x, y) = 3
gosub celebrate
rem Read the maze again from the first DATA line and start over
restore
gosub read_maze
goto restart
endif
sync
loop
rem Subroutines share every variable with the main program, so they use
rem their own loop counters (gx, gy) and leave the walker's x and y alone
read_maze:
gy = 0
while gy < rows
read row$
for gx = 0 to columns - 1
c$ = mid$(row$, gx + 1)
select c$
case "#" : maze(gx, gy) = 1 : endcase
case "S" : maze(gx, gy) = 2 : startx = gx : starty = gy : endcase
case "E" : maze(gx, gy) = 3 : endcase
case default : maze(gx, gy) = 0 : endcase
endselect
next gx
inc gy
endwhile
return
draw_maze:
cls rgb(20, 22, 30)
for gy = 0 to rows - 1
for gx = 0 to columns - 1
if maze(gx, gy) = 1
ink rgb(70, 80, 110), 0
else
ink rgb(32, 36, 48), 0
endif
if maze(gx, gy) = 3 then ink rgb(60, 200, 110), 0
box originx + gx * cell, originy + gy * cell, originx + gx * cell + cell - 1, originy + gy * cell + cell - 1
next gx
next gy
return
celebrate:
flashes = 0
repeat
if flashes mod 2 = 0 then ink rgb(60, 200, 110), 0 else ink rgb(255, 255, 255), 0
center text screen width() / 2, originy + rows * cell + 8, "EXIT FOUND IN " + str$(steps) + " STEPS"
sync
sleep 150
inc flashes
until flashes = 8
return
rem 1 when the cell is inside the maze and not a wall
function is_open(cx, cy)
if cx < 0 or cy < 0 or cx >= size(0) or cy >= size(1) then exitfunction 0
passable = 1
if maze(cx, cy) = 1 then passable = 0
endfunction passable
data "#####################"
data "#S#.......#.........#"
data "#.###.###.#.#.#######"
data "#...#...#...#.......#"
data "###.#.#############.#"
data "#.#.#.#...#.......#.#"
data "#.#.###.#.#.#####.#.#"
data "#.#.....#...#.....#.#"
data "#.###########.#####.#"
data "#...........#...#...#"
data "#.###.#####.###.#.#.#"
data "#...#.....#...#...#.#"
data "###.#####.#########.#"
data "#.......#..........E#"
data "#####################"
Score Helpers - functions shared through #INCLUDE by high_scores.dba dbcExample
examples/dbc/tutorials/language/score_helpers.dba · 33 lines (21 code)
An included file is spliced into the program that includes it, so it holds only functions: running it on its own does nothing. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Linear: runs once from top to bottom.
- Structure. 3 user function(s):
pad$, zeros$, ordinal$.
Commands used (2)
View source (33 lines)
rem ==================================================================
rem Score Helpers - functions shared through #INCLUDE by high_scores.dba
rem An included file is spliced into the program that includes it, so it
rem holds only functions: running it on its own does nothing.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
rem Pad text$ with spaces on the right up to width characters
function pad$(text$, width)
while len(text$) < width
text$ = text$ + " "
endwhile
endfunction text$
rem Put leading zeros in front of a number: 7 becomes 0007
function zeros$(value, digits)
result$ = str$(value)
while len(result$) < digits
result$ = "0" + result$
endwhile
endfunction result$
rem 1st, 2nd, 3rd, 4th ... 11th, 12th, 13th ... 21st
function ordinal$(place)
suffix$ = "th"
if place mod 100 < 11 or place mod 100 > 13
select place mod 10
case 1 : suffix$ = "st" : endcase
case 2 : suffix$ = "nd" : endcase
case 3 : suffix$ = "rd" : endcase
endselect
endif
endfunction str$(place) + suffix$
Station Clock - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/language/station_clock.dba · 90 lines (66 code)
Shows the ways a program waits and keeps time: WAIT and SLEEP pause for a number of milliseconds, SYNC RATE paces the frames, TIMER() counts milliseconds, GET TIME$() and GET DATE$() read the clock, and WAIT KEY, SUSPEND FOR KEY and SUSPEND FOR MOUSE wait for input. ESCAPE stops the clock; then a key and a click end the program. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 2 GOSUB subroutine(s):
draw_hand_setup, draw_hand.
Commands used (29)
View source (90 lines)
rem ==================================================================
rem Station Clock - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Shows the ways a program waits and keeps time: WAIT and SLEEP pause
rem for a number of milliseconds, SYNC RATE paces the frames, TIMER()
rem counts milliseconds, GET TIME$() and GET DATE$() read the clock,
rem and WAIT KEY, SUSPEND FOR KEY and SUSPEND FOR MOUSE wait for input.
rem ESCAPE stops the clock; then a key and a click end the program.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
w = screen width() : h = screen height()
cx = w / 2 : cy = h / 2 - 10 : radius = 170
rem A short countdown, paced with WAIT
for n = 3 to 1 step -1
cls rgb(20, 20, 30)
ink rgb(255, 255, 255), 0
center text cx, cy, "Starting in " + str$(n)
wait 300
next n
sync on : sync rate 30
start = timer()
do
time$ = get time$()
hours = val(left$(time$, 2)) mod 12
minutes = val(right$(left$(time$, 5), 2))
seconds = val(right$(time$, 2))
rem TIMER adds the fraction of a second so the second hand sweeps smoothly
fraction# = ((timer() - start) mod 1000) / 1000.0
cls rgb(30, 34, 46)
rem The dial: a white face filled line by line, ticks every minute
ink rgb(245, 245, 240), 0
for dy = 0 - radius to radius
half = sqrt(radius * radius - dy * dy)
line cx - half, cy + dy, cx + half, cy + dy
next dy
ink rgb(30, 30, 30), 0
for tick = 0 to 59
inner = radius - 8
if tick mod 5 = 0 then inner = radius - 22
a = tick * 6
line cx + sin(a) * inner, cy - cos(a) * inner, cx + sin(a) * (radius - 4), cy - cos(a) * (radius - 4)
next tick
rem Hands: each angle grows clockwise from twelve o'clock
gosub draw_hand_setup
hand_angle# = (hours + minutes / 60.0) * 30 : hand_length = radius * 0.5 : hand_width = 5
gosub draw_hand
hand_angle# = (minutes + seconds / 60.0) * 6 : hand_length = radius * 0.8 : hand_width = 3
gosub draw_hand
ink rgb(200, 30, 30), 0
hand_angle# = (seconds + fraction#) * 6
line cx, cy, cx + sin(hand_angle#) * (radius - 12), cy - cos(hand_angle#) * (radius - 12)
circle cx, cy, 4
ink rgb(230, 230, 230), 0
center text cx, cy + radius + 14, get date$() + " " + time$
text 10, 10, "Running for " + str$((timer() - start) / 1000) + " s - ESCAPE stops the clock"
if escapekey() = 1 then exit
sync
loop
rem Waiting for input: each command returns once the player acts
sync off
cls rgb(20, 20, 30)
ink rgb(255, 255, 255), 0
center text cx, cy, "Clock stopped. Press a key."
wait key
cls rgb(20, 20, 30)
center text cx, cy, "Now press another key."
suspend for key
cls rgb(20, 20, 30)
center text cx, cy, "And finally click the mouse."
suspend for mouse
sleep 200
end
draw_hand_setup:
ink rgb(30, 30, 30), 0
return
rem A hand is a fan of lines hand_width pixels wide
draw_hand:
for side = 0 - hand_width to hand_width
offset# = side / 2.0
line cx + cos(hand_angle#) * offset#, cy + sin(hand_angle#) * offset#, cx + sin(hand_angle#) * hand_length, cy - cos(hand_angle#) * hand_length
next side
return
Tutorials — Legacy
Compatibility Stubs - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/legacy/compat_stubs.dba · 72 lines (54 code)
DarkBASIC Classic relied on Windows 98 technology that has no portable equivalent today: audio CDs, FTP sessions, the Windows folder and old hardware switches. OpenDBC still accepts every one of these commands so that old programs run, but they do nothing: commands return at once, numbers read 0 and strings are empty. This program calls each of them and shows what it answers, one page per area. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 2 array(s) declared with
DIM.
Commands used (41)
View source (72 lines)
rem ==================================================================
rem Compatibility Stubs - an OpenDBC tutorial for the DarkBASIC Classic profile
rem DarkBASIC Classic relied on Windows 98 technology that has no portable
rem equivalent today: audio CDs, FTP sessions, the Windows folder and
rem old hardware switches. OpenDBC still accepts every one of
rem these commands so that old programs run, but they do nothing: commands
rem return at once, numbers read 0 and strings are empty. This program
rem calls each of them and shows what it answers, one page per area.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
dim title$(4) : dim why$(4)
dim label$(70) : dim value$(70) : dim group(70)
n = 0
rem --- 1. Audio CDs
title$(1) = "AUDIO CD" : why$(1) = "There is no CD drive to read audio tracks from."
load cdmusic 1, 1
n = n + 1 : group(n) = 1 : label$(n) = "LOAD CDMUSIC 1, 1 then MUSIC EXIST(1)" : value$(n) = str$(music exist(1))
rem --- 2. FTP
title$(2) = "FTP" : why$(2) = "Plain FTP is obsolete and insecure; every session fails at once."
ftp connect "ftp.example.org", "anonymous", "guest"
ftp set dir "/"
ftp find first : ftp find next
ftp get file "remote.txt", "local.txt", 1
ftp put file "local.txt"
ftp delete file "old.txt"
ftp proceed : ftp terminate : ftp disconnect 0
g = 2
n = n + 1 : group(n) = g : label$(n) = "GET FTP STATUS()" : value$(n) = str$(get ftp status())
n = n + 1 : group(n) = g : label$(n) = "GET FTP FAILURE()" : value$(n) = str$(get ftp failure())
n = n + 1 : group(n) = g : label$(n) = "GET FTP PROGRESS()" : value$(n) = str$(get ftp progress())
n = n + 1 : group(n) = g : label$(n) = "GET FTP FILE TYPE()" : value$(n) = str$(get ftp file type())
n = n + 1 : group(n) = g : label$(n) = "GET FTP FILE SIZE()" : value$(n) = str$(get ftp file size())
n = n + 1 : group(n) = g : label$(n) = "GET FTP FILE NAME$()" : value$(n) = """" + get ftp file name$() + """"
n = n + 1 : group(n) = g : label$(n) = "GET FTP DIR$()" : value$(n) = """" + get ftp dir$() + """"
n = n + 1 : group(n) = g : label$(n) = "GET FTP ERROR$()" : value$(n) = """" + get ftp error$() + """"
rem --- 3. The Windows folder and old hardware switches
title$(3) = "HARDWARE" : why$(3) = "Modern systems hide the Windows folder and need no software emulation."
set graphics card "Primary Display Driver"
disable tnl : enable tnl
set emulation on : set emulation off
disable systemkeys : enable systemkeys
set eax 1
g = 3
n = n + 1 : group(n) = g : label$(n) = "WINDIR$()" : value$(n) = """" + windir$() + """"
n = n + 1 : group(n) = g : label$(n) = "EMULATION MODE()" : value$(n) = str$(emulation mode())
pages = 3 : page = 1 : changed = timer()
do
if timer() - changed > 4000 or (rightkey() = 1 and held = 0) then page = (page mod pages) + 1 : changed = timer()
held = rightkey()
cls rgb(26, 30, 40)
ink rgb(255, 200, 90), 0
text 20, 14, "COMPATIBILITY STUBS - page " + str$(page) + " of " + str$(pages) + ": " + title$(page)
ink rgb(190, 190, 210), 0
text 20, 38, why$(page)
y = 76
for i = 1 to n
if group(i) = page
ink rgb(230, 230, 230), 0 : text 30, y, label$(i)
ink rgb(120, 220, 160), 0 : text 420, y, value$(i)
y = y + 22
endif
next i
ink rgb(140, 140, 160), 0
text 20, screen height() - 30, "Every command on this page ran without error. RIGHT shows the next page."
sync
loop
Tutorials — Maths
Cannon - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/maths/cannon.dba · 103 lines (79 code)
Ballistics with the maths functions: ASIN works out the angle that lands a shot on the target, SIN and COS turn the angle into a velocity, SQRT measures how close a shot came and EXP fades the smoke. Move the mouse to aim (ATANFULL points the barrel), click to fire. Without the mouse the cannon aims and fires by itself. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame.
Commands used (27)
View source (103 lines)
rem ==================================================================
rem Cannon - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Ballistics with the maths functions: ASIN works out the angle that
rem lands a shot on the target, SIN and COS turn the angle into a
rem velocity, SQRT measures how close a shot came and EXP fades the smoke.
rem Move the mouse to aim (ATANFULL points the barrel), click to fire.
rem Without the mouse the cannon aims and fires by itself.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
randomize timer()
w = screen width() : h = screen height()
ground = h - 40 : gunx = 50 : guny = ground - 14
speed# = 9.0 : gravity# = 0.15
targetx = 300 + rnd(w - 360)
angle# = 45.0 : flying = 0 : cooldown = 60
shots = 0 : hits = 0 : smoke = 0
lastmx = mousex() : lastmy = mousey()
do
rem The mouse takes over once it moves; otherwise the gunner aims
if mousex() <> lastmx or mousey() <> lastmy then manual = 1
lastmx = mousex() : lastmy = mousey()
if manual = 1
rem ATANFULL gives the heading of the mouse seen from the cannon
angle# = 90 - wrapvalue(atanfull(mousex() - gunx, guny - mousey()))
if angle# < 5 then angle# = 5
if angle# > 85 then angle# = 85
else
rem Range of a shot: speed^2 * SIN(2 * angle) / gravity, so the angle
rem for a given range is half of ASIN(range * gravity / speed^2)
ratio# = (targetx - gunx) * gravity# / (speed# * speed#)
if ratio# > 1 then ratio# = 1
angle# = asin(ratio#) / 2.0
endif
if flying = 0 then dec cooldown
fire = 0
if manual = 1 and mouseclick() = 1 and flying = 0 then fire = 1
if manual = 0 and cooldown <= 0 then fire = 1
if fire = 1
rem The gunner is not perfect: a small random error spoils some shots
aim# = angle#
if manual = 0 then aim# = angle# + (rnd(40) - 20) / 10.0
shotx# = gunx + cos(aim#) * 30 : shoty# = guny - sin(aim#) * 30
vx# = cos(aim#) * speed# : vy# = 0 - sin(aim#) * speed#
flying = 1 : smoke = 0 : cooldown = 90
inc shots
endif
if flying = 1
shotx# = shotx# + vx# : shoty# = shoty# + vy# : vy# = vy# + gravity#
distance# = sqrt((shotx# - targetx) ^ 2 + (shoty# - (ground - 12)) ^ 2)
if distance# < 20
inc hits
flying = 0
message$ = "HIT!"
targetx = 300 + rnd(w - 360)
endif
if shoty# > ground
flying = 0
message$ = "Missed by " + str$(int(abs(shotx# - targetx))) + " pixels"
endif
endif
rem --- Drawing
for y = 0 to ground
ink rgb(120 + y / 6, 170 + y / 8, 230), 0
line 0, y, w, y
next y
ink rgb(80, 140, 60), 0
box 0, ground, w, h
rem The target: a flag on a pole
ink rgb(90, 60, 30), 0 : box targetx - 2, ground - 40, targetx + 2, ground
ink rgb(230, 40, 40), 0 : box targetx + 2, ground - 40, targetx + 26, ground - 26
rem Smoke from the muzzle fades with EXP
inc smoke
fade = 255 * exp(0 - smoke / 25.0)
if fade > 8
ink rgb(fade, fade, fade), 0
for r = 1 to 6 + smoke / 6
circle gunx + cos(aim#) * 34, guny - sin(aim#) * 34 - smoke / 4, r
next r
endif
rem The barrel: a fan of lines along the aim angle
ink rgb(40, 40, 48), 0
for side# = -4.0 to 4.0 step 0.5
line gunx - sin(angle#) * side#, guny - cos(angle#) * side#, gunx + cos(angle#) * 30 - sin(angle#) * side#, guny - sin(angle#) * 30 - cos(angle#) * side#
next side#
box gunx - 16, guny, gunx + 16, ground
if flying = 1
ink rgb(20, 20, 20), 0
for r = 1 to 5 : circle shotx#, shoty#, r : next r
endif
ink rgb(20, 20, 40), 0
accuracy = 0
if shots > 0 then accuracy = int(hits * 100 / shots)
text 10, 10, "ANGLE " + str$(int(angle#)) + " SHOTS " + str$(shots) + " HITS " + str$(hits) + " ACCURACY " + str$(accuracy) + "%"
text 10, 30, message$
sync
loop
Random Garden - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/maths/random_garden.dba · 73 lines (58 code)
Grows a garden from random numbers. RANDOMIZE sets the starting point of RND, so the same seed always grows the same garden. A new seed comes every few seconds; R grows the last one again. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 2 GOSUB subroutine(s):
grow, draw_flower. - Data. 1 array(s) declared with
DIM.
Commands used (21)
View source (73 lines)
rem ==================================================================
rem Random Garden - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Grows a garden from random numbers. RANDOMIZE sets the starting
rem point of RND, so the same seed always grows the same garden.
rem A new seed comes every few seconds; R grows the last one again.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
w = screen width() : h = screen height()
flowers = 18
dim fx(flowers) : dim fy(flowers)
seed = 1999
gosub grow
changed = timer()
do
if timer() - changed > 5000
rem TIMER() is never the same twice, which makes a good new seed
seed = timer() mod 100000
gosub grow
changed = timer()
endif
if inkey$() = "r" or inkey$() = "R" then gosub grow : changed = timer()
sync
loop
grow:
randomize seed
cls rgb(150, 205, 240)
ink rgb(90, 160, 70), 0
box 0, h / 3, w, h
rem Pick spots, rejecting any closer than 50 pixels to an earlier one
placed = 0 : tries = 0
while placed < flowers and tries < 500
inc tries
x = 30 + rnd(w - 60) : y = h / 3 + 30 + rnd(h * 2 / 3 - 60)
ok = 1
for f = 1 to placed
if sqrt((x - fx(f)) ^ 2 + (y - fy(f)) ^ 2) < 50 then ok = 0
next f
if ok = 1 then inc placed : fx(placed) = x : fy(placed) = y
endwhile
rem Far flowers first, so the near ones overlap them
for f = 1 to placed
gosub draw_flower
next f
ink rgb(20, 40, 60), 0
text 10, 10, "RANDOMIZE " + str$(seed) + " - " + str$(placed) + " flowers in " + str$(tries) + " tries - R grows it again"
return
draw_flower:
size = 8 + rnd(10)
stem = 20 + rnd(30)
petals = 5 + rnd(4)
red = 150 + rnd(105) : green = rnd(120) : blue = 80 + rnd(175)
ink rgb(40, 110 + rnd(40), 40), 0
line fx(f), fy(f), fx(f), fy(f) - stem
line fx(f) + 1, fy(f), fx(f) + 1, fy(f) - stem
cy = fy(f) - stem
rem Each petal is a small filled circle placed around the centre
for p = 0 to petals - 1
a = p * 360 / petals
ink rgb(red, green, blue), 0
for r = 1 to size / 2
circle fx(f) + sin(a) * size, cy - cos(a) * size, r
next r
next p
ink rgb(255, 220, 60), 0
for r = 1 to int(size / 2.5)
circle fx(f), cy, r
next r
return
Wave Lab - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/maths/wave_lab.dba · 113 lines (89 code)
Plots the maths functions side by side. Angles are in degrees in DarkBASIC: SIN(90) is 1. The waves drift so you can watch their phase, and a rotating pointer shows what ATANFULL measures. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 GOSUB subroutine(s):
frame.
Commands used (27)
View source (113 lines)
rem ==================================================================
rem Wave Lab - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Plots the maths functions side by side. Angles are in degrees in
rem DarkBASIC: SIN(90) is 1. The waves drift so you can watch their
rem phase, and a rotating pointer shows what ATANFULL measures.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
w = screen width() : h = screen height()
panel_w = w / 2 - 15 : panel_h = h / 3 - 18
do
phase = phase + 4
cls rgb(14, 16, 24)
rem Panel 1: SIN and COS, a quarter turn apart, and ABS folding SIN up
px = 10 : py = 10 : gosub frame
lastx = px : lasts# = py + panel_h / 2 : lastc# = lasts# : lasta# = lasts#
for x = 0 to panel_w step 3
a = x * 2 + phase
s# = py + panel_h / 2 - sin(a) * panel_h * 0.4
c# = py + panel_h / 2 - cos(a) * panel_h * 0.4
b# = py + panel_h / 2 - abs(sin(a)) * panel_h * 0.4
ink rgb(255, 120, 90), 0 : line lastx, lasts#, px + x, s#
ink rgb(90, 200, 255), 0 : line lastx, lastc#, px + x, c#
ink rgb(120, 120, 140), 0 : line lastx, lasta#, px + x, b#
lastx = px + x : lasts# = s# : lastc# = c# : lasta# = b#
next x
ink rgb(230, 230, 230), 0 : text px + 6, py + 4, "SIN COS ABS(SIN)"
rem Panel 2: TAN shoots off to infinity at 90 degrees; clip it to the panel
px = w / 2 + 5 : py = 10 : gosub frame
lastx = -1
for x = 0 to panel_w step 2
a = x + phase
t# = tan(a)
if t# > 3 or t# < -3
lastx = -1
else
y# = py + panel_h / 2 - t# * panel_h / 6
ink rgb(255, 210, 90), 0
if lastx >= 0 then line lastx, lasty#, px + x, y#
lastx = px + x : lasty# = y#
endif
next x
ink rgb(230, 230, 230), 0 : text px + 6, py + 4, "TAN (clipped to -3..3)"
rem Panel 3: the hyperbolic functions over -3..3. Like every trig
rem function in DarkBASIC they take degrees, so 3 becomes 3 * 57.3
px = 10 : py = h / 3 + 4 : gosub frame
for k = 0 to 2
lastx = -1
for x = 0 to panel_w step 3
v# = ((x * 6.0 / panel_w) - 3.0) * 57.29578
if k = 0 then r# = hsin(v#) / 10.0 : ink rgb(255, 120, 90), 0
if k = 1 then r# = hcos(v#) / 10.0 : ink rgb(90, 200, 255), 0
if k = 2 then r# = htan(v#) : ink rgb(140, 255, 140), 0
y# = py + panel_h / 2 - r# * panel_h * 0.45
if lastx >= 0 then line lastx, lasty#, px + x, y#
lastx = px + x : lasty# = y#
next x
next k
ink rgb(230, 230, 230), 0 : text px + 6, py + 4, "HSIN/10 HCOS/10 HTAN"
rem Panel 4: EXP makes a wave die away
px = w / 2 + 5 : py = h / 3 + 4 : gosub frame
lastx = px : lasty# = py + panel_h / 2
for x = 0 to panel_w step 2
v# = x / 60.0
y# = py + panel_h / 2 - exp(0 - v#) * sin(x * 6 + phase) * panel_h * 0.45
ink rgb(220, 140, 255), 0 : line lastx, lasty#, px + x, y#
lastx = px + x : lasty# = y#
next x
ink rgb(230, 230, 230), 0 : text px + 6, py + 4, "EXP(-x) * SIN"
rem Panel 5: the inverse functions turn a ratio back into an angle
px = 10 : py = 2 * h / 3 - 2 : gosub frame
for k = 0 to 2
lastx = -1
for x = 0 to panel_w step 3
v# = (x * 2.0 / panel_w) - 1.0
if k = 0 then r# = asin(v#) : ink rgb(255, 120, 90), 0
if k = 1 then r# = acos(v#) - 90 : ink rgb(90, 200, 255), 0
rem ATAN answers between 0 and 360: fold the far half back below zero
if k = 2 then r# = atan(v# * 4) : ink rgb(140, 255, 140), 0
if r# > 180 then r# = r# - 360
y# = py + panel_h / 2 - r# * panel_h / 200.0
if lastx >= 0 then line lastx, lasty#, px + x, y#
lastx = px + x : lasty# = y#
next x
next k
ink rgb(230, 230, 230), 0 : text px + 6, py + 4, "ASIN ACOS-90 ATAN (degrees)"
rem Panel 6: ATANFULL(x, y) gives the full 0-360 heading of a point
px = w / 2 + 5 : py = 2 * h / 3 - 2 : gosub frame
cx = px + panel_w / 2 : cy = py + panel_h / 2 + 8
dx# = sin(phase * 0.7) * 50 : dy# = cos(phase * 0.7) * 50
ink rgb(255, 210, 90), 0
line cx, cy, cx + dx#, cy - dy#
circle cx + dx#, cy - dy#, 4
circle cx, cy, 50
ink rgb(230, 230, 230), 0
text px + 6, py + 4, "ATANFULL(" + str$(int(dx#)) + "," + str$(int(dy#)) + ") = " + str$(int(wrapvalue(atanfull(dx#, dy#))))
sync
loop
frame:
ink rgb(28, 32, 46), 0
box px, py, px + panel_w, py + panel_h
ink rgb(60, 66, 90), 0
line px, py + panel_h / 2, px + panel_w, py + panel_h / 2
return
Tutorials — Memblocks
Plasma - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/memblock/plasma.dba · 84 lines (59 code)
A memblock is a block of bytes you can read and write freely. An image memblock starts with three dwords (width, height, depth) and then holds one dword per pixel, written like an RGB() colour. This program paints a plasma into such a memblock every frame, turns it into a bitmap and stretches it over the screen. A second memblock shows the other ways of reading and writing bytes. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 2 array(s) declared with
DIM.
Commands used (35)
View source (84 lines)
rem ==================================================================
rem Plasma - an OpenDBC tutorial for the DarkBASIC Classic profile
rem A memblock is a block of bytes you can read and write freely. An
rem image memblock starts with three dwords (width, height, depth) and
rem then holds one dword per pixel, written like an RGB() colour. This
rem program paints a plasma into such a memblock every frame, turns it
rem into a bitmap and stretches it over the screen. A second memblock
rem shows the other ways of reading and writing bytes.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
w = screen width() : h = screen height()
pw = 80 : ph = 60
rem --- The plasma memblock: 12 header bytes, then 4 bytes per pixel
make memblock 1, 12 + pw * ph * 4
write memblock dword 1, 0, pw
write memblock dword 1, 4, ph
write memblock dword 1, 8, 32
rem A palette of 256 colours, so each pixel needs a single lookup
dim palette(255)
for i = 0 to 255
palette(i) = rgb(128 + sin(i * 1.41) * 127, 128 + sin(i * 1.41 + 120) * 127, 128 + sin(i * 2.82) * 127)
next i
dim wave(359)
for i = 0 to 359 : wave(i) = 32 + sin(i) * 31 : next i
rem --- A small memblock for the byte, word, dword and float readers
make memblock 2, 16
write memblock byte 2, 0, 200
write memblock word 2, 2, 40000
write memblock dword 2, 4, 123456
write memblock float 2, 8, 2.5
rem COPY MEMBLOCK from, to, from offset, to offset, bytes: duplicate the word
copy memblock 2, 2, 2, 12, 2
rem --- A drawn logo travels the other way: image to memblock and back
create bitmap 2, 64, 32
set current bitmap 2
cls rgb(255, 255, 255)
ink rgb(30, 30, 30), 0 : box 4, 4, 59, 27
ink rgb(255, 210, 80), 0 : text 10, 8, "MEM"
get image 1, 0, 0, 64, 32
set current bitmap 0
delete bitmap 2
make memblock from image 3, 1
rem Swap the red and blue bytes of every pixel (blue comes first in memory)
for p = 0 to 64 * 32 - 1
b = memblock byte(3, 12 + p * 4)
write memblock byte 3, 12 + p * 4, memblock byte(3, 14 + p * 4)
write memblock byte 3, 14 + p * 4, b
next p
make image from memblock 2, 3
delete memblock 3
do
t = t + 3
for y = 0 to ph - 1
row = 12 + y * pw * 4
a = wave((y * 6 + t) mod 360)
for x = 0 to pw - 1
v = a + wave((x * 5 + t * 2) mod 360) + wave(((x + y) * 4 + t) mod 360) + wave((x * 3 - y * 2 + 720 + t) mod 360)
write memblock dword 1, row + x * 4, palette(v mod 256)
next x
next y
rem The memblock becomes bitmap 1, stretched over the screen
rem MAKE BITMAP FROM MEMBLOCK also makes the new bitmap the current one
make bitmap from memblock 1, 1
set current bitmap 0
copy bitmap 1, 0, 0, pw, ph, 0, 0, 0, w, h
rem Read the screen back into a memblock to show its size
if t = 3 then make memblock from bitmap 4, 0 : screen_bytes = get memblock size(4) : delete memblock 4
paste image 1, 20, h - 100
paste image 2, 100, h - 100
ink rgb(0, 0, 0), 0
box 0, 0, w, 66
ink rgb(255, 255, 255), 0
text 10, 4, "MEMBLOCK 1: " + str$(get memblock size(1)) + " bytes (" + str$(pw) + " x " + str$(ph) + ") screen: " + str$(screen_bytes) + " bytes"
text 10, 24, "BYTE " + str$(memblock byte(2, 0)) + " WORD " + str$(memblock word(2, 2)) + " DWORD " + str$(memblock dword(2, 4)) + " FLOAT " + str$(memblock float(2, 8)) + " COPY " + str$(memblock word(2, 12))
text 10, 44, "MEMBLOCK EXIST(3) = " + str$(memblock exist(3)) + " right logo: red and blue swapped"
sync
loop
Tone Maker - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/memblock/tone_maker.dba · 80 lines (62 code)
Builds sounds sample by sample in a memblock: three dwords (bits per sample, samples per second, sample count) followed by the samples. MAKE SOUND FROM MEMBLOCK turns it into a sound, and MAKE MEMBLOCK FROM SOUND reads a sound back so its wave can be drawn. Keys 1 to 5 pick sine, square, saw, noise or a chord; otherwise the waves take turns. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 GOSUB subroutine(s):
build_sound. - Data. 1 array(s) declared with
DIM.
Commands used (33)
View source (80 lines)
rem ==================================================================
rem Tone Maker - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Builds sounds sample by sample in a memblock: three dwords (bits per
rem sample, samples per second, sample count) followed by the samples.
rem MAKE SOUND FROM MEMBLOCK turns it into a sound, and MAKE MEMBLOCK
rem FROM SOUND reads a sound back so its wave can be drawn.
rem Keys 1 to 5 pick sine, square, saw, noise or a chord; otherwise the
rem waves take turns.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
w = screen width() : h = screen height()
rate = 22050 : samples = rate / 2
dim name$(5)
name$(1) = "SINE" : name$(2) = "SQUARE" : name$(3) = "SAW" : name$(4) = "NOISE" : name$(5) = "CHORD"
wave = 1 : gosub build_sound
changed = timer()
do
for k = 1 to 5
if keystate(k + 1) = 1 and wave <> k then wave = k : gosub build_sound : manual = 1
next k
if manual = 0 and timer() - changed > 1500
wave = (wave mod 5) + 1 : gosub build_sound : changed = timer()
endif
cls rgb(16, 22, 30)
ink rgb(255, 210, 120), 0
center text w / 2, 10, "TONE MAKER - " + name$(wave) + " (keys 1 to 5)"
rem The wave as stored in the sound: memblock 2 was made from sound 1
ink rgb(40, 60, 80), 0
line 0, h / 2, w, h / 2
ink rgb(120, 230, 255), 0
count = memblock dword(2, 8)
lasty = h / 2
for x = 0 to w - 1
rem Skip the fade-in: draw from the middle of the sound
value = memblock word(2, 12 + ((count / 2 + x) * 2 mod (count * 2)))
if value > 32767 then value = value - 65536
y = h / 2 - value * 150 / 32768
line x - 1, lasty, x, y
lasty = y
next x
ink rgb(220, 220, 230), 0
text 10, h - 60, "Header: " + str$(memblock dword(2, 0)) + " bits, " + str$(memblock dword(2, 4)) + " samples per second, " + str$(count) + " samples"
text 10, h - 40, "Screen memory: " + str$(get backbuffer width()) + " x " + str$(get backbuffer height()) + " x " + str$(get backbuffer depth()) + " bits, " + str$(get backbuffer pitch()) + " bytes per row"
sync
loop
build_sound:
if sound exist(1) = 1 then delete sound 1
if memblock exist(1) = 1 then delete memblock 1
if memblock exist(2) = 1 then delete memblock 2
make memblock 1, 12 + samples * 2
write memblock dword 1, 0, 16
write memblock dword 1, 4, rate
write memblock dword 1, 8, samples
for i = 0 to samples - 1
phase# = i * 220.0 * 360.0 / rate
select wave
case 1 : v# = sin(phase#) : endcase
case 2 : if sin(phase#) >= 0 then v# = 0.6 else v# = -0.6
endcase
case 3 : v# = (wrapvalue(phase#) / 180.0 - 1.0) * 0.7 : endcase
case 4 : v# = (rnd(2000) - 1000) / 1500.0 : endcase
case 5 : v# = (sin(phase#) + sin(phase# * 1.26) + sin(phase# * 1.5)) / 3.0 : endcase
endselect
rem Fade in and out so the sound starts and stops without a click
if i < 500 then v# = v# * i / 500.0
if i > samples - 500 then v# = v# * (samples - i) / 500.0
value = v# * 26000
rem A word is unsigned: negative samples wrap around to the top half
if value < 0 then value = value + 65536
write memblock word 1, 12 + i * 2, value
next i
make sound from memblock 1, 1
play sound 1
make memblock from sound 2, 1
return
Tutorials — Music
Jukebox - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/music/jukebox.dba · 89 lines (69 code)
Plays two tunes with the music commands. Music is one long piece played at a time, unlike sounds. LOAD MUSIC reads MIDI files; when the computer has no MIDI synthesiser, OpenDBC plays the WAV of the same name instead. 1 and 2 pick a tune, L loops it, P pauses and resumes, S stops, D deletes tune 2. Otherwise the tunes alternate. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 GOSUB subroutine(s):
disc. - Data. 1 array(s) declared with
DIM. - Assets. Loads 2 media file(s) from the example folder:
harbour_waltz.mid, night_march.mid.
Commands used (34)
View source (89 lines)
rem ==================================================================
rem Jukebox - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Plays two tunes with the music commands. Music is one long piece
rem played at a time, unlike sounds. LOAD MUSIC reads MIDI files; when
rem the computer has no MIDI synthesiser, OpenDBC plays the WAV of the
rem same name instead. 1 and 2 pick a tune, L loops it, P pauses and
rem resumes, S stops, D deletes tune 2. Otherwise the tunes alternate.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 30
w = screen width() : h = screen height()
load music "harbour_waltz.mid", 1
load music "night_march.mid", 2
dim title$(2)
title$(1) = "Harbour Waltz" : title$(2) = "Night March"
track = 1 : loop music track : changed = timer()
do
key$ = lower$(inkey$())
if key$ <> lastkey$
if key$ = "1" or key$ = "2"
if music exist(val(key$)) = 1
stop music track
track = val(key$) : play music track : manual = 1
endif
endif
if key$ = "l" then stop music track : loop music track : manual = 1
if key$ = "p"
if music paused(track) = 1 then resume music track else pause music track
manual = 1
endif
if key$ = "s" then stop music track : manual = 1
if key$ = "d" and music exist(2) = 1
if track = 2 then stop music 2 : track = 1 : play music 1
delete music 2
endif
endif
lastkey$ = key$
rem Without the keyboard the two tunes take turns every twelve seconds
if manual = 0 and timer() - changed > 12000 and music exist(2) = 1
stop music track
track = 3 - track
loop music track
changed = timer()
endif
rem --- A turntable that spins while the music plays
cls rgb(40, 28, 24)
ink rgb(90, 60, 40), 0
box 60, 80, w - 60, h - 70
if music playing(track) = 1 and music paused(track) = 0 then spin# = wrapvalue(spin# + 6)
ink rgb(20, 20, 20), 0
radius = 130 : gosub disc
ink rgb(60, 60, 60), 0
for r = 34 to 126 step 9 : circle 220, 240, r : next r
ink rgb(200, 60, 50), 0
radius = 28 : gosub disc
ink rgb(255, 230, 200), 0
line 220, 240, 220 + sin(spin#) * 26, 240 - cos(spin#) * 26
rem A level meter that only dances while the music plays
for bar = 0 to 7
level = 10
if music playing(track) = 1 and music paused(track) = 0 then level = 20 + rnd(110)
if level > 120 then level = 120
ink rgb(240, 180 - bar * 12, 60), 0
box 400 + bar * 22, 390 - level, 416 + bar * 22, 390
next bar
ink rgb(255, 240, 220), 0
center text w / 2, 20, "JUKEBOX"
text 400, 120, "NOW: " + title$(track)
text 400, 150, "PLAYING " + str$(music playing(track))
text 400, 170, "LOOPING " + str$(music looping(track))
text 400, 190, "PAUSED " + str$(music paused(track))
text 400, 220, "TUNE 2 EXISTS " + str$(music exist(2))
text 70, h - 50, "1/2 pick a tune, L loops, P pauses, S stops, D deletes tune 2"
sync
loop
rem A filled disc around the turntable centre, one LINE per row
disc:
for dy = 0 - radius to radius
half = sqrt(radius * radius - dy * dy)
line 220 - half, 240 + dy, 220 + half, 240 + dy
next dy
return
Tutorials — Screen
Display Lab - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/screen/display_lab.dba · 65 lines (47 code)
Asks the computer about its screen and graphics card: the display modes on offer, the current width, height, depth and frame rate, and the video memory. Checklists hold the answers: a name in CHECKLIST STRING$ and up to four numbers in CHECKLIST VALUE A to D. SET GAMMA fades the whole picture; M switches to the next display mode, but only when you press it. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM.
Commands used (36)
View source (65 lines)
rem ==================================================================
rem Display Lab - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Asks the computer about its screen and graphics card: the display
rem modes on offer, the current width, height, depth and frame rate,
rem and the video memory. Checklists hold the answers: a name in
rem CHECKLIST STRING$ and up to four numbers in CHECKLIST VALUE A to D.
rem SET GAMMA fades the whole picture; M switches to the next display
rem mode, but only when you press it.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
rem Display modes: width, height and depth in values A, B and C; value D
rem is the refresh rate where the system reports one
perform checklist for display modes
modes = checklist quantity()
dim modew(64) : dim modeh(64) : dim moded(64) : dim moder(64)
for m = 1 to modes
if m <= 64 then modew(m) = checklist value a(m) : modeh(m) = checklist value b(m) : moded(m) = checklist value c(m) : moder(m) = checklist value d(m)
next m
perform checklist for graphics cards
cards = checklist quantity()
if cards > 0 then card$ = checklist string$(1)
empty checklist
emptied = checklist quantity()
mode = 0
do
if lower$(inkey$()) = "m" and held = 0 and modes > 0
mode = (mode mod modes) + 1
rem CHECK DISPLAY MODE answers 1 when the mode can be used
if check display mode(modew(mode), modeh(mode), moded(mode)) = 1 then set display mode modew(mode), modeh(mode), moded(mode)
endif
held = (lower$(inkey$()) = "m")
rem Gamma 255 is normal; the picture breathes between dim and bright
level = 255 + sin(timer() / 20.0) * 120
set gamma level, level, level
w = screen width() : h = screen height()
cls rgb(30, 30, 44)
for x = 0 to w step 16
ink rgb(x * 255 / w, 120, 255 - x * 255 / w), 0
box x, h - 60, x + 15, h - 20
next x
ink rgb(255, 210, 120), 0
text 20, 14, "DISPLAY LAB"
ink rgb(230, 230, 240), 0
text 20, 44, "SCREEN " + str$(w) + " x " + str$(h) + " x " + str$(screen depth()) + " bits TYPE " + str$(screen type()) + " FPS " + str$(screen fps())
text 20, 64, "GAMMA " + str$(level) + " SCREEN INVALID " + str$(screen invalid())
text 20, 94, "GRAPHICS CARD " + current graphics card$() + " (" + str$(cards) + " found: " + card$ + ")"
text 20, 114, "TNL AVAILABLE " + str$(tnl available())
text 20, 134, "MEMORY: DISPLAY " + str$(system dmem available()) + " SYSTEM " + str$(system smem available()) + " TOTAL " + str$(system tmem available())
text 20, 164, str$(modes) + " display modes (EMPTY CHECKLIST leaves " + str$(emptied) + "):"
for m = 1 to modes
if m <= 24
x = 20 + ((m - 1) / 8) * 200 : y = 184 + ((m - 1) mod 8) * 20
if m = mode then ink rgb(120, 230, 160), 0 else ink rgb(180, 180, 200), 0
text x, y, str$(modew(m)) + " x " + str$(modeh(m)) + " x " + str$(moded(m)) + " " + str$(moder(m)) + " Hz"
endif
next m
ink rgb(150, 150, 170), 0
text 20, h - 90, "M switches to the next display mode"
sync
loop
Tutorials — Sound
Bee Garden - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/sound/bee_garden.dba · 70 lines (51 code)
3D sound seen from above: three buzzing bees circle the garden and you are the listener. A 3D sound gets louder as it comes closer and moves between the speakers as you turn. UP and DOWN walk, LEFT and RIGHT turn; without the keys the listener turns slowly on the spot. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Assets. Loads 1 media file(s) from the example folder:
buzz.wav.
Commands used (39)
View source (70 lines)
rem ==================================================================
rem Bee Garden - an OpenDBC tutorial for the DarkBASIC Classic profile
rem 3D sound seen from above: three buzzing bees circle the garden and
rem you are the listener. A 3D sound gets louder as it comes closer and
rem moves between the speakers as you turn. UP and DOWN walk, LEFT and
rem RIGHT turn; without the keys the listener turns slowly on the spot.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 30
w = screen width() : h = screen height()
cx = w / 2 : cy = h / 2 + 10 : scale# = 2.0
rem LOAD 3DSOUND gives a sound a position in the world
for b = 1 to 3
load 3dsound "buzz.wav", b
set sound speed b, 9000 + b * 1500
loop sound b
next b
px# = 0.0 : pz# = -40.0 : heading# = 0.0
do
rem Walking and turning
moving = 0
if leftkey() = 1 then heading# = wrapvalue(heading# - 3) : moving = 1
if rightkey() = 1 then heading# = wrapvalue(heading# + 3) : moving = 1
if upkey() = 1 then px# = newxvalue(px#, heading#, 1.5) : pz# = newzvalue(pz#, heading#, 1.5) : moving = 1
if downkey() = 1 then px# = newxvalue(px#, heading#, -1.5) : pz# = newzvalue(pz#, heading#, -1.5) : moving = 1
if moving = 0 then heading# = wrapvalue(heading# + 0.8)
position listener px#, 0, pz#
rotate listener 0, heading#, 0
rem Each bee flies its own loop around the flowers
t# = t# + 1.0
for b = 1 to 3
a# = t# * (0.8 + b * 0.35) + b * 120
position sound b, sin(a#) * (40 + b * 25), 0, cos(a# * 1.3) * (30 + b * 20)
next b
rem --- The garden from above: +X is right and +Z is up the screen
cls rgb(70, 130, 60)
for f = 0 to 11
fx = cx + sin(f * 30) * (60 + (f mod 3) * 50) * scale#
fy = cy - cos(f * 30) * (60 + (f mod 3) * 50) * scale# / 1.4
ink rgb(240, 120 + (f mod 3) * 50, 160), 0
for r = 1 to 6 : circle fx, fy, r : next r
next f
for b = 1 to 3
bx = cx + sound position x(b) * scale# : by = cy - sound position z(b) * scale#
ink rgb(255, 220, 40), 0
for r = 1 to 6 : circle bx, by, r : next r
ink rgb(20, 20, 20), 0
line bx - 6, by, bx + 6, by
ink rgb(255, 255, 255), 0
text bx + 10, by - 8, "bee " + str$(b)
next b
rem The listener: a triangle pointing where it faces
lx = cx + listener position x() * scale# : ly = cy - listener position z() * scale#
ink rgb(255, 255, 255), 0
line lx + sin(heading#) * 16, ly - cos(heading#) * 16, lx + sin(heading# + 140) * 10, ly - cos(heading# + 140) * 10
line lx + sin(heading#) * 16, ly - cos(heading#) * 16, lx + sin(heading# - 140) * 10, ly - cos(heading# - 140) * 10
line lx + sin(heading# + 140) * 10, ly - cos(heading# + 140) * 10, lx + sin(heading# - 140) * 10, ly - cos(heading# - 140) * 10
ink rgb(0, 0, 0), 0
box 0, 0, w, 44
ink rgb(255, 255, 255), 0
text 10, 4, "LISTENER POSITION " + str$(int(listener position x())) + ", " + str$(int(listener position y())) + ", " + str$(int(listener position z())) + " ANGLE " + str$(int(listener angle x())) + ", " + str$(int(listener angle y())) + ", " + str$(int(listener angle z()))
text 10, 24, "BEE 1 AT " + str$(int(sound position x(1))) + ", " + str$(int(sound position y(1))) + ", " + str$(int(sound position z(1))) + " SOUND TYPE(1) = " + str$(sound type(1)) + " (3D)"
sync
loop
Recorder - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/sound/recorder.dba · 78 lines (57 code)
Records three seconds from the microphone into a sound, plays it back and saves it to a WAV file. Recording starts only when you press R, so the program never opens the microphone on its own. SPACE plays the take, S saves it as take.wav, X deletes it. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Assets. Loads 3 media file(s) from the example folder:
pluck.wav, take.wav, Saved take.wav.
Commands used (28)
View source (78 lines)
rem ==================================================================
rem Recorder - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Records three seconds from the microphone into a sound, plays it
rem back and saves it to a WAV file. Recording starts only when you
rem press R, so the program never opens the microphone on its own.
rem SPACE plays the take, S saves it as take.wav, X deletes it.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 30
w = screen width() : h = screen height()
rem SET EAX chose a Creative EAX reverb preset; modern systems have none,
rem so OpenDBC accepts the command and does nothing
set eax 0
load sound "pluck.wav", 2
state$ = "Press R to record three seconds"
do
key$ = lower$(inkey$())
if key$ <> lastkey$
if key$ = "r" and recording = 0
if sound exist(1) = 1 then delete sound 1
rem RECORD SOUND sound number, seconds
record sound 1, 3
recording = 1 : started = timer()
state$ = "Recording..."
endif
if key$ = " " and sound exist(1) = 1 then play sound 1 : state$ = "Playing the take"
if key$ = "s" and sound exist(1) = 1
if file exist("take.wav") = 1 then delete file "take.wav"
save sound "take.wav", 1
state$ = "Saved take.wav"
endif
if key$ = "x" and sound exist(1) = 1 then delete sound 1 : state$ = "Take deleted"
endif
lastkey$ = key$
rem STOP RECORDING SOUND ends early; here the three seconds run out first
if recording = 1 and timer() - started > 3000
stop recording sound
recording = 0
state$ = "Recorded - SPACE plays, S saves, X deletes"
endif
rem Without a take, the pluck sound keeps the meter busy
if recording = 0 and sound exist(1) = 0 and sound playing(2) = 0 then play sound 2
cls rgb(30, 26, 36)
rem A level meter: a fake, but it shows when something is going on
level = 0
if recording = 1 then level = 40 + rnd(160)
if sound exist(1) = 1
if sound playing(1) = 1 then level = 60 + rnd(180)
endif
if sound playing(2) = 1 then level = 30 + rnd(120)
for bar = 0 to 15
if bar * 15 < level
ink rgb(60 + bar * 12, 220 - bar * 10, 80), 0
else
ink rgb(60, 56, 70), 0
endif
box 60 + bar * 33, 300, 88 + bar * 33, 360
next bar
if recording = 1
ink rgb(230, 40, 40), 0
for r = 1 to 14 : circle w / 2, 200, r : next r
ink rgb(255, 255, 255), 0
center text w / 2, 230, str$((3000 - (timer() - started)) / 1000 + 1) + " s"
endif
ink rgb(240, 240, 240), 0
center text w / 2, 40, "RECORDER"
center text w / 2, 90, state$
text 20, h - 60, "Take: SOUND EXIST(1) = " + str$(sound exist(1))
text 20, h - 40, "R records, SPACE plays, S saves take.wav, X deletes"
sync
loop
Sound Board - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/sound/sound_board.dba · 94 lines (74 code)
Six pads play sounds: keys 1 to 6 hit them, or a drum pattern plays by itself. Pad 6 is a CLONE SOUND of pad 4 with a higher SET SOUND SPEED. L loops the synth line, P pauses it, UP/DOWN set its volume, LEFT/RIGHT its pan and D deletes the clone. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 3 array(s) declared with
DIM. - Assets. Loads 7 media file(s) from the example folder:
kick.wav, snare.wav, bell.wav, pluck.wav, synth_loop.wav, x...x...x...x..x (+1 more).
Commands used (38)
View source (94 lines)
rem ==================================================================
rem Sound Board - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Six pads play sounds: keys 1 to 6 hit them, or a drum pattern plays
rem by itself. Pad 6 is a CLONE SOUND of pad 4 with a higher SET SOUND
rem SPEED. L loops the synth line, P pauses it, UP/DOWN set its volume,
rem LEFT/RIGHT its pan and D deletes the clone.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 30
w = screen width() : h = screen height()
load sound "kick.wav", 1
load sound "snare.wav", 2
load sound "bell.wav", 3
load sound "pluck.wav", 4
load sound "synth_loop.wav", 5
clone sound 6, 4
rem The clone shares the pluck's samples; playing it faster raises its pitch
set sound speed 6, 33000
dim pad$(6)
pad$(1) = "KICK" : pad$(2) = "SNARE" : pad$(3) = "BELL"
pad$(4) = "PLUCK" : pad$(5) = "SYNTH LOOP" : pad$(6) = "PLUCK CLONE"
rem A sixteen-step pattern: one string per pad, x marks a hit
dim pattern$(6)
pattern$(1) = "x...x...x...x..x"
pattern$(2) = "....x.......x..."
pattern$(3) = "x..............."
pattern$(4) = "..x...x...x.x..."
pattern$(6) = ".x.....x.....x.x"
dim held(6)
volume = 70 : pan = 0
loop sound 5
set sound volume 5, volume
do
rem Keys 1 to 6 (scancodes 2 to 7); any key stops the automatic pattern
for p = 1 to 6
if keystate(p + 1) = 1 and held(p) = 0 and sound exist(p) = 1 and p <> 5 then play sound p : manual = 1
held(p) = keystate(p + 1)
next p
if manual = 0
inc frame
if frame mod 4 = 0
stepnum = (stepnum mod 16) + 1
for p = 1 to 6
if p <> 5 and sound exist(p) = 1
if mid$(pattern$(p), stepnum) = "x" then play sound p
endif
next p
endif
endif
rem The synth line: loop, pause, volume and pan
key$ = lower$(inkey$())
if key$ = "l" and lastkey$ <> "l"
if sound looping(5) = 1 then stop sound 5 else loop sound 5
endif
if key$ = "p" and lastkey$ <> "p"
if sound paused(5) = 1 then resume sound 5 else pause sound 5
endif
lastkey$ = key$
if upkey() = 1 and volume < 100 then inc volume
if downkey() = 1 and volume > 0 then dec volume
if rightkey() = 1 and pan < 10000 then pan = pan + 200
if leftkey() = 1 and pan > -10000 then pan = pan - 200
set sound volume 5, volume
set sound pan 5, pan
if key$ = "d" and sound exist(6) = 1 then delete sound 6
rem --- Pads light up while their sound plays
cls rgb(24, 24, 32)
for p = 1 to 6
x = 40 + ((p - 1) mod 3) * 190 : y = 60 + ((p - 1) / 3) * 150
if sound exist(p) = 0
ink rgb(50, 50, 50), 0
else
if sound playing(p) = 1 then ink rgb(255, 170, 40), 0 else ink rgb(70, 80, 120), 0
endif
box x, y, x + 170, y + 120
ink rgb(255, 255, 255), 0
text x + 10, y + 10, str$(p) + " " + pad$(p)
if sound exist(p) = 0 then text x + 10, y + 30, "deleted"
next p
ink rgb(220, 220, 220), 0
text 20, 16, "SOUND BOARD - keys 1 to 6 play the pads"
text 20, h - 36, "L loops the synth, P pauses it, arrows set volume and pan, D deletes the clone"
text 20, h - 100, "Synth: PLAYING " + str$(sound playing(5)) + " LOOPING " + str$(sound looping(5)) + " PAUSED " + str$(sound paused(5))
text 20, h - 80, "GET SOUND VOLUME " + str$(get sound volume(5)) + " GET SOUND PAN " + str$(get sound pan(5)) + " GET SOUND SPEED " + str$(get sound speed(5))
text 20, h - 60, "Clone: SOUND EXIST(6) " + str$(sound exist(6)) + " SOUND TYPE(1) " + str$(sound type(1)) + " (0 = ordinary, 1 = 3D)"
sync
loop
Tutorials — Sprites
Buoy Field - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/sprite/buoy_field.dba · 60 lines (39 code)
Loads images, places them on the screen as sprites and queries them. A jet scooter weaves up through a field of buoys whose lamps blink with HIDE SPRITE and SHOW SPRITE. H hides every sprite while held, D deletes the scooter sprite and its image. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Assets. Loads 3 media file(s) from the example folder:
sea.bmp, scooter.bmp, buoy.bmp.
Commands used (29)
View source (60 lines)
rem ==================================================================
rem Buoy Field - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Loads images, places them on the screen as sprites and queries them.
rem A jet scooter weaves up through a field of buoys whose lamps blink
rem with HIDE SPRITE and SHOW SPRITE.
rem H hides every sprite while held, D deletes the scooter sprite and
rem its image.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
w = screen width() : h = screen height()
rem The sea goes onto the screen; sprites are drawn over it and the
rem screen behind them is kept intact
load bitmap "sea.bmp"
load image "scooter.bmp", 1
load image "buoy.bmp", 2
rem Sprites 2 to 13: a grid of buoys
for b = 0 to 11
sprite 2 + b, 70 + (b mod 4) * 160 + (b / 4) * 40, 60 + (b / 4) * 150, 2
next b
x# = w / 2 : y# = h
do
rem The scooter climbs the screen, swaying left and right, then wraps
y# = y# - 1.5
if y# < -64 then y# = h
x# = w / 2 - 32 + sin(y# * 1.2) * 180
if deleted = 0 then sprite 1, x#, y#, 1
rem Every buoy blinks on its own rhythm
for b = 0 to 11
if ((timer() / 400) + b) mod 5 = 0
hide sprite 2 + b
else
show sprite 2 + b
endif
next b
if keystate(35) = 1
hide all sprites
else
if hidden = 1 then show all sprites
endif
hidden = keystate(35)
if keystate(32) = 1 and deleted = 0 then delete sprite 1 : delete image 1 : deleted = 1
ink rgb(0, 0, 0), 0
box 0, h - 44, w, h
ink rgb(255, 255, 255), 0
if sprite exist(1) = 1
text 10, h - 40, "SPRITE 1: X " + str$(sprite x(1)) + " Y " + str$(sprite y(1)) + " IMAGE " + str$(sprite image(1)) + " SIZE " + str$(sprite width(1)) + " x " + str$(sprite height(1))
else
text 10, h - 40, "SPRITE 1 deleted - SPRITE EXIST(1) = " + str$(sprite exist(1)) + ", IMAGE EXIST(1) = " + str$(image exist(1))
endif
text 10, h - 20, "H hides all sprites, D deletes the scooter"
sync
loop
Rock Dodge - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/sprite/rock_dodge.dba · 78 lines (54 code)
A small game: steer the scooter with the arrow keys between the rocks drifting down a scrolling sea. SPRITE HIT reports each new crash, SPRITE COLLISION tells whether the scooter is still touching a rock. Without a key press the scooter steers by itself. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM. - Assets. Loads 3 media file(s) from the example folder:
sea.bmp, scooter.bmp, rock.bmp.
Commands used (24)
View source (78 lines)
rem ==================================================================
rem Rock Dodge - an OpenDBC tutorial for the DarkBASIC Classic profile
rem A small game: steer the scooter with the arrow keys between the
rem rocks drifting down a scrolling sea. SPRITE HIT reports each new
rem crash, SPRITE COLLISION tells whether the scooter is still touching
rem a rock. Without a key press the scooter steers by itself.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
randomize timer()
w = screen width() : h = screen height()
rem The sea lives in bitmap 1 and is copied to the screen every frame
load bitmap "sea.bmp", 1
set current bitmap 0
load image "scooter.bmp", 1
load image "rock.bmp", 2
rem The whole screen is redrawn each frame, so the sprites need no saved
rem background: SET SPRITE n, 0, 1 turns backsave off and keeps black
rem as the transparent colour
rocks = 6
dim rockx(rocks) : dim rocky#(rocks) : dim rockspeed#(rocks)
for r = 1 to rocks
rockx(r) = rnd(w - 48) : rocky#(r) = 0 - rnd(h) : rockspeed#(r) = 2 + rnd(30) / 10.0
sprite 1 + r, rockx(r), rocky#(r), 2
set sprite 1 + r, 0, 1
next r
px# = w / 2 - 32 : py = h - 90
sprite 1, px#, py, 1
set sprite 1, 0, 1
lives = 3 : score = 0 : flash = 0
do
rem Scroll the sea: copy two slices of bitmap 1 so the picture wraps
scroll = (scroll + 3) mod h
copy bitmap 1, 0, h - scroll, w, h, 0, 0, 0, w, scroll
copy bitmap 1, 0, 0, w, h - scroll, 0, 0, scroll, w, h
rem Steering: the arrow keys, or a simple pilot that avoids the closest rock
steer = rightkey() - leftkey()
if steer = 0 and leftkey() = 0 and rightkey() = 0
closest = 0 : best# = h
for r = 1 to rocks
gap# = py - rocky#(r)
if gap# > 0 and gap# < best# and abs(rockx(r) - px#) < 70 then best# = gap# : closest = r
next r
if closest > 0
if rockx(closest) > px# then steer = -1 else steer = 1
endif
endif
px# = px# + steer * 5
if px# < 0 then px# = 0
if px# > w - 64 then px# = w - 64
sprite 1, px#, py, 1
for r = 1 to rocks
rocky#(r) = rocky#(r) + rockspeed#(r)
if rocky#(r) > h
rockx(r) = rnd(w - 48) : rocky#(r) = -48 : score = score + 1
endif
sprite 1 + r, rockx(r), rocky#(r), 2
next r
rem SPRITE HIT only reports a crash once; SPRITE COLLISION stays true
rem while the sprites overlap
if sprite hit(1, 0) > 0 then lives = lives - 1 : flash = 20
touching = sprite collision(1, 0)
if lives = 0 then lives = 3 : score = 0
ink rgb(0, 0, 0), 0
box 0, 0, w, 24
ink rgb(255, 255, 255), 0
if flash > 0 then ink rgb(255, 80, 60), 0 : flash = flash - 1
text 10, 4, "LIVES " + str$(lives) + " ROCKS DODGED " + str$(score) + " TOUCHING ROCK " + str$(touching)
sync
loop
Sprite Lab - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/sprite/sprite_lab.dba · 58 lines (34 code)
Five scooters show the commands that change how a sprite is drawn: OFFSET SPRITE moves its handle, SCALE SPRITE and STRETCH SPRITE resize it in percent, SIZE SPRITE in pixels, MIRROR SPRITE and FLIP SPRITE turn it over. PASTE SPRITE draws a copy of a sprite as it looks right now, and PASTE IMAGE stamps an image onto the screen. Code and media are part of OpenDBC (MIT code, CC0 media).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Assets. Loads 3 media file(s) from the example folder:
sea.bmp, scooter.bmp, rock.bmp.
Commands used (31)
View source (58 lines)
rem ==================================================================
rem Sprite Lab - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Five scooters show the commands that change how a sprite is drawn:
rem OFFSET SPRITE moves its handle, SCALE SPRITE and STRETCH SPRITE
rem resize it in percent, SIZE SPRITE in pixels, MIRROR SPRITE and
rem FLIP SPRITE turn it over. PASTE SPRITE draws a copy of a sprite as
rem it looks right now, and PASTE IMAGE stamps an image onto the screen.
rem Code and media are part of OpenDBC (MIT code, CC0 media).
rem ==================================================================
sync on : sync rate 60
w = screen width() : h = screen height()
load bitmap "sea.bmp"
load image "scooter.bmp", 1
load image "rock.bmp", 2
rem Stamp a row of rocks along the bottom once: PASTE IMAGE with
rem transparency 1 skips the black background around each rock
for x = 0 to w step 56
paste image 2, x, h - 60, 1
next x
column = w / 6
for s = 1 to 5
sprite s, column * s - 32, 150, 1
next s
rem Sprite 1 spins its handle around the image centre instead of the corner
offset sprite 1, 32, 32
do
t = t + 1
rem 1. A handle in the middle: the sprite now orbits around its centre
sprite 1, column - 32 + 32 + cos(t * 3) * 30, 150 + 32 + sin(t * 3) * 30, 1
rem 2. Uniform scale from 50% to 150%
scale sprite 2, 100 + sin(t * 2) * 50
rem 3. Independent horizontal and vertical stretch
stretch sprite 3, 100 + sin(t * 3) * 60, 100 + cos(t * 3) * 60
rem 4. An absolute size in pixels
size sprite 4, 32 + (t mod 64), 64
rem 5. Mirror and flip every second
if t mod 60 = 0 then mirror sprite 5
if t mod 120 = 0 then flip sprite 5
rem PASTE SPRITE copies sprite 2 with its current scale; paste it every frame
paste sprite 2, column * 2 - 32, 290
ink rgb(0, 0, 0), 0
box 0, 0, w, 100
ink rgb(255, 255, 255), 0
text 10, 6, "1 OFFSET " + str$(sprite offset x(1)) + "," + str$(sprite offset y(1))
text 10, 26, "2 SCALE " + str$(sprite scale x(2)) + "%"
text 10, 46, "3 STRETCH " + str$(sprite scale x(3)) + "% x " + str$(sprite scale y(3)) + "%"
text 10, 66, "4 SIZE " + str$(sprite width(4)) + " x " + str$(sprite height(4)) + " pixels"
text w / 2, 6, "5 MIRRORED " + str$(sprite mirrored(5)) + " FLIPPED " + str$(sprite flipped(5))
text w / 2, 26, "PASTE SPRITE copies sprite 2 below it"
text w / 2, 46, "PASTE IMAGE drew the rocks below"
sync
loop
Tutorials — System
Window Lab - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/system/window_lab.dba · 75 lines (56 code)
Programs run full screen or in a window. SET WINDOW ON switches to a window, which the program can then title, move, resize, hide, show, minimise, maximise and restore. Every change waits for a key, so the program never moves your desktop around by itself: W window on/off T title S size P position L layout H hide (it comes back after two seconds) N minimise X maximise R restore A always active ESCAPE asks before quitting Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 user function(s):
on_off$. - Data. 1 array(s) declared with
DIM; constant tables in DATA blocks read with READ.
Commands used (36)
View source (75 lines)
rem ==================================================================
rem Window Lab - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Programs run full screen or in a window. SET WINDOW ON switches to a
rem window, which the program can then title, move, resize, hide, show,
rem minimise, maximise and restore. Every change waits for a key, so
rem the program never moves your desktop around by itself:
rem W window on/off T title S size P position L layout
rem H hide (it comes back after two seconds) N minimise X maximise
rem R restore A always active ESCAPE asks before quitting
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
rem EXIT PROMPT sets the message box shown when the program ends
exit prompt "Thanks for trying the Window Lab!", "Goodbye"
rem The message box only makes sense in a window, so escape is ours
disable escapekey
windowed = 0 : active = 0
dim title$(3)
title$(0) = "Window Lab" : title$(1) = "Hello from DarkBASIC" : title$(2) = "OpenDBC" : title$(3) = "Still here?"
log$ = "Press a key from the list"
do
key$ = lower$(inkey$())
if key$ <> lastkey$ and key$ <> ""
select key$
case "w"
windowed = 1 - windowed
if windowed = 1 then set window on else set window off
log$ = "SET WINDOW " + on_off$(windowed)
endcase
case "t" : t = (t + 1) mod 4 : set window title title$(t) : log$ = "SET WINDOW TITLE """ + title$(t) + """" : endcase
case "s" : big = 1 - big : set window size 480 + big * 320, 360 + big * 240 : log$ = "SET WINDOW SIZE" : endcase
case "p" : set window position 40 + rnd(300), 40 + rnd(200) : log$ = "SET WINDOW POSITION" : endcase
case "l" : layout = 1 - layout : set window layout layout, layout, 1 : log$ = "SET WINDOW LAYOUT " + str$(layout) + ", " + str$(layout) + ", 1" : endcase
case "h" : hide window : sleep 2000 : show window : log$ = "HIDE WINDOW, then SHOW WINDOW" : endcase
case "n" : minimize window : log$ = "MINIMIZE WINDOW" : endcase
case "x" : maximize window : log$ = "MAXIMIZE WINDOW" : endcase
case "r" : restore window : log$ = "RESTORE WINDOW" : endcase
case "a"
active = 1 - active
if active = 1 then always active on else always active off
log$ = "ALWAYS ACTIVE " + on_off$(active) + ": the program " + left$("keeps running", 13 * active) + left$("pauses", 6 * (1 - active)) + " in the background"
endcase
endselect
endif
lastkey$ = key$
if escapekey() = 1 then exit
cls rgb(30, 40, 60)
ink rgb(255, 210, 120), 0
center text screen width() / 2, 16, "WINDOW LAB"
ink rgb(230, 230, 240), 0
restore
for item = 0 to 10
read menu$
text 30 + (item mod 3) * 200, 60 + (item / 3) * 24, menu$
next item
ink rgb(120, 230, 160), 0
text 30, 180, log$
ink rgb(170, 170, 190), 0
text 30, 220, "Windowed " + str$(windowed) + " always active " + str$(active) + " screen " + str$(screen width()) + " x " + str$(screen height())
sync
loop
enable escapekey
end
data "W window on/off", "T title", "S size", "P position", "L layout", "H hide for 2 s"
data "N minimise", "X maximise", "R restore", "A always active", "ESCAPE quit"
function on_off$(flag)
result$ = "OFF"
if flag = 1 then result$ = "ON"
endfunction result$
Tutorials — Text
Secret Messages - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/text/secret_messages.dba · 72 lines (51 code)
Encodes a message with a Caesar shift, one character at a time, using MID$, ASC and CHR$, then takes it apart with LEFT$, RIGHT$, LEN, UPPER$, LOWER$, VAL and STR$, and shows each letter's code in decimal, HEX$ and BIN$. UP and DOWN change the shift. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Structure. 1 user function(s):
caesar$.
Commands used (26)
View source (72 lines)
rem ==================================================================
rem Secret Messages - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Encodes a message with a Caesar shift, one character at a time, using
rem MID$, ASC and CHR$, then takes it apart with LEFT$, RIGHT$, LEN,
rem UPPER$, LOWER$, VAL and STR$, and shows each letter's code in
rem decimal, HEX$ and BIN$. UP and DOWN change the shift.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
w = screen width()
message$ = "Meet me at the lighthouse at 11 tonight"
shift = 3 : changed = timer()
do
if upkey() = 1 and held = 0 then shift = (shift + 1) mod 26 : changed = timer()
if downkey() = 1 and held = 0 then shift = (shift + 25) mod 26 : changed = timer()
held = upkey() + downkey()
if timer() - changed > 2500 then shift = (shift + 1) mod 26 : changed = timer()
secret$ = caesar$(message$, shift)
cls rgb(20, 24, 34)
ink rgb(255, 210, 120), 0
text 20, 14, "SHIFT " + str$(shift) + " (UP and DOWN change it)"
ink rgb(230, 230, 230), 0
text 20, 44, "Message: " + message$
ink rgb(120, 220, 160), 0
text 20, 64, "Secret: " + secret$
ink rgb(230, 230, 230), 0
text 20, 84, "Decoded: " + caesar$(secret$, 26 - shift)
rem Cutting the message up
ink rgb(160, 190, 255), 0
text 20, 124, "LEN = " + str$(len(message$)) + " LEFT$(,4) = " + left$(message$, 4) + " RIGHT$(,7) = " + right$(message$, 7)
text 20, 144, "UPPER$ = " + upper$(message$)
text 20, 164, "LOWER$ = " + lower$(message$)
rem VAL turns the digits found in the text back into a number
digits$ = ""
for i = 1 to len(message$)
c$ = mid$(message$, i)
if asc(c$) >= 48 and asc(c$) <= 57 then digits$ = digits$ + c$
next i
text 20, 184, "Digits found: """ + digits$ + """ VAL + 1 = " + str$(val(digits$) + 1)
rem A table of codes, laid out with SET CURSOR and PRINT in a font
rem whose letters all have the same width
ink rgb(255, 255, 255), 0
set text font "Courier New"
set cursor 20, 224
print "CHAR ASC HEX$ BIN$"
for i = 1 to 8
c$ = mid$(secret$, i)
code = asc(c$)
set cursor 20, 224 + i * 20
print " " + c$ + " " + str$(code) + " " + hex$(code) + " " + right$(bin$(code), 8)
next i
set text font "Arial"
sync
loop
rem Shift the letters of text$ along the alphabet, keeping their case
function caesar$(text$, shift)
result$ = ""
for i = 1 to len(text$)
code = asc(mid$(text$, i))
if code >= 65 and code <= 90 then code = 65 + (code - 65 + shift) mod 26
if code >= 97 and code <= 122 then code = 97 + (code - 97 + shift) mod 26
result$ = result$ + chr$(code)
next i
endfunction result$
Type Specimen - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/text/type_specimen.dba · 72 lines (50 code)
Prints a specimen sheet for a font: its styles, a range of sizes and opaque versus transparent text. The font changes every few seconds; LEFT and RIGHT pick one yourself. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame. - Data. 1 array(s) declared with
DIM.
Commands used (30)
View source (72 lines)
rem ==================================================================
rem Type Specimen - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Prints a specimen sheet for a font: its styles, a range of sizes and
rem opaque versus transparent text. The font changes every few seconds;
rem LEFT and RIGHT pick one yourself.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 30
w = screen width() : h = screen height()
rem PERFORM CHECKLIST FOR FONTS lists the fonts this computer offers
perform checklist for fonts
installed = checklist quantity()
dim font$(4)
font$(0) = "Arial" : font$(1) = "Times New Roman" : font$(2) = "Courier New"
font$(3) = "Georgia" : font$(4) = "Verdana"
current = 0 : changed = timer()
do
if timer() - changed > 4000 then current = (current + 1) mod 5 : changed = timer()
if rightkey() = 1 and held = 0 then current = (current + 1) mod 5 : changed = timer()
if leftkey() = 1 and held = 0 then current = (current + 4) mod 5 : changed = timer()
held = leftkey() + rightkey()
cls rgb(246, 241, 230)
set text font font$(current)
set text transparent
rem The title, centred on the screen
ink rgb(120, 40, 30), 0
set text size 36 : set text to bold
center text w / 2, 14, font$(current)
rem The four styles, each reporting TEXT STYLE()
ink rgb(40, 40, 50), 0
set text size 20
for style = 0 to 3
select style
case 0 : set text to normal : name$ = "normal" : endcase
case 1 : set text to bold : name$ = "bold" : endcase
case 2 : set text to italic : name$ = "italic" : endcase
case 3 : set text to bolditalic : name$ = "bold italic" : endcase
endselect
text 30, 80 + style * 30, "Sphinx of black quartz - " + name$ + " (TEXT STYLE " + str$(text style()) + ")"
next style
rem A size ramp: TEXT WIDTH tells where the next sample starts
set text to normal
x = 30
for size = 10 to 46 step 6
set text size size
text x, 230 + 46 - text height("Ag"), "Ag"
x = x + text width("Ag") + 14
next size
rem Opaque text paints the INK background colour behind the letters
set text size 18
set text opaque
ink rgb(255, 255, 255), rgb(120, 40, 30)
text 30, 300, " SET TEXT OPAQUE: TEXT BACKGROUND TYPE() = " + str$(text background type()) + " "
set text transparent
ink rgb(120, 40, 30), 0
text 30, 330, " SET TEXT TRANSPARENT: TEXT BACKGROUND TYPE() = " + str$(text background type()) + " "
ink rgb(90, 90, 100), 0
set text size 14
text 30, h - 60, "TEXT FONT$() = " + text font$() + " TEXT SIZE() = " + str$(text size())
text 30, h - 40, str$(installed) + " fonts found by PERFORM CHECKLIST FOR FONTS - LEFT and RIGHT change font"
sync
loop
Typewriter - an OpenDBC tutorial for the DarkBASIC Classic profile dbcExample
examples/dbc/tutorials/text/typewriter.dba · 61 lines (44 code)
Types a short story one character at a time, wrapping the words itself with TEXT WIDTH, while a news ticker scrolls along the bottom of the screen. SPACE types the whole page at once. Code is part of OpenDBC (MIT).
How it is built
- Program shape. Frame-driven: a main
do … loop runs every frame under sync on, so the program calls sync itself to present each frame.
Commands used (19)
View source (61 lines)
rem ==================================================================
rem Typewriter - an OpenDBC tutorial for the DarkBASIC Classic profile
rem Types a short story one character at a time, wrapping the words
rem itself with TEXT WIDTH, while a news ticker scrolls along the bottom
rem of the screen. SPACE types the whole page at once.
rem Code is part of OpenDBC (MIT).
rem ==================================================================
sync on : sync rate 60
w = screen width() : h = screen height()
set text font "Courier New" : set text size 18
story$ = "The keeper climbed the hundred steps of the lighthouse as the storm rolled in. "
story$ = story$ + "Far out on the water a small scooter was racing for the harbour, its lamp "
story$ = story$ + "blinking between the waves. The keeper lit the great lantern, and the beam "
story$ = story$ + "swept across the sea once, twice, three times. When the rain finally came, "
story$ = story$ + "the scooter was already tied up at the quay, and its rider was waving."
ticker$ = " +++ STORM WARNING FOR THE COAST TONIGHT +++ HARBOUR OPEN UNTIL MIDNIGHT +++ LIGHTHOUSE BEAM CHECKED AND WORKING "
shown = 0 : tick# = 0.0
do
rem Reveal one more character every other frame
if spacekey() = 1 then shown = len(story$)
if shown < len(story$) then shown = shown + 1
if shown = len(story$) then pause = pause + 1
if pause > 240 then shown = 0 : pause = 0
cls rgb(250, 247, 238)
ink rgb(40, 40, 40), 0
center text w / 2, 16, "THE LIGHTHOUSE KEEPER"
rem Word wrapping: add the next word to the line only if it still fits
margin = 40 : y = 60 : line$ = "" : word$ = ""
for i = 1 to shown
c$ = mid$(story$, i)
word$ = word$ + c$
if c$ = " " or i = shown
if text width(line$ + word$) > w - margin * 2
text margin, y, line$
y = y + text height(line$) + 6
line$ = ""
endif
line$ = line$ + word$
word$ = ""
endif
next i
rem The blinking cursor sits after the last character typed
if (timer() / 400) mod 2 = 0 then line$ = line$ + "_"
text margin, y, line$
rem The ticker: draw the text twice so that it loops without a gap
ink rgb(30, 30, 50), 0
box 0, h - 40, w, h
ink rgb(255, 220, 90), 0
tick# = tick# + 2.0
loop_width = text width(ticker$)
if tick# > loop_width then tick# = tick# - loop_width
text 0 - tick#, h - 30, ticker$
text loop_width - tick#, h - 30, ticker$
sync
loop