**The Brick Engine** '''The Brick Engine''' is a fast cross-platform 2D video game engine. The Brick Engine lets you make fast-paced, action-packed games written in Tcl (as well as a variety of other languages). It supports unlimited sprites, scrollable tile-based maps, songs, sounds, keyboard and joystick input. It is under active development by [hat0] as of February 2011. The v5.3 version was released on February 19th, 2011! The brick engine includes many features that make all types of video games possible to the tcl programmer: a variety of sprite frame types (both RGB pixel data and filter types such as a brightness/contrast adjustment frame, a convolution kernel, saturation/desaturation, etc), NPC introspection (including line-of-sight tests and a rich collision detection system), and even a tiny embedded custom language that you can use to give sprites a greater degree of autonomy outside the slow scripting world of Tcl. Multi-player gaming is a breeze, with an input system that supports any combination of keyboard and joysticks, and of course network play is no problem thanks to [socket] and friends. The engine can be built as a standalone executable with Tcl embedded, or as a loadable module. The web site is here: http://rs.tc/br/ ***Games and Demos*** There are two fully playable game demos on the brick engine site now: * Zeml'a kroshechnykh mashin - The Land of Tiny Machines. This is a side-scroller feat. multiple weapons, enemies with different behaviors, music and sound, all in under 1600 lines of Tcl. Controls are: left ctrl/alt to jump and shoot. Z and X to switch weapons. Arrow keys to move. Backspace to quit out of the game immediately. Linux kit is at http://rs.tc/br/lib/exe/fetch.php/games:hat0:zkm.gz and Windows kit is at http://rs.tc/br/lib/exe/fetch.php/games:hat0:zkm.exe (no OS X release yet). * Escape. An overhead-view maze game, with multiple levels and some nice visual effects. Arrow keys to move, escape to quit out of the game immediately. Linux kit is at http://rs.tc/br/lib/exe/fetch.php/games:hat0:maze.gz and Windows kit is at http://rs.tc/br/lib/exe/fetch.php/games:hat0:maze.exe (no OS X release yet). For both games, if you have a joystick, you can use that instead of the keyboard controls. Source code is also provided for ZKM. ''Note that you must have SDL, SDL_mixer, and SDL_image installed.'' If you'd just like to see a few visual demos, showing off how quickly and easily you can make games with this program, please visit http://rs.tc/br/doku.php/demos . **The ZKM Base Classes** Want an easy way to get started with writing Brick Engine games? Take yourself a look at the http://rs.tc/br/doku.php/zkm%|%ZKM Base Classes%|%. These [incr Tcl]-based classes give you a variety of tools to make game writing even quicker and easier. (note to future hat0: this could probably go on its own page..) ---- [AMG]: Is this related to Five Thousand Italians? [http://yellow5.com/pokey/pcs/5000.zip] [http://yellow5.com/pokey/pcs/5000.tar.gz] [http://yellow5.com/pokey/pcs/5000_osx.tar.gz] [hat0]: Yes, that project uses an early version of the brick engine! ---- [Googie] - 13.10.2008 - Amaizing! I was working on something similar months ago but I've dropped it. Brick looks like mature, powerful and as easy to use as Tcl itself :) I can't find information about GL acceleration - is it supported? I don't mean 3D graphics, but 2D GL-accelerated (like [TkZinc] does). [hat0] - Thank you for the kind words - if you make a project I'd love to hear about it! As for GL acceleration, brick uses it only for the final blit to screen, to give fast performance on the various platforms, and to allow for a resized display window. SDL's native blit is, of course, terribly slow, and does no resizing at all. I have done a little work on a fully-OpenGL rendering pathway, using various ARB extensions to reproduce the sprite filters (such as convolution, available in the Imaging extension, and the colorspace manipulations), but haven't gone too far in this. To be fully honest about it, I only discovered recently that I was compiling the brick engine without any compiler optimization. After turning the optimizations on (-O3), I myself was very surprised at its performance.. (and this is when I lost some interest in the OpenGL work!) [Googie] - 16.11.2009 - I've just tried out the demo game and I want to give some feedback. Escpecially about installation process - I have Linux 64-bit, so I had to compile from sources and I couldn't manage to tell CMAKE where to look for tcl includes and library. I had to manually edit CMakeCache.txt and put paths in there. Some INSTALL file would be nice. Another thing is that I got a lot of warnings about casting integer 32bit into pointer (which is 64bit on my system), but nevertheless library is working correctly. Also 'make install' didn't install brick Tcl engine anywhere. When I managed to get it working - I like the results :) I have to spent some time with it to discover its possibilites, but demo game looks nice for 8kb of code (another 7kb is data, not the essential code). Keep good work! ---- [hat0] - And here is a short game, my first attempt at making the smallest playable game (both for fun, and to see where the API could be improved). sloccount counts it at 164 lines of code, and including comments and whitespace, I count 344 lines. 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 [ZB] Just tried the game, and I noticed, it eats power of my weak processor (Pentium II 400) completely. Is it the brick engine itself, that needs so much power - or perhaps the code of the game (didn't make any analysis yet) can be somewhat improved (loops most probably)? [hat0] - It's partly the brick engine, and partly Tcl. The 4.1 release, just finished, contains a variety of speed improvements which will boost the performance on older hardware, so I would invite you to give the new release a go and see how it runs. Any optimizations to the Tcl are, of course, totally and absolutely welcome! [ZB] 2009-09-16 Gave it a try, but release 4.1a doesn't produce "br" executable anymore. It made libbr.so.4.1, a symlink libbr.so - and nothing more. Is it now just a library, and not executable? [hat0] - Hi ZB, my apologies both for not answering sooner, and for not making it clearer on the download page: I've split the engine and language bindings into separate downloads now. So, the libbr.so.4.1 is the engine itself, and to get the 'br' executable you'll just need to download and compile the Tcl bindings. They'll give you the option both to build the 'br' executable and the br.so loadable module (and accompanying pkgIndex file). Please let me know if you have any problems getting the Tcl bindings built! [ZB] 2009-11-12 Not sure, how to install. Made the library, after "make install" it said: [100%] Built target br Install the project... -- Install configuration: "" -- Installing: /usr/local/lib/libbr.so.4.1 -- Up-to-date: /usr/local/lib/libbr.so -- Installing: /usr/local/include/brick.h Then I was trying to compile bindings, but it's complaining: [..] -- Found TK: /lib/libtk8.6.so CMake Error at extra/Findbrick.cmake:60 (message): Could not find brick Call Stack (most recent call first): src/CMakeLists.txt:13 (find_package) [hat0] 2009-11-14 Hm. that's tough. Can you confirm that the header and library were placed into those directories, and are world-readable? The file /extra/Findbrick.cmake contains a set of search paths to find the Brick header/library (in order to build the bindings), and it seems that it can't see the brick you've installed.. [ZB] 2009-11-15 Oh, yes... being confident, that it did its job, I didn't check it; indeed: there wasn't any libbr* file in /usr/local/lib, no idea, why. It did write "Installing", anyway. After I've copied library into that sub-dir, brick engine seems to be answering. One more thing: the game example, given below, needs a little fix; when trying to run it, I've received another error: Tcl quit with an error: wrong # args: should be "graphics open accel w h fullscreen zoom-factor " Tried: graphics open accel 640 480 0 1 But after that change, there is new error: Tcl quit with an error: unknown or ambiguous subcommand "add-tile": must be animate-tiles, create, destroy, info, reset-tiles, set-data, set-single, size, tile, or tile-size Not sure, which subcommand replaced "add-tile"; no such "update" comments in api-quickreference. [hat0] 2009-11-15 Hi ZB, thanks for letting me know the game here wasn't working! My apologies. The API changed a bit from the 3.x release to the 4.x release (well, it actually changed quite a bit), and the game code on this page was for the older version. I've updated it now to work on the 4.1 release - please give it a go and let me know if it works for you! (Also wanted to mention once again, that the games on the Games page are just starkits, and can be unpacked and examined with a quick `sdx unwrap`..) [ZB] Thanks, now it works without problems. :) As I can see, it needs much less CPU by now, although still CPU utilization is rather high (about 80% of Pentium III 700). Not sure, anyway, is it brick engine itself - or the game code rather. During following days I'll try to take a closer look. ---- ====== namespace import br::* # # first set up the graphics and audio # graphics open accel 320 240 off 2 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 # set t1 [tile create] set t2 [tile create] set t3 [tile create] tile add-frame $t1 rgb 8 8 [binary format H384 AAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCCAAAAAAAAAAAAAAAAAAAAAAAACCCCCCCCCCCCCCCCCC] tile add-frame $t2 rgb 8 8 [binary format H384 DDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDCCCCCCCCCCCCCCCCCC] tile add-frame $t3 rgb 8 8 [binary format H384 CCCCCCCCCCCCCCCCCCAAAAAACCCCCCCCCCCCCCCCCCAAAAAACCCCCC888888888888444444CCCCCC888888888888444444CCCCCC888888888888444444CCCCCC888888888888444444AAAAAA444444444444444444AAAAAA444444444444444444CCCCCCCCCCCCCCCCCCAAAAAACCCCCCCCCCCCCCCCCCAAAAAACCCCCC888888888888444444CCCCCC888888888888444444CCCCCC888888888888444444CCCCCC888888888888444444AAAAAA444444444444444444AAAAAA444444444444444444] tile collides $t3 box map tile-size $layers(bg.map) 8 8 map tile $layers(bg.map) 1 $t1 map size $layers(bg.map) 40 30 map set-data $layers(bg.map) [binary format H4800 [string repeat 0100 1200]] # and the playfield map tile-size $layers(main.map) 8 8 map tile $layers(main.map) 1 $t2 map tile $layers(main.map) 2 $t3 map size $layers(main.map) 46 32 map set-data $layers(main.map) [binary format H5888 [string map {"-" 0100 "1" 0200} 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 3 3 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 3 3 sprite load-program $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 load-program $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 [collision 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 [collision 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 [collision map $id $layers(main.map) 1] 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 [collision map $id $layers(main.map) 1] 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 [collision 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 [collision map $enemy $layers(main.map) 1] 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] } { render display-frame ; 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)}] render display-frame delay 50 } # the game-over message show_msg "You have perished" ====== OldCoder: 2010-10-04: I've ported the preceding game to the Brick Engine 5.2 API, restructured the code, and added some new features. The new version supports multiple worlds, both random and invariant maps, compressed inline data (including background music), and other features that may be useful to developers. Additionally, the enemies are a bit more intelligent now. Steve knows about the new version of the game and has, in fact, modified the Brick Engine Tcl interface to make a couple of features possible. If you'd like to get the source code, including the required patch for the Tcl interpreter, you'll find everything on the following web page: http://tools3110.99k.org/general/beworld/ Questions and comments may be sent to the E-mail address shown on the page. The game as it stands is just a prototype. However, if there's any significant interest, I'll continue to add new features. OldCoder: 2010-10-11: The game described above (BEWorld) has been updated. There's some new objects (cows, gold coins, and trees), better sound compression (5X to 10X in some cases), an improved configuration layer, the start of a player inventory (i.e., a backpack), additional code restructuring, and more documentation. Cows are simply grazers who happen to eat red enemies. The player can hide behind trees, subject to limitations described in the changelog. Gold coins were added as an exercise; there's no way to spend them yet. Individual objects can be named now. For example, every red enemy has a mostly-unique name (overlaps will occur in some cases). The program is still a single self-contained Tcl script. Admittedly, a large one. :-) All data files (including music and sound effects) are stored inline as compressed and base64-encoded data blocks. The code is pure Tcl plus Brick Engine Tcl 5.2. I've avoided the use of other external libraries; for example, the LZ77 decompression and base64 decoding routines are included as part of the program. The license is still MIT/X. However, I've thought about switching to Creative Commons for future releases (as I understand it, the original license allows this). I've used Tcl occasionally, but this is probably the largest pure-Tcl project that I've attempted. It's worked out quite well; Tcl has proved to be more flexible than I realized. This wiki page has gotten fairly long due to the inclusion of the original source code. If there's no objection, I may start a separate wiki page related specifically to BEWorld. OldCoder: 2011-09-30: If Tcl coders are interested, there's a new release of BEWorld available. It's about 100 pages of Tcl code now, not counting embedded data or blank lines. Link is the same as last year: http://tools3110.99k.org/general/beworld/ A specific version of the Brick Engine (including Tcl bindings) is required, but it's provided in the source bundle. The patch mentioned in previous copies of this page is no longer needed. Changes: Animations are now supported. Objects can change shape. Added drop shadows, a power-up, a new level, and new objects: cars, dogs, pigs, tigers, and international currencies. The game no longer needs OpenGL. Objects can use different sounds under different circumstances. There is now a goal: Make it to the exit. License has changed to Creative Commons (with the consent of the lead Brick Engine developer). ---- !!!!!! %| [Category Games] | [Toys and Games] |% !!!!!!