Teacher's Guide: Custom-Drawn Sprites!
Overview
This is the first of two lessons focused on creating custom sprites. This lesson introduces the drawon
function, which causes one sprite (the "drawor") to draw on another (the "drawee"). The following lesson (Custom-Sized Sprites!) discusses options for changing the size of a sprite.
More about the lesson
The use of drawon
is straightforward, though two issues should be emphasized. One, drawon
is turtle-specific, so that any sprite can be a drawor or drawee through appropriate use of dot notation and calls to drawon
. Two, students should call drawon window
(or, equivalently, drawon null
) when the drawor has finished its drawing, in order to prevent it from drawing on the drawee in subsequent steps of the program, and also ensure that it later draws on the background canvas. Additionally, as noted in the lesson, the call to drawon window
includes an embedded call to sync
, which causes the rest of the program to "wait" for the drawer's animation on the drawee to complete, as is typically the desired result. Without a call to drawon window
(or an appropriate call to sync
), the drawee can move before the drawer finishes its artistry, yielding unexpected results. An effective illustration of this is provided by commenting out the drawon window
statement in the ant example provided in the handout.
Sprite orientation
The orientation of a sprite determines which way it will move in response to functions such as fd
and bk
. By default, the default orientation is "facing north," i.e., towards the top of the screen. Calls to fd
and bk
will therefore move the sprite upwards and downwards on the screen, respectively. A difficulty students may run into is that if they draw their sprite with an orientation that does not agree with "facing north", then their image won't move correctly. For example, if the student drew a boat, then calls to fd
would move it into the air. Calls to rt
cannot solve this problem.
There are a number of solutions to orientation problems. For the purposes of this lesson, the simple solution is the add a few lines of code so that the image works with the default orientation of a sprite. That is, the student should either draw it so that the image aligns with the desired orientation, or (often the easier solution) rotate the drawee prior to drawing on it.
Students will encounter additional strategies and tools to deal with orientation issues in subsequent lessons, including the slide
and mirror
functions (introduced in Images!) and twist
(in the notes to the Images! lesson).
By default, sprites have a light-grey translucent background. As noted in the lesson, students can override this by including a color as an argument. Introduce the "color" transparent
to make the background invisible. (transparent
is not actually a color, such as could be generate using an rgb
or hsl
code; rather it is a special CSS property.) However, a drawback to working with a transparent background is that students cannot see the sprite as they draw on it, which can lead to confusion. Advise students to leave the color as the default while they are coding their sprites, and then go back and change to transparent
as a last step.
Notes to Activities
Walker will likely be the most challenging activity, both because it involves more lines of code, but also because it presents some significant logical challenges. Coach students to take the program one step at a time, beginning with drawing the two sprites. But resist the temptation to give away too much of the logic behind the animation. An effective approach for students to take, as suggested in the lesson, is to alternate which sprites is visible. Students often overlook the fact that they need to position both sprites before they start moving (since they will effectively leapfrog eachother). Another helpful bit of advice is not to start using calls to st
and ht
until after the motions have been successfully coded. Also, you may need to remind students that pause
is sprite-specific, so dot notation and/or calls to sync
must be used in calls to this function as well. And as usual, encourage students to personalize the programs, rather than just mimick the examples shown. For example, a one-eyed Walker. A fun variation on the Walker activity is to code sprites that look like left and right feet. They might also dream up their own rocket design.
Additional activities
- Employ similar tactics used to code the Walker activity to code an animated StickFigure which walks, jumps, or rolls across the screen or performs some other activity.
- Create a script to create four custom GhostSprites. Each ghost should have eyes pointing in a different direction, i.e., north, east, south, and west. Naturally, each ghost should also be translucent (read: use
rgba
!). Save each sprite as an image file, which we can then import into future programs. Do this using the sprite-specificsaveimg
function. For example, save an image of a sprite namedeysUpGhost
with the expressioneyesUpGhost.saveimg "eyesUpGhost.png"
. - A KanizsaTriangle is an optical illusion in which the eye perceives a white equilateral triangle where none is actually drawn. Write a program that makes a solid dot morph into the illusion. Do this by creating three sprites, each with a dot with an appropriate sector removed, so that when the sprites move apart, the illusion appears.
Beyond the lesson
Pencil
sprites
The Pencil
constructor simplifies the process of creating a sprite for the purpose of drawing on another. The key is to pass the drawee to the Pencil
constructor as an argument, which then obviates the need to subsequently call drawon
:
spot = new Sprite() p = new Pencil(spot) p.dot blue, 50 #draw a dot on spot
Pencil sprites are transparent and have a default speed of Infinity, which may be a drawback during the debugging phase, but beneficial for a program's finished product.
Saving sprite images
The saveimg
function generates a .png
file copy of a sprite's image. Reasons for creating an image file range from a desire to download and share outside of the Pencil Code environment, to creating images for sprites to be used in other programs. (Importing images for use as sprites is the focus of the Images! lesson.)
saveimg
is sprite-specific, meaning it is invoked using dot notation. For example, in a program with a sprite named rocket
, the statement rocket.saveimg "Rocket"
creates the file Rocket.png
in the same folder as the program in which it is run. (Pencil Code adds the ".png" extension if you don't explicitly include it in the output filename.)
The following example illustrates code to create and save an image; the thumbnail of the file that appears in the Pencil Code folder; and a view of that file when opened in the Pencil Code editor.
Files on the Pencil Code server are limited to one megabyte, so larger images cannot be saved. This is typically not an issue with the basic custom sprites that students create in this lesson. However, saveimg
also lets you save images imported from elsewhere, and these files frequently do exceed the size limit. Unfortunately, saveimg
does not provide feedback indicating whether the file was successfully created or not; you simply have to check.
To erase a .png
file saved on the Pencil Code server, use the save
function. Continuing with the DiamondStar example, delete the image file with the statement save 'DiamondStar.png', ''
. But great care must be used when using save
, as it is easy to erase the wrong file, which subsequently cannot be retrieved. In this case, the .png
makes all the difference.
What can go wrong
The most common student mistakes are omitting the call to drawon window
or failing to use dot notation correctly. These are easily remedied. The real challenge typically is identifying the problem. This is all the more likely to be the case when the default turtle was the drawer and it is no longer visible. The following program illustrates this mistake (click to see the source code):
Remember to position the drawor "on top of" the drawee. If not in a position shared by the drawee, the drawor's artistry will not be captured.
drawon document
Pencil Code includes a drawon document
block in the coding palette under "Art". However, there is a bug associated with this statement, as illustrated in this script. After calling drawon document
, subsequent turtle drawings draw on the default turtle itself. Hence, this lesson and these notes promote use of drawon window
.
Pedagogy
This lesson encourages students to experiment with the drawon
function. Arguably, the standard approach would be to have the drawee stand still while the drawer it doing its job, but interesting results can happen if we don't adhere to this. This is the motivation behind the SpinArt activity, which is designed so that students will intentionally leave out calls to drawon window
. Students can also experiment by creating shapes by intentionally "smearing" animations.
A subtle but important point for students to take away is that every coding language is written in a certain way to enable us to do certain intended tasks, but if we are clever we can use it in ways that it perhaps was not originally intended. If in so doing we succeed in our goals, that's good hacking.
Technicalities
Behind the scenes, Pencil Code sprites are at root HTML <canvas>
elements. This holds for all sprites, even turtles; turtles differ from generic sprites primarily in that turtles make use of the CSS background-image
style property.
Pencil Code adds the functionality that converts basic <canvas>
elements to manipulable sprites using custom jQuery code. jQuery facilitates actions such as animations, which Pencil Code builds on to create simple-to-use functions such as fd
and dot
.