A blessed and fortune-filled Chinese New Year to all! It's the Year of the Rooster and to mark the occasion, I'd like to walk you through a little fun assignment - a QBasic animation and song.
Sound in QBasic is pretty much a function of supplying different tones at different frequencies. For me, it was a whole mess of trial and error, but once you hit the right first few notes, the rest just sort of fall into place.
In fact, we're going to start off with this...
DIM basetune$
basetune$ = "C-6 C+6 D6 E6 G3 F+4 F+6 B6 B6 F+6 F+3 F-4 F-6 G6 G-6 E6 E3 D4 D6 C+6 C-6 <B-6 >C-3 C-4 E4 G-8 D4 G-8 D-6 G-8 C-6 G-8 E4 G-8 D4 G-8 D-6 G-8 C-6"
PLAY basetune$
When you compile and run, your computer should emit that lovely Chinese New Year song.
Now make the following changes, creating
bgtune as a new string variable. This plays the previous tune, then replays it in a higher pitch courtesy of the ">" symbol.
DIM basetune$
basetune$ = "C-6 C+6 D6 E6 G3 F+4 F+6 B6 B6 F+6 F+3 F-4 F-6 G6 G-6 E6 E3 D4 D6 C+6 C-6 <B-6 >C-3 C-4 E4 G-8 D4 G-8 D-6 G-8 C-6 G-8 E4 G-8 D4 G-8 D-6 G-8 C-6"
DIM bgtune$
bgtune$ = basetune$ + " > " + basetune$
PLAY bgtune$
And again!
DIM basetune$
basetune$ = "C-6 C+6 D6 E6 G3 F+4 F+6 B6 B6 F+6 F+3 F-4 F-6 G6 G-6 E6 E3 D4 D6 C+6 C-6 <B-6 >C-3 C-4 E4 G-8 D4 G-8 D-6 G-8 C-6 G-8 E4 G-8 D4 G-8 D-6 G-8 C-6"
DIM bgtune$
bgtune$ = basetune$ + " > " + basetune$ + " >> " + basetune$
PLAY bgtune$
It may all be a little too high pitched, so just add this to the beginning...
bgtune$ = "<<<" + basetune$ + " > " + basetune$ + " >> " + basetune$
PLAY bgtune$
But what's music without a little dance, right? Let's make a rooster dance right here, on screen. To do that, we'll need to draw a rooster on screen. We'll draw different parts of the rooster and animate those parts separately. And, to make our life easier, we'll rely on some good old-fashioned abstraction.
First, we'll need a delay subroutine. Let's create one, and call it, very imaginatively,
delay(). A variable
Value is passed in, and that is the number of milliseconds you want the function to delay by.
bgtune$ = "<<<" + basetune$ + " > " + basetune$ + " >> " + basetune$
PLAY bgtune$
SUB delay (Value!)
END SUB
The variable T is set to the number of milliseconds since midnight.
SUB delay (Value!)
T! = TIMER
END SUB
And after that, we use a
DO: LOOP UNTIL loop that does nothing until the
T subtracted from the current number of milliseconds since midnight, equals or exceeds the value of parameter
Value.
SUB delay (Value!)
T! = TIMER
DO: LOOP UNTIL (TIMER - T!) >= Value!
END SUB
Then we'll need another subroutine,
drawstr(). Here, we pass in the parameters
row,
col,
fg,
bg and
text.
text is the string you want to print, while the rest are integers.
SUB delay (Value!)
T! = TIMER
DO: LOOP UNTIL (TIMER - T!) >= Value!
END SUB
SUB drawstr (row, col, fg, bg, text$)
PRINT text$
END SUB
row and
col define where your cursor is going to start drawing.
SUB drawstr (row, col, fg, bg, text$)
LOCATE row, col
PRINT text$
END SUB
fg is the foreground color of your string, and
bg is the background color.
SUB drawstr (row, col, fg, bg, text$)
COLOR fg, bg
LOCATE row, col
PRINT text$
END SUB
Now we begin drawing the head of the rooster. It's the subroutine
draw_head1(), and here you pass in a variable
bg which will be used as the background color. Then it's a simple matter of using the
drawstr() subroutine to draw each line of the rooster's head.
SUB drawstr (row, col, fg, bg, text$)
COLOR fg, bg
LOCATE row, col
PRINT text$
END SUB
SUB draw_head1 (bg)
CALL drawstr(2, 29, 0, bg, " ")
CALL drawstr(2, 30, 0, 4, " ")
CALL drawstr(2, 34, 0, bg, " ")
CALL drawstr(3, 29, 0, bg, " ")
CALL drawstr(3, 30, 0, 15, " o ")
CALL drawstr(3, 34, 0, 4, " ")
CALL drawstr(3, 35, 0, bg, " ")
CALL drawstr(4, 28, 0, bg, " ")
CALL drawstr(4, 29, 14, bg, "<")
CALL drawstr(4, 30, 0, 15, " ")
CALL drawstr(4, 34, 0, 4, " ")
CALL drawstr(4, 35, 0, bg, " ")
CALL drawstr(5, 29, 0, bg, " ")
CALL drawstr(5, 30, 0, 4, " ")
CALL drawstr(5, 31, 0, 15, " ")
CALL drawstr(5, 34, 0, bg, " ")
END SUB
Let's test this subroutine out. declare a variable,
bg, in the main body. We'll set
bg to 6, which is a lovely
orange. Then we cover the entire screen in this color using the
CLS function.
PLAY bgtune$
DIM bg
bg = 6
COLOR 0, bg
CLS
Then call the
draw_head1() subroutine passing in the 2 as a test argument, which is
green.
PLAY bgtune$
DIM bg
bg = 6
COLOR 0, bg
CLS
CALL draw_head1(2)
This is what you have! Don't worry about the jarring
green background. This is a
test, remember?
But you'll notice that the song has to finish playing before your code will run. Add this to your
bgtune variable. Now your tune runs in the background!
bgtune$ = "mb <<<" + basetune$ + " > " + basetune$ + " >> " + basetune$
Write the
draw_head2() subroutine. It's almost a mirror of the
draw_head1() subroutine, with a few differences.
SUB draw_head1 (bg)
CALL drawstr(2, 29, 0, bg, " ")
CALL drawstr(2, 30, 0, 4, " ")
CALL drawstr(2, 34, 0, bg, " ")
CALL drawstr(3, 29, 0, bg, " ")
CALL drawstr(3, 30, 0, 15, " o ")
CALL drawstr(3, 34, 0, 4, " ")
CALL drawstr(3, 35, 0, bg, " ")
CALL drawstr(4, 28, 0, bg, " ")
CALL drawstr(4, 29, 14, bg, "<")
CALL drawstr(4, 30, 0, 15, " ")
CALL drawstr(4, 34, 0, 4, " ")
CALL drawstr(4, 35, 0, bg, " ")
CALL drawstr(5, 29, 0, bg, " ")
CALL drawstr(5, 30, 0, 4, " ")
CALL drawstr(5, 31, 0, 15, " ")
CALL drawstr(5, 34, 0, bg, " ")
END SUB
SUB draw_head2 (bg)
CALL drawstr(2, 28, 0, bg, " ")
CALL drawstr(2, 29, 0, 4, " ")
CALL drawstr(2, 33, 0, bg, " ")
CALL drawstr(3, 28, 0, bg, " ")
CALL drawstr(3, 29, 0, 15, " u ")
CALL drawstr(3, 33, 0, 4, " ")
CALL drawstr(3, 34, 0, bg, " ")
CALL drawstr(4, 27, 0, bg, " ")
CALL drawstr(4, 28, 14, bg, "<")
CALL drawstr(4, 29, 0, 15, " ")
CALL drawstr(4, 33, 0, 4, " ")
CALL drawstr(4, 34, 0, bg, " ")
CALL drawstr(5, 28, 0, bg, " ")
CALL drawstr(5, 29, 0, 4, " ")
CALL drawstr(5, 30, 0, 15, " ")
CALL drawstr(5, 33, 0, bg, " ")
END SUB
Now test using a FOR loop and the
delay() subroutine. Does the head move?
COLOR 0, bg
CLS
FOR i = 0 TO 10 STEP 1
CALL draw_head1(2)
CALL delay(0.5)
CALL draw_head2(2)
CALL delay(0.5)
NEXT i
Change the code by passing in the actual background color,
bg. Compile and run again. Is the animation right?
FOR i = 0 TO 10 STEP 1
CALL draw_head1(bg)
CALL delay(0.5)
CALL draw_head2(bg)
CALL delay(0.5)
NEXT i
In the same vein, write the subroutines
draw_legs1(),
draw_legs2(),
draw_body1(),
draw_body2(),
draw_tail1() and
draw_tail2(). You may notice that the
draw_body1() and
draw_body2() subroutines do not require parameters. That's because the animation does not change the shape of the body at all.
SUB draw_head2 (bg)
CALL drawstr(2, 28, 0, bg, " ")
CALL drawstr(2, 29, 0, 4, " ")
CALL drawstr(2, 33, 0, bg, " ")
CALL drawstr(3, 28, 0, bg, " ")
CALL drawstr(3, 29, 0, 15, " u ")
CALL drawstr(3, 33, 0, 4, " ")
CALL drawstr(3, 34, 0, bg, " ")
CALL drawstr(4, 27, 0, bg, " ")
CALL drawstr(4, 28, 14, bg, "<")
CALL drawstr(4, 29, 0, 15, " ")
CALL drawstr(4, 33, 0, 4, " ")
CALL drawstr(4, 34, 0, bg, " ")
CALL drawstr(5, 28, 0, bg, " ")
CALL drawstr(5, 29, 0, 4, " ")
CALL drawstr(5, 30, 0, 15, " ")
CALL drawstr(5, 33, 0, bg, " ")
END SUB
SUB draw_tail1 (bg)
CALL drawstr(5, 45, 0, bg, " ")
CALL drawstr(5, 46, 0, 4, " ")
CALL drawstr(5, 52, 0, bg, " ")
CALL drawstr(6, 44, 0, bg, " ")
CALL drawstr(6, 45, 0, 4, " ")
CALL drawstr(6, 50, 0, bg, " ")
CALL drawstr(7, 44, 0, bg, " ")
CALL drawstr(7, 45, 0, 4, " ")
CALL drawstr(7, 47, 0, bg, " ")
CALL drawstr(8, 44, 0, bg, " ")
CALL drawstr(8, 45, 0, 4, " ")
CALL drawstr(8, 46, 0, bg, " ")
CALL drawstr(9, 44, 0, bg, " ")
CALL drawstr(9, 45, 0, 4, " ")
CALL drawstr(9, 46, 0, bg, " ")
END SUB
SUB draw_tail2 (bg)
CALL drawstr(5, 46, 0, bg, " ")
CALL drawstr(5, 47, 0, 4, " ")
CALL drawstr(5, 52, 0, bg, " ")
CALL drawstr(6, 45, 0, bg, " ")
CALL drawstr(6, 46, 0, 4, " ")
CALL drawstr(6, 51, 0, bg, " ")
CALL drawstr(7, 44, 0, bg, " ")
CALL drawstr(7, 45, 0, 4, " ")
CALL drawstr(7, 47, 0, bg, " ")
CALL drawstr(8, 44, 0, bg, " ")
CALL drawstr(8, 45, 0, 4, " ")
CALL drawstr(8, 47, 0, bg, " ")
CALL drawstr(9, 44, 0, bg, " ")
CALL drawstr(9, 45, 0, 4, " ")
CALL drawstr(9, 46, 0, bg, " ")
END SUB
SUB draw_body1 ()
CALL drawstr(6, 30, 0, 15, " ")
CALL drawstr(7, 29, 0, 15, " ")
CALL drawstr(8, 28, 0, 15, " ")
CALL drawstr(9, 27, 0, 15, " ")
CALL drawstr(10, 27, 4, 15, " ")
CALL drawstr(11, 28, 4, 15, " . . ) ")
CALL drawstr(12, 29, 4, 15, " . . ) ")
CALL drawstr(13, 30, 4, 15, " .. ) ")
CALL drawstr(14, 31, 4, 15, " ")
END SUB
SUB draw_body2 ()
CALL drawstr(6, 30, 0, 15, " ")
CALL drawstr(7, 29, 0, 15, " ")
CALL drawstr(8, 28, 0, 15, " ")
CALL drawstr(9, 27, 0, 15, " ")
CALL drawstr(10, 27, 4, 15, " ")
CALL drawstr(11, 28, 4, 15, " . ) ")
CALL drawstr(12, 29, 4, 15, " . .. ) ")
CALL drawstr(13, 30, 4, 15, " . . ) ")
CALL drawstr(14, 31, 4, 15, " ")
END SUB
SUB draw_legs1 (bg)
CALL drawstr(15, 1, 14, bg, " | |")
CALL drawstr(16, 1, 14, bg, " | |")
CALL drawstr(17, 1, 14, bg, " | |")
CALL drawstr(18, 1, 14, bg, " | |")
CALL drawstr(19, 1, 14, bg, " ^ ^")
END SUB
SUB draw_legs2 (bg)
CALL drawstr(15, 1, 14, bg, " | |")
CALL drawstr(16, 1, 14, bg, " >----| |")
CALL drawstr(17, 1, 14, bg, " |")
CALL drawstr(18, 1, 14, bg, " |")
CALL drawstr(19, 1, 14, bg, " ^")
END SUB
Now, we'll use the first
FOR loop we wrote as a test, and expand by adding calls to the subroutines for the rest of the rooster. Also, you may want to initialize the drawing of the rooster first.
DIM bg
bg = 6
COLOR 0, bg
CLS
CALL draw_head1(bg)
CALL draw_body1
CALL draw_legs1(bg)
CALL draw_tail1(bg)
FOR i = 0 TO 10 STEP 1
CALL draw_head1(bg)
CALL draw_body1
CALL delay(0.5)
CALL draw_head2(bg)
CALL draw_body2
CALL delay(0.5)
NEXT i
We have a dancing rooster!
Vary it up. Make different parts move using different FOR loops.
FOR i = 0 TO 10 STEP 1
CALL draw_head1(bg)
CALL draw_body1
CALL delay(0.5)
CALL draw_head2(bg)
CALL draw_body2
CALL delay(0.5)
NEXT i
FOR i = 0 TO 10 STEP 1
CALL draw_legs1(bg)
CALL draw_body1
CALL delay(0.5)
CALL draw_legs2(bg)
CALL draw_body2
CALL delay(0.5)
NEXT i
FOR i = 0 TO 10 STEP 1
CALL draw_tail1(bg)
CALL draw_body1
CALL delay(0.5)
CALL draw_tail2(bg)
CALL draw_body2
CALL delay(0.5)
NEXT i
FOR i = 0 TO 10 STEP 1
CALL draw_head1(bg)
CALL draw_legs1(bg)
CALL draw_tail1(bg)
CALL draw_body1
CALL delay(0.5)
CALL draw_head2(bg)
CALL draw_legs2(bg)
CALL draw_tail2(bg)
CALL draw_body2
CALL delay(0.5)
NEXT i
And there you go...
Dancing rooster. Clunky graphics, very retro. But still awesome McCool.
Once again, Gong Ji Fa Cai!
T___T