You can make a lot of games with just buttons and labels. For example, number and word oriented games look just fine with buttons and labels. We can even do a decent job of making a game look neat by putting images on the buttons.
A lot of nifty games need pictures that the program builds while it's running.
We made a Tchuka Ruma game in the last lesson with buttons and images. The image for 3 beans is always the same image. It would be cooler to make the game draw the images itself, and make them a little bit different each time.
We can easily make the Tchuka Ruma game look like this:
This game is built out of 5
canvas widgets, instead
of 5 buttons. The
canvas widget is a Tcl widget
that we can draw stuff on.
We create a
canvas widget just like we create all the
other Tcl/Tk widgets. We call the
canvas command to
tell Tcl/Tk that we're going to make a canvas, then we give this
canvas a unique name that starts with a period and a lowercase letter,
and finally we add some option/value pairs to make the canvas look just
like we want it to.
Here's the syntax for the canvas command.
There are several options we can use with a canvas. The first two we'll need for this game are the height and width of the canvas.
|The height of the canvas|
|The height of the canvas|
We normally describe the height and width in terms of pixels.
The word pixel stands for PIcture ELement. I probably should have been a pictel, but pixel sounds cooler.
All computer screens and televisions show you images that are made up of many small dots. The dots can be red, green or blue, and they can be dark or light. Each of these dots is a pixel.
Normal computer screens are 1024 pixels wide by 768 pixels tall, but that can vary. Some are 1280 pixels wide by 1024 pixels tall, and some are even 1600 pixels wide by 1200 pixels tall. If you've got a wide-screen TV or computer monitor, it might be 1680 pixels by 1050 pixels tall.
There are ways for a computer program to find out how big the display screen is that a player is using, and then make itself just fit the screen.
For now, we'll just assume that a player has at least a 1024 pixel by 768 pixel display and make games that will fit on that comfortably.
If we make the canvases for the small bins 100 pixels wide, and make the large bin twice that wide, we end up with 600 pixels wide and about 80 pixels tall. That fits nicely on most monitors.
Take a look at the image below. This shows a canvas 100 pixels wide and 80 pixels tall.
We define a location on that canvas with two numbers, an X coordinate (how many pixels from the left border) and a Y coordinate (how many pixels down from the top. Tcl/Tk puts the 0, 0 is the upper left hand corner, instead of the lower left hand corner the way we commonly draw graphs. Having 0,0 in the upper left and counting Y as we go down the screen is common in computer graphics.
The blue rectangle on this canvas starts at position 10, 10 and extends to position 90, 70. These two locations are the opposite corners that define the rectangle. We could also describe this rectangle by saying that corners are 10, 70 and 90, 10.
The red oval is exactly covered by the blue rectangle. We call the blue rectangle the bounding rectangle or bounding box for the oval, because this rectangle just exactly covers the oval. If the box were any smaller, part of the oval would be on the outside, and if it were any larger, you'd be able to see part of the box around the outside edge of the oval.
The idea of a bounding box is common in graphic programming. Any time we need to describe an area on the canvas we use a bounding box. It can cover an oval, a circle, or even a bunch of different objects.
Lets look at drawing the rectangle, oval and lines.
The Tcl/Tk widgets are what computer folks call an object. That means it's really a command and data wrapped up into one thing.
When we create a new canvas with the
canvas command, Tcl/Tk
makes a new command with the name of the canvas we created. Like the
sound commands, that new command has options that are commands we can
use to control it.
When we create the canvas named
.c, Tcl/Tk creates a command
The main command for the
canvas widget we created is
create. This is how we draw on the canvas. We
create lines, rectangles, ovals, text and so forth.
create commands all follow the same pattern. From
left to right we have:
The location and size of the thing we're creating will be 1 or more X/Y
pairs to define a location on the canvas. For things like an image or
a word, we can use 1 X/Y pair, because the
defines where the thing goes, but doesn't control how big it is.
create command can control how big the thing we're
creating is, we need 2 X/Y pairs. Each pair will be the X, Y
coordinates of a corner of a rectangle that would just cover the object
we're creating. This is the bounding box we just looked at.
You can define a rectangle by giving the X/Y coordinates of any two opposite corners.
Here's some code that:
Like all the other Tcl/Tk widgets, you need to use the
grid command to tell Tcl/Tk where to put the canvas.
Here's some of the things you can create on a canvas and some of the useful options for them:
|rectangle||2 pair||-fill color, -outline color, -width number|
|oval||2 pair||-fill color, -outline color, -width number|
|text||1 pair||-text "string of words", -fill color|
|line||2 or more pairs||-fill color, -width number|
Type the code above (or copy and paste it) into Komodo and see what it does. Try changing the colors and X/Y locations and see what you get.
After you've change the colors, try changing the order in which the oval and rectangle are created. It will look like the oval wasn't created - but it really is there. Whatever you create in a canvas is drawn on top of what's already there. The rectangle completely covers (and hides) the oval.
Everything that we create on a canvas has a few things in common, whether it's a rectangle, oval or line. Everything gets a unique number when it is created, and everything can have a tag associated with it.
The unique number is how Tcl/Tk keeps track of the things on the canvas. Our program can use this number if we want it to. People (even computer programmers) prefer to think in words, rather than numbers.
A tag is a word that our program uses to reference something we've drawn on the screen. We can even give the same tag to several things we create on the canvas, so we can do something to all of them at once.
What sorts of thing would we want to do to more than one canvas element? Well, we might want to delete them.
In order to delete something from a canvas, we need to be able to tell the canvas what to delete. We do this by giving the canvas an identifier. The identifier could be the unique number the canvas assigned when we created the element, or it could be the tag that we told the canvas to associate with the things we've created.
One identifier that always exists is the word
means (can you guess?) to delete all the stuff in a canvas.
The command to delete things from a canvas is this:
canvasName delete identifier
Here's code like the previous example, but this time everything we create on the canvas has a tag. The buttons will delete things from the canvas when you click them.
Now for a tricky bit.
Whenever you move a mouse, hit a key or blink, the computer sees an event. OK, not when you blink. But lots of things cause events for the computer.
You're already familiar with some of these events, you're just so used to them that you don't even think about them. When you move a mouse over a button, the button changes color to let you know it's ready to be clicked. When you push the mouse-button you see the button on the screen click down, and when you release the mouse-button it clicks back up, etc.
All of these things are done by giving the computer a list of events to watch for, and instructions for what to do when that event occurs. We do this for every single thing that gets drawn on the computer screen from a big window to a single button.
We call this binding an event to a window. Or just binding for short.
Luckily, most of these bindings are assigned automaticly without us needing to do anything about it. Buttons get the bindings for mouse-enter, mouse-leave, button-press and button-release without us needing to do anything.
Tcl/Tk gives you the power to put new bindings on the widgets we create. We can even put bindings on the things we draw on a canvas (but we'll get to that later).
For the Tchuka Ruma game, we want to be able to click on a bin and
have it move the beans around, as if the
canvas were a
We can do that by setting a binding on the canvas for the ButtonRelease event.
The Tcl/Tk command to assign bindings is
bind command needs to know three things - the window to
put a binding on, the event to watch for, and the commands to run when
the event happens.
Here's the syntax for the
That's all the pieces we need to make a Tchuka Ruma game that looks like it's being played with bins and beans (if you don't look too close)
We can make a Tchuka Ruma game using
delete command. We'll use a big oval
for the bins, and smaller ovals for the beans.
Most of the code we wrote in the previous version of Tchuka Ruma will be the same, we'll just change it to use canvases instead of buttons.
Obviously, the big changes will be to the
showBeans procedures. Instead of buttons, we'll be using
canvases. Our code will need to draw the dark yellow ovals to be the bins
and the dark brown ovals to be beans.
Here's the new
buildBoard procedure. The only part we
kept from the previous
buildBoard is the loop, the
grid commands and the procedure header comment. We
button commands with
We added a
bind command so that clicking on
the canvases will call the
moveBeans procedure. This
line of code is what gets done for you when you use the
option with the
Finally, we draw an oval to use as the bin.
moveBeans procedure won't need to be changed at all.
That's the nice thing about this style of programming. The biggest
part of the program is the code that knows how to play the game,
and we can make a big visual change without touching that.
showBeans procedure gets totally rewritten. As with
buildBoard procedure, we change almost everything
except the header comment.
Here's the new procedure. Notice that we use the
option on the beans so that they all have the same tag, and we can
delete the beans without deleting the big gold oval.
Look at the calculation for where to put the beans in the previous code. The beans will always be put in a row. The first bean is always in the same column, the next bean in another and so forth. That's OK, but it's a little bit boring. The display looks like this.
We can use some random numbers to spice this up. The code below varies the X and Y position of the beans just a little bit so they end up scattered across the bin, but are still mostly in a row, and mostly in columns. We need to be careful with random numbers like this to make sure we don't put beans exactly on top of each other, or outside the bin.
Here's the complete code for this game.
Try modifying the colors of the big bins and beans.
You can make differently shaped beans by changing the command that sets x2 and y2. Try adding some randomness to the bean size and see how the game looks.
This is trickier. Try reworking the
so that it takes an argument. That argument will be the index for
the bin to show. Instead of showing all the beans in all the bins,
only show the beans in the bin described by the argument. Here's how
the new procedure will start.
You'll be able to get rid of the loop and the lines of code that
Now modify the
moveBeans procedure to call
$binNumber when it changes the number of beans in
Finally change the calls to
to pass the number of the bin that's being made.
The advantage of this way of updating the board is that we can add a sound effect for dropping the beans into the bins.
You can get a plink sound effect here
Add the commands to load the plink sound to
and then add
plink play to
need to also add
update idle to
show the beans as they are being dropped into the bin.
canvasName delete allcommand will delete everything from a canvas where
canvasNameis the name of the canvas (like
-tag wordoption to put an identifier on things you create on the canvas.
bindcommand. For instance, you can make a canvas act like a button by putting a binding on it for the
There is a lot of stuff we can do with a canvas. We'll start looking at another game with pictures in the next lesson.