Animations
If you want to advance from regular drawings to drawings that actually move, this is the right section for you. In this section, you will create a small movie of a sailboat sailing into the sunset.
Special thanks goes to Paolo Ruiz, who created this wonderful program. The entire program is also available in our Logo Library.
Apart from animation, this section also shows how to work with bitmaps instead of turtles as moving objects.
But first, let us dive into animation in general.
The Basics
Let the Turtle Move
The trick behind animation is to move your turtle independently from a running program. The easiest way to move a turtle is to let it advance, then wait for a little while, and repeat everything over and over again:
TO MOVE.IT
FOREVER [
FD 1
WAIT 50
]
END
If you run this code, the turtle starts moving, and stops only when you click the Stop button. Easy enough, but what if you need to move more than one turtle at a time? Then things become really complicated very quickly.
SETVELOCITY
Thankfully, Logo offers a way to let a turtle move independently. The SETVELOCITY command does just that. It expects a single input, which is the number of pixels (dots) the turtle should move per second. A value of 100, for example, lets the turtle move with a speed of 100 dots per second:
SETVELOCITY 100
Setting it back to 0 stops the turtle.
Right-click your turtle and open the Properties dialog; then play with the Velocity and Heading settings to get a feeling for the velocity.
Using SETVELOCITY, you can move multiple turtles around without having to loop and wait and loop and wait…
Using Shapes
You probably already know that you can drop a shape onto the turtle to change its shape to, say, a bird or a car. Moving a bird around looks so much more fun than just having the turtle move!
Problems arise when you want the turtle to move in a direction different from straight up. Your shape rotates just nicely as well. This may be fine if you, for example, want to steer a racing car, but rotating an elephant, for example, does not look right. An elephant would not want to walk on its tusk.
Logo also has a solution to this problem. The LOCKSHAPE command locks the shape in whatever heading it points so regardless of the underlying turtle’s heading, and the UNLOCKSHAPE command makes the shape rotate with the turtle’s heading again.
Try this for yourself. Drop an elephant on the turtle, make it become an elephant. Then, enter the LOCKSHAPE command. Now you can move and turn the elephant without having it rotate.
The Turtle Size
Let us assume that your turtle’s shape is now an elephant. Elephants are big animals, and the elephant shape is also big. What if you want the elephant to be much smaller?
Again, Logo comes to your rescue. The SETTURTLESIZE (or SETTS
) command changes the
size of your shape. A size of 1 is the original size. Make it smaller by using a smaller value, like 0.5 for half the size,
for example, or double its size by using SETTS 2
. The TURTLESIZE command reports the
current size of the shape.
You could even change the actual size of the shape by setting the turtle’s SIZE
property. This property takes a two-element
list containing the shape’s width and height in pixels.
The elephant shape, for example, reports a width of 249 and a height of 200 pixels:
GPROP 0 "SIZE
Result: [249 200]
You think that the elephant is too skinny? Well, make it wider:
PPROP 0 "SIZE [400 200]
Try for yourself!
Did you know that many Logo commands just manipulate a turtle’s properties? The SETTS
commmand,
for example, alters the turtle’s SCALE
property, and the SETVELOCITY
command changes the turtle’s VELOCITY
property.
That’s why you can change most of these values when your right-click a turtle to display the property editor.
Sailing Into the Sunset
Now let’s get to our beautiful sunset movie.
For this movie, you will not actually use the turtle other than to draw the actual objects that move. In the animation world, they call objects that move “sprites,” and this is what we will be calling them.
How do we create sprites?
First, let us look art the SNAP command. This command expects two or three inputs. The first two inputs are the width and height of the area to snap. The third input instructs the command to either snap both the drawing and the background (which is the default), the drawing only, or the background only. The command returns the name of the snapped bitmap. The turtle’s position is used as the lower left corner of the area to snap.
An example:
SETXY [10 20]
(SNAP 50 100 "DRAWING)
Here, the lower left corner is at 10 horizontal and 20 vertical. The area to snap is 50x100 pixels, which puts the upper right corner at 60 horizontal and 120 vertical. In our case, we do not want any of the white background to be snapped, so we use the optional value “DRAWING as the third input.
The return value is the name of the snapped bitmap, which could, for example, be BITMAP.1
. In the procedures below,
we store that name into a more descriptive variable, like for example:
MAKE "CLOUD1 (SNAP 60 30 "DRAWING)
This lets us use the variable to activate the bitmap later. The TELL command instructs Logo to send all turtle and movement commands to the name or list of widgets given as input.
Examples:
TELL 0 ; activate turtle #0
TELL [0 1 2 3 4 5] ; turtles 0–5 will listen to commands
TELL :CLOUD1 ; the bitmap whose name is in :CLOUD1 will listen
The Sailboat
First, we need the sailboat. There is no sailboat in the Toolbox to speak of, so we need to draw our own. Here is the Logo procedure:
TO MAKE.SHIP
; Draws the original ship size
SETW 3 SETPC "INDIGO
SETH 90 FD 200*.7 RT 100
REPEAT 3 [FD 200*.1 RT 6]
LT 80 FD 200*.2 BK 200*.2 RT 80
SETH 270 FD 200*.55
LT 30 FD 200*.2 BK 200*.2 RT 30
RT 70
REPEAT 5 [FD 200*.06 RT 4]
SETH 90 FD 200*.3 LT 90 FD 200*1.4 RT 150
REPEAT 7 [FD 200*.2 RT 4]
SETH 265
FD 200*.43
SETH 10
REPEAT 6 [FD 200*.21 LT 4]
SETH 200
REPEAT 5 [FD 200*.24 LT 4]
SETH 95
FD 200*.23
; move the turtle to the lower left corner of the drawing
PU SETXY [-28 -80] SETH 0
; then snap the drawing into a bitmap and save the name
MAKE "SHIP (SNAP 200 370 "DRAWING)
; set the ship to WINDOW because we do not want the sailboat to wrap
PPROP :SHIP "WRAPMODE "WINDOW
; remove the drawing and send the turtle home
CS
END
It is a good idea to clean the screen after the drawing so the next drawing can happen.
A word about this command:
PPROP :SHIP "WRAPMODE "WINDOW
Usually, turtles and sprites wrap to the other side of the Graphics panel when they hit any boundary.
The sailboat should start off-screen and sail into the horizon at about the center of the picture. If the sailboat
would wrap, we would not be able to really place it outside of the drawing area. Therefore, we set
the sailboat’s WRAPMODE
property to WINDOW
, which lets the sprite sail outside of the drawing area.
For more wrap modes, see the WINDOW command.
The Clouds
We want the clouds to move slowly across the screen; therefore, clouds need to be sprites as well. Here is how:
TO MAKE.CLOUD1
HOME PD
SETPC "DARKGRAY
SETH 25 REPEAT 19 [FD 1 RT 3]
SETH 25 REPEAT 29 [FD 1 RT 4]
SETH 90 REPEAT 17 [FD 1 RT 5]
SETH 110 REPEAT 9 [FD 1 RT 4]
SETH 270 FD 58
PU SETH 0 BK 5
MAKE "CLOUD1 (SNAP 60 30 "DRAWING)
CS
END
TO MAKE.CLOUD2
HOME PD
SETPC "DARKGRAY
SETH 25 REPEAT 19 [FD 1 RT 2]
SETH 25 REPEAT 36 [FD 1 RT 3]
SETH 90 REPEAT 17 [FD 1 RT 4]
SETH 85 REPEAT 14 [FD 2 RT 5]
SETH 270 FD 80
PU SETH 0 BK 5
MAKE "CLOUD2 (SNAP 80 40 "DRAWING)
CS
END
Then, we create the procedures that make the clouds, and initialize them:
TO CLOUD1
TELL :CLOUD1
ST LOCKSHAPE
SETXY [120 190]
SETH 90
SETVELOCITY 20
TELL 0
END
TO CLOUD2
TELL :CLOUD2
ST LOCKSHAPE
SETXY [-200 200]
SETH 90
SETVELOCITY 20
TELL 0
END
These procedures position the clouds; they use the LOCKSHAPE
command because
we want the clouds to move from left to right, which needs a heading of 90 degrees
so that the cloud doesn’t turn as well. Finally, we use SETVELOCITY
to have them start
moving on their own.
Note how we use the TELL
command to activate each sprite and have it listen to Logo
commands.
The ship also need some initialization:
TO SHIP
TELL :SHIP
ST LOCKSHAPE
SETXY [50 -525]
TELL 0
END
The ship needs to adjust the size while moving, which is a bit more complex
than SETVELOCITY
. We’ll talk about this in more detail below.
The Scenery
Finally, we can put it all together and create the scenery. A small procedure for a few waves, and another procedure for the horizon and the sun are needed.
TO WAVES :X
SETPC "LIGHTSEAGREEN
SETH 120
REPEAT :X [REPEAT 7 [FD 2 LT 10] RT 70]
END
TO HORIZON
; the curved horizon
PU SETY 45 RT 90 PD
SETW 3
SETPC "MEDIUMAQUAMARINE
REPEAT 5 [FD 80 RT 0.5] ;curved horizon
REPEAT 10 [LT 0.5 BK 80]
REPEAT 5 [FD 80 RT 0.5]
; the sun
setpc "gold
SETH 0 REPEAT 19 [FD 10 RT 10]
END
TO SCENERY
CLOUD1
CLOUD2
SHIP
HORIZON
PU SETXY [-120 -60] PD
WAVES 3
PU SETXY [-250 -150] PD
WAVES 5
PU SETXY [200 -120] PD
WAVES 4
END
Note that we first need to make the clouds and the ship; after that, we are free to draw the rest.
Moving the Sailboat
We are almost there. The tricky part is to move the sailboat. It needs to move forward and at the same time become smaller, and all in a smooth fashion.
Now it is time to talk about the core of animation. You would not want to use the SETVELOCITY command, because you need to shrink and move the boat at the same time, both in certain time intervals.
Usually, this would require some sophisticated programming with wait loops or the like.
You do not want to do this. Instead, Logo uses a concept called “Animation Frames”. You may remember that movies in a cinema are actually a sequence of pictures, often with 24 pictures per second. The human eye is too slow to distinguish between these pictures, creating the illusion of continous movement in the brain. If you, for example, were from Mars with much sharper eyesight, you could not watch a movie without getting a Martian headache, because the flickering would drive you crazy.
Logo uses the same concept to animate turtles, shapes, and the like. It acts like a movie with 30 pictures, or frames, per second. And Logo offers to call a list of your Logo commands 30 times a second.
How convenient! Now, we just need to create a procedure that Logo needs to call 30 times a second, and both advance and shrink the sailboat during each of these procedure calls. Here it is:
MAKE "START.POS 203
MAKE "SHIP.POS :START.POS
; Called on every animation frame
TO MOVE.SHIP
IF :SHIP.POS <= 1 THEN (WHEN [ANIMATION FRAME]) STOP
TELL :SHIP
; Move ship slower the farther it gets
FD 1 + :SHIP.POS / 55
MAKE "SHIP.POS :SHIP.POS - 1
SETTS :SHIP.POS / :START.POS
END
The first line stops the animation frames for the ship and exits the procedure when the ship has reached the horizon.
You can now use the WHEN command. This is a very powerful command that lets you attach Logo commands to a lot of events, such as when you press a mouse button, or the mouse moves, and much more. The Logo manual has an entire chapter dedicated to the WHEN command. Click here for more information.
For now, we just use the WHEN command to make Logo call our movement procedure:
WHEN [ANIMATION FRAME] [MOVE.SHIP]
As you saw before, to stop Logo from calling MOVE.SHIP
repeatedly, use the WHEN
command without a runlist:
(WHEN [ANIMATION FRAME])
The Main Program
That’s it. Well, almost.
We need the main program that puts everything together:
TO ANIMATE
DRAW HT
MAKE.SHIP
MAKE.CLOUD1
MAKE.CLOUD2
SCENERY
; start the animation frame monitor
WHEN [ANIMATION FRAME] [MOVE.SHIP]
; sit and wait to keep everything moving
WAIT -1
END
The last command WAIT -1
needs an explanation. All Logo commands that Logo should execute on certain
events, like an animation frame, stop when a program ends. If you do not wait forever, the WHEN
command simply ceases working before the first animation frame is executed. So the only solution is
to wait forever so Logo can call the animation frame commands repeatedly.
But how to end the program then, apart from clicking the Stop button?
If you look at the MOVE.SHIP
procedure, the first line reads
IF :SHIP.POS <= 1 THEN (WHEN [ANIMATION FRAME]) STOP
If you want the whole movie to stop, including the clouds, use the TOPLEVEL command instead. This command causes Logo to return to toplevel, ending the program, and stopping all independent movement.
IF :SHIP.POS <= 1 THEN TOPLEVEL
Right now, we stop the program by clicking the Stop button so we can admire the clouds.
Finishing Touches
Are we done?
Almost.
If you run the ANIMATE
procedure, everything runs just fine, but you see an annoying flicker
when you draw the sailboat and the clouds. It would be great if the turtle could draw the sprites
without disrupting the movie.
Again, Logo is there to help you. The FREEZEPIC command freezes the output of the Graphics panel. You can still draw, but the drawing will not display anymore. This is exactly what the doctor ordered. The UNFREEZEPIC revives the Graphics panel, and all drawings that have been made while the picture was frozen will appear.
What is needed is the following:
- Call FREEZEPIC.
- Do all the drawing and creation of the sprites.
- Erase the drawing and call UNFREEZEPIC.
Let us alter the ANIMATE
procedure to include FREEZEPIC
and UNFREEZEPIC
:
TO ANIMATE
DRAW HT
FREEZEPIC
MAKE.SHIP
MAKE.CLOUD1
MAKE.CLOUD2
UNFREEZEPIC
SCENERY
; start the animation frame monitor
WHEN [ANIMATION FRAME] [MOVE.SHIP]
; wait for a TOPLEVEL
WAIT -1
END
Now, we are really done! Enjoy!