A (very simple) Brick Engine game FROM: http://wiki.tcl.tk/19800 (imaged on 2009 mar 19) Here is a short game, a first attempt at making the smallest playable game (both for fun, and to see where the API could be improved). To try it out, you can save this into a directory (say, as game.tcl) with the brick executable for your platform, and then run the engine like so: ./br game.tcl --------- game.tcl: namespace import br::* # # first set up the graphics and audio # graphics open accel off 640 480 audio open speaker # # then create the layers and store some layer values for convenience # foreach l { bg main } { set layers($l) [layer add] set layers($l.spr-list) [lindex [layer info $layers($l)] 0] set layers($l.map) [lindex [layer info $layers($l)] 1] set layers($l.str-list) [lindex [layer info $layers($l)] 2] } # # create our maps - first, the background # map tile-size $layers(bg.map) 8 8 map add-tile $layers(bg.map) 1 [binary format H384 AAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCC] map set-data $layers(bg.map) 40 30 [binary format H4800 [string repeat 0001 1200]] # and the playfield map tile-size $layers(main.map) 8 8 map add-tile $layers(main.map) 0 [binary format H384 DDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCC] map add-tile $layers(main.map) 1 [binary format H384 CCCCCCCCCCCCCCCCCCAAAAAACCCCCCCCCCCCCCCCCCAAAAAACCCCCC888888888888444444CCCCCC888888888888444444CCCCCC888888888888444444CCCCCC888888888888444444AAAAAA444444444444444444AAAAAA444444444444444444CCCCCCCCCCCCCCCCCCAAAAAACCCCCCCCCCCCCCCCCCAAAAAACCCCCC888888888888444444CCCCCC888888888888444444CCCCCC888888888888444444CCCCCC888888888888444444AAAAAA444444444444444444AAAAAA444444444444444444] map set-data $layers(main.map) 46 32 [binary format H5888 [string map {"-" 0000 "1" 0001} 11111111111111111111111111111111111111111111111----------------------------111-------------11--111111111-----1-----------111-------------11--1------------1------------111-------------11--1-1111111-------------1---111-------------11--1-1-1------1----1---------111-------------11--1---1------1----1---------111-------------11--11111------1-------1------111-------------11----1----------------1111111111-------------11----1----111----------------111-------------11----1---11------------------111-------------11----1---11-----11-----------11--------------11----1-----------------------111-----11------11--------11----------------1111111111111-----11--------11111111111------1111---------------11--------------------------1111111111111-----11---11-----------------------111-------------11--------1------11---1-------11-------------111----1--------1-------1------1-----------1--111--1------------------1------------------1--111---1----111----------1----------1-------1--111--------1-----------1---1111------------1--111-------11------1--------1-------1--1----1--111---------------------1111---------------1--111-------------1-------1--1111------1-----1--111--1111----------1-------1-------1----1--1--111--1111----1----11-------1------------1--1--111--1111---------------1111111-------111--1--111--1111--------111----1--1-------1111----1--111---------------------1--1-------1-----111--111----------------1-------1111---------------111111111111111111111111111111111111111111111111]] # # create some sprites to use as prototypes for the game # set proto(player) [sprite create] sprite collides $proto(player) box sprite add-frame $proto(player) rgb 3 3 [binary format H54 0000FFFF00FF0000FFFF00FFFF00FFFF00FF0000FF0000FF0000FF] 255 0 255 sprite bound $proto(player) 0 0 0 2 2 set proto(enemy) [sprite create] sprite collides $proto(enemy) box sprite add-frame $proto(enemy) rgb 3 3 [binary format H54 FF0000FF00FFFF0000FF00FFFF00FFFF00FFFF0000FF0000FF0000] 255 0 255 sprite bound $proto(enemy) 0 0 0 2 2 sprite mcc $proto(enemy) { add xpos, xvel add ypos, yvel } set proto(bullet) [sprite create] sprite collides $proto(bullet) box sprite add-frame $proto(bullet) rgb 1 1 [binary format H6 000000] sprite bound $proto(bullet) 0 0 0 1 1 sprite mcc $proto(bullet) { add xpos, xvel add ypos, yvel } # # and load the sound effect for the gunshot # sound load-raw [binary format H4428 807d81877c7f96948492827e938687868a8576777d8e796f7e7b797e7d72717e857467707f7a7677787f7a82887d7f7b787a7786827a848a8c8b8d8a888381878a8b80838a84878384867f848e888683879080828888897c757d7f7778767672727b7d7f808483828c9185797b84847b7a7f81868a8a847f8687827e8285878a7e7772766a7280757e6f718575757f7d7d76757a7d7d7676756d7375717a787d8184877d7f7d7a7375798181777e7d847e76838f8a7675797d868b857e79787c7c827f787c7f7b75747d7d787d808380888f8d888382828580797873717e757074727d8283837f81888275797b757e898181827b7a857d747a7d817a7c7a747b7d71717e7b79828d8479727581838b8680857c7d81817b77807c7e7c7b7c757974818d8a88868686828288888b8b85878a87858d8a8283878b8b92968d88908f8d867b878c8b908a89877f81828184878a8c837c818280828684807d7a7f858683878787837e8685817f84887d7e817b79787d7b787f7f7d7b7e807c7f7c787e7984928a8680838985837a787a74757d817c7f7d777b7378787a7f7576797d7e7f7c7a7e807a787d7b7c7b7d7b80847d7b7c7c7d7e7c7a7a7f7d766d6c75777a7e81817f7d7b7c7e7c7f7f8183838383847879837c81827b807a7a827c787d7d797779818580797c797578767776727272787a77797776757576747a7d8084837f82828189888080827f807c7b7f7c7f7f7b797a79797a7779787e817c7c7b787979787b81828281807e7e81807f7d7c7c797c7e7c8286817f807e80827f82828082807f837c7e817f847e7d7c7b7e7f8180827d7c7e7879797a7a7b7f8184838689858383858180848886807e7d7e8183838284847b7b7c7e817f808185827e7f7b7b7c7c7f7f7d7d7e7e81837e7e7e7e7c7a7b7f85828385827d7d7b767978777c82837e8183807e7e7d797b7c7c7c7a7b787b7e817f787a7d7f7d7b7d7c7a7b7c7b7c7d7e7f7f7b7b7e7f807f818383837f7d7e7f818586878580818380818381807f8081817f7f8182828080807f7f82818081807c7d7e7c7d7e7f8181807f7d7d7d7e7d7d7e7c7c7b7c7c7d7f7d7c7c7d7c7b7c7b7c7e7f7e7e7e7f7f80807d7e7d7d7c7c7d7d7d7d7c7b7d7f7f7d7c7e7f808283807e7e7d7c7d7c7d807f7e7e8080818181807f8284848586838082818081807e808080818080807f7e7f7e7f7e807f7e7e7d7e7e7d7d7d7c7d7d7d7d7c7d7e7d7a79797c7a797c7e7e7d7e7e7d7e7e7d7d7c7c7e7d7b7d7e7e7e7e7f7e7e7f7e7a7b7d7c7d7e7f7d7f807e7d7d818180807e7f7e7e7f7f7f8081828281807e7e7c7d7f808180808081838181838482828282827f7f807f80807f7f81808081807f7e8181818381818183848383838283838282828183848283838283858483848482817f7f80807e7f80807f8082828180818182848382828181828281808082807e7e7d7d7c7a7b7a7b7c7c7b7c7d7d7f808080808283828283848383848282838381808080818282807f7f81807f808182817f7f7f807f7f8180808080808181807e7d7d7d7c7b7d7e7e7f7f7f7f8081807e7e7f7e7f8182818080818181818080808181808080808181807f8080818181808182807e7f7f80807e7f7f7f80807f807e7c7c7b7c7c7f8080807f80818181818080808080808182817f7e7d7e7e7f807f80818181807f7f80807f80807f807f7f7f7f80807f7e80807f7f8180807e7d7e7e7d7f7f7f8080808181807f7f807e7c7c7c7d7d7c7c7d7e7e7d7e7e7e7e7e7f7e7f807e7d7d7e7e7d7d7e7e7f7e7d7e7f7f81818080807f7f7f7e7d7d7e7d7d7d7d7e7e7e7f7f7e7f7f7e7f80807f7f7f7f807f7f808081818080807f7f7f80818181807f7f7f7f8080808080807f7e7f7f81818181818181818182838383818181818180818180808081828281818182838280808181808080818181828282828181807f8182818383818181807f8080807f7f7f7f7f807e7f7e7f808181807f7f7f7e7d7d7d7f7e7d7d7c7d7c7e7e7d7f7f7f7f7f7f7e80817f80818181817f80818181808081818281808080807f80807f7e7e7e7f7e7e7d7c7c7e7f80807f7f7f7e7e7e7f80807f7f7f818282808081807f7f7f7f7f7f7d7c7d7f807f7f7f7e7f7f7f7f7f7f7f7f7f7f7f807f8080808080807f807f7f80807f7f7f7f7e80807e7e7f7f7e7e7e7e7e7d7c7c7c7c7d7c7d7e7f7f7f7f7f7f80807f7f7e7e7f7f7f7f7f7f807f7f7e7f7f807f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7e7f8080818181818180808080808080807f7e7e7e7e7f7f7f7f8080818180807f7f7f7f7f7f7f7f7f8080808080807f7f80818180807f807f807f7f8080807f7f7f7f7e7e7f8080808080807f7f7f8080808081818181808080807f8080808081807f7f7f807f80807f7f7e7f7f7f7f7f7f7f7f7e7f80807f7f8081808181818080807f8080807f7f7f7f7f7f7f7f7f8080808080807f7f7f7f7f7f7f7f7e7e7e7f7f7f7f7e7f80808080808080807f7f7f7f80808080807f807f7f7f7f7f7f7f7f807f7f7f7f7f808081818181808181808080808080808181818281818181818180808080807f7f7f7f7f7f7f7f7f807f7f7f7f7f7f7e7f7f7e7e7f7f7f7f7f8080807f7f7f7f808080808081808080808080807f80808181807f7f7f7f808080807f7f807f80807f7f7f7f7f7f7f8080807f7f7f7f808080818181818181818080808080808080808080808081818181818081808080807f7f7f7f80808080808080808080808080808080808080818180808080807f7f7f7f7f7f80807f7f7f7f7f7f7f7f7f80807f7f80807f7f7f7f7f7f7f7f7f80807f7f7f7f7f8080808080808080807f80807f7f7f7f7f80808080808080807f7f7f7f7f8080807f8080807f7f7f808080807f7f7f7f7f7f7f7f7f7f8080807f7f7f7f7f7f7f808080808080807f80807f7f7f7f7f7f7f7f7f7f7f807f807f8080807f7f7f7f7f7f7f7f7f7f7f7f7f7f807f80807f7f7f80807f7f7f807f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f7f807f7f7f7f7f7f7f7f7f7f7f7f7f8080807f7f808080807f7f8080808080808080] gunshot # # the sprite control procs - first, the player # proc run_player { id } { global proto sdata layers # fetch input set io [io fetch 0] set horiz [lindex $io 0 0] set vert [lindex $io 0 1] if { [lindex $io 7] || [io quit] } { exit } # get player movement set vx [expr { $horiz < 0 ? -1 : ($horiz > 0 ? 1 : 0) }] set vy [expr { $vert < 0 ? -1 : ($vert > 0 ? 1 : 0) }] sprite vel $id $vx $vy # check for collision with walls set colls [collisions map $id $layers(main.map) 1] set vx [expr {[lindex $colls 1] + [lindex $colls 3]}] set vy [expr {[lindex $colls 2] + [lindex $colls 4]}] # set new position incr sdata($id.px) $vx incr sdata($id.py) $vy sprite pos $id $sdata($id.px) $sdata($id.py) # if there is any movement, save the direction for possible shooting if { $horiz || $vert } { set sdata($id.gx) $vx set sdata($id.gy) $vy } # and if a shot is fired .. if { [lindex $io 2 0] } { # and the trigger hasn't been held down if { !$sdata($id.shot) && ($sdata($id.gx) || $sdata($id.gy)) } { # create the bullet set bullet [sprite clone $proto(bullet)] sprite pos $bullet [expr {$sdata($id.px)+1}] [expr {$sdata($id.py)+1}] sprite vel $bullet [expr {$sdata($id.gx)*2}] [expr {$sdata($id.gy)*2}] # and add it to the lists brlist add $layers(main.spr-list) $bullet set sdata($bullet.) run_bullet set sdata($id.shot) 1 # and play the sound sound play gunshot } } else { # reset the trigger for the next shot set sdata($id.shot) 0 } # track player with camera layer camera $layers(main) [expr {$sdata($id.px) - 160}] [expr {$sdata($id.py) - 120}] } # the bullet control proc proc run_bullet { id } { global sdata layers # run the motion program to move the bullet motion single $id # check for collisions with enemies foreach tgt [collisions sprites $id $layers(main.spr-list)] { set tgt_id [lindex $tgt 1] if { $sdata($tgt_id.) eq "run_enemy" } { # enemy hit! update score and remove enemy incr sdata(score) brlist remove $layers(main.spr-list) $tgt_id sprite destroy $tgt_id array unset sdata $tgt_id.* # and flag this bullet for removal set remove_bullet YES } } # and check for bullet removal or collisions with walls if { [info exists remove_bullet] || [lindex [collisions map $id $layers(main.map)] 0] } { brlist remove $layers(main.spr-list) $id sprite destroy $id array unset sdata $id.* } } # the enemy control proc proc run_enemy { id } { global sdata layers # increment the enemy counter incr sdata($id.ct) # and move the enemy once every five ticks if { !($sdata($id.ct) % 5) } { switch $sdata($id.dir) { 0 { set vx 0; set vy -1 } 1 { set vx 1; set vy 0 } 2 { set vx 0; set vy 1 } 3 { set vx -1; set vy 0 } } sprite vel $id $vx $vy # check to see if we've hit a wall or if we are changing direction at random if { [lindex [collisions map $id $layers(main.map)] 0] == 1 || rand() > .99 } { # yes? ok! pick a new direction at random set sdata($id.dir) [expr {int(rand()*4)}] } else { # no change in direction? run the motion-program to move the enemy sprite motion single $id } } # check for collision with the player foreach tgt [collisions sprites $id $layers(main.spr-list)] { set tgt_id [lindex $tgt 1] if { $sdata($tgt_id.) eq "run_player" } { # player hit! decrement health incr sdata(health) -1 } } } # and the proc to generate new enemies at random proc new_enemies {} { global proto sdata layers # only seldom create a new enemy if { rand() > .994 } { # create and initialize the enemy set enemy [sprite clone $proto(enemy)] array set sdata [list $enemy. run_enemy $enemy.ct 0 $enemy.dir [expr {int(rand()*4)}]] brlist add $layers(main.spr-list) $enemy # and position it, with a guarantee that it isn't in any walls while 1 { sprite pos $enemy [expr {int(rand() * (46*8))}] [expr {int(rand() * (32*8))}] if { ![lindex [collisions map $enemy $layers(main.map)] 0] } { break } } } } # # initialize the player sprite # set player [sprite clone $proto(player)] array set sdata [list $player. run_player $player.px 10 $player.py 10 $player.gx 0 $player.gy 0 $player.shot 0] brlist add $layers(main.spr-list) $player # # set up the heads-up display # set stg_x 10 set stg_y 10 foreach stg { time health score } { set sdata(stg.$stg) [brstring create] brstring position $sdata(stg.$stg) $stg_x $stg_y brlist add $layers(main.str-list) $sdata(stg.$stg) incr stg_y 8 } # and a proc to show a message and wait for keypress proc show_msg { text } { global layers set stg [brstring create] brstring position $stg 80 80 brstring text $stg $text brlist add $layers(main.str-list) $stg while { ![lindex [io fetch 0] 5] } { graphics render ; after 25 } brlist remove $layers(main.str-list) $stg brstring destroy $stg } # set traces to keep heads-up display updated trace add variable sdata(time) write {apply {{a1 a2 op} { upvar 1 $a1 a ; brstring text $a(stg.time) "Time |[clock format $a(time) -format %M:%S]" }}} trace add variable sdata(health) write {apply {{a1 a2 op} { upvar 1 $a1 a ; brstring text $a(stg.health) "Health|$a(health)" }}} trace add variable sdata(score) write {apply {{a1 a2 op} { upvar 1 $a1 a ; brstring text $a(stg.score) "Score |$a(score)" }}} # # the main game loop itself - first, show the greeting msg # show_msg "Welcome to hell!\r\nUse arrow keys\r\nto move, ctrl to\r\nshoot, esc to\r\nquit.\r\n\nPress enter to begin." # and set these data (which will fire the traces and set up the heads-up display) set sdata(health) 100 set sdata(score) 0 set sdata(start_time) [clock seconds] # the main loop while { $sdata(health) > 0 } { # run the callbacks for the sprites foreach { id callback } [array get sdata *.] { # make sure that sprites that are removed from play mid-loop are not called if { [info exists sdata($id)] } { $callback [string trim $id .] } } # and take care of accessory business new_enemies set sdata(time) [expr {[clock seconds] - $sdata(start_time)}] graphics render delay 50 } # the game-over message show_msg "You have perished"