namespace eval car {variable maxSteer 0.25 cars ""} proc car::new {name c color {keys {<Left> <Right> <Up> <Down>}}} { variable cars lappend cars $name interp alias {} $name {} car::dispatch $name namespace eval $name { variable angle 0 frontangle 0 speed 0 } namespace eval $name [list variable canvas $c] interp alias {} $name: {} namespace eval ::car::$name $c create line 44 55 66 55 -tag $name -width 2 $c create line 44 80 66 80 -tag $name -width 3 $c create poly 51 45 48 87 62 87 59 45 -fill $color -tag $name wheel $name $c 44 55 left$name wheel $name $c 66 55 right$name wheel $name $c 44 80 wheel $name $c 66 80 $c create poly 52 67 52 73 58 73 58 67 -smooth 1 -fill white \ -tag $name ;# driver's helmet foreach key $keys action { {steer +.04} {steer -.04} {accelerate +1} {accelerate -1} } { bind . $key [concat $name $action] } } proc car::dispatch {name cmd args} {eval ::car::$cmd $name $args} proc car::wheel {name c x y {tags ""}} { set dx 3; set dy 6 set x0 [expr {$x - $dx}] set y0 [expr {$y - $dy}] set x1 [expr {$x + $dx}] set y1 [expr {$y + $dy}] $c create poly $x0 $y0 $x0 $y1 $x1 $y1 $x1 $y0 -fill black\ -tag [lappend tags $name] } proc car::accelerate {names amount} { if {$names == "all"} {variable cars; set names $cars} foreach name $names { if $amount { if {[$name: set speed]>-1 || $amount > 0} { $name: incr speed $amount } } else {$name: set speed 0 ;# emergency stop} } } proc car::steer {name amount} { variable maxSteer set fa [$name: set frontangle] if {abs([$name: set angle] - ($fa + $amount)) < $maxSteer} { $name: set frontangle [expr {$fa + $amount}] canvas'rotate [set ${name}::canvas] left$name $amount canvas'rotate [set ${name}::canvas] right$name $amount } } proc car::move {} { variable cars set title TclRacing foreach name $cars { set c [$name: set canvas] ;# always the same, though... set mean [expr {([$name: set angle]+[$name: set frontangle])/2.}] set speed [$name: set speed] set amount [expr {($mean - [$name: set angle])*$speed/5.}] canvas'rotate $c $name $amount $name: set angle [expr {[$name: set angle] + $amount}] $name: set frontangle [expr {[$name: set frontangle] + $amount}] set dx [expr {-$speed * sin([$name: set angle])}] set dy [expr {-$speed * cos([$name: set angle])}] $c move $name $dx $dy foreach {x0 y0 x1 y1} [$c bbox $name] break if {$x0<0 || $y0<0 || $x1>[$c cget -width] || $y1>[$c cget -height]} { crash $name ;# went over canvas borders } lappend title $name: [expr {$speed*10}] mph } } proc car::crash name { if {[set ${name}::speed] > 3} { set c [set ${name}::canvas] $c create oval [$c bbox $name] -fill white -outline white\ -stipple gray12 -tag cloud$name set center [canvas'center $c $name] foreach color {yellow orange red brown black} { after 250 $c itemconfig cloud$name -fill $color -outline $color \ -stipple gray12 ;# -stipple doesn't work on Win95 eval $c scale cloud$name $center 1.4 1.4 update idletasks } after 250 $c delete cloud$name } set ${name}::speed 0 ;# in any case, stop that thing } #-------- Generally useful routines: proc canvas'center {w tag} { foreach {x0 y0 x1 y1} [$w bbox $tag] break list [expr {($x0 + $x1) / 2.}] [expr {($y0 + $y1) / 2.}] } proc canvas'rotate {w tag angle} { foreach {xm ym} [canvas'center $w $tag] break foreach item [$w find withtag $tag] { set coords {} foreach {x y} [$w coords $item] { set rad [expr {hypot($x-$xm, $y-$ym)}] set th [expr {atan2($y-$ym, $x-$xm)}] lappend coords [expr {$xm + $rad * cos($th - $angle)}] lappend coords [expr {$ym + $rad * sin($th - $angle)}] } $w coords $item $coords } } proc every {ms body} {eval $body; after $ms [info level 0]} #-------------------- test and demo: pack [canvas .c -width 600 -height 400 -bg darkgreen] .c create text 300 200 -text TclRacing \ -font {Helvetica 64 {bold italic}} -fill green4 set track {45 45 300 45 560 45 560 360 45 360 45 45} .c create line $track -fill bisque -width 85 -smooth 1 -capstyle round ;# background .c create line $track -fill grey -width 75 -smooth 1 -capstyle round ;# race track .c create line 300 5 300 80 -fill yellow ;# finish line car::new Ferrari .c red ;# default: cursor keys .c move Ferrari 0 100 car::new BMW .c blue {a s w y} ;# other keys for second car .c move BMW -35 100 bind . <space> {car::accelerate all 0} every 50 {car::move} bind . <Escape> {exec wish $argv0 &; exit} bind . ? {console show}
The screenshot (Win2k) shows a crack in the road on the left side. This does not occur in Win95, and has to do with -capstyle attributes - setting them "round" when drawing the track gives a seamless tarmac.
Heh, the playability in this game is so off it's quite a challenge to play ;-) Btw. take a look at the old DOS game "Slicks'n'Slides". Absolutely brilliant. Would give anything to have a Linux GPLed version of that game with network code.--Setok
This is far too advanced for so few lines of code to be of natural origin. I suspect Satanic influence. -FW - RS: no Satan involved - this is just how Tcl is...
The graphics and the cars look cool. But, I recommend that the steering return to center after you release the keys, like in a real car. Then the cars would be easier to control
TV Little short of wonderful, I have the two cars each running their own circle taking up 13 % of the processor time, which is well spent. Like little atoms. I have nothing to add. Yet.(little later) Oops, it seems the solving of the differential motion equation by an approximated, as it seems non symmetrical difference eq, approximation, the circles are not circles in the end, one car just went of the screen...AvL To solve this, one might "merge" this prog with my drive.tcl [1]. While mine has exact calculation of the position after each time-step (and thus the car goes around in circles for very long time without drifting off), it would surely benefit from the nice "environment" (track, look of cars) that can be found here :-)Wouldn't it be right to log races altogether?