We started today by reviewing the Week 7
class notes, in particular, we did a quick review
of timing
with millis(), then we discussed
using state variables as a way
to keep track of the phase or situation that your code is in,
and finally we discussed
using functions as a
technique for organizing larger projects by increasing
modularity and reusability.
One quick comment about this review. During our conversation I made a syntax mistake. I accidentally wrote that you could do something like this to toggle a Boolean variable value:
def keyPressed():
global switchState
if key == 'n':
# Warning: this is wrong!
switchState = ! switchState
The exclamation point is used as the symbol for the logical
inverse operator in many programming languages, including Java
and JavaScript. I got mixed up today and used this symbol. In
Python, the logical inverse operator is not. So the
proper way to write this in Python would be like this:
def keyPressed():
global switchState
if key == 'n':
switchState = not switchState
Sorry for any confusion!
(jump back up to table of contents)
For the rest of class I fielded review questions about topics from throughout the semester, mainly focused on questions people had about completing their midterm projects.
(jump back up to table of contents)This example illustrates all the basic principles of working with text in Python / Processing:
size(800,800)
background(0)
arialblack = loadFont("Arial-Black-48.vlw")
arial24 = createFont("Arial", 24)
georgia16 = createFont("Georgia", 128)
score = 42
textFont(arialblack)
fill(255,0,0)
textSize(128)
text("Hello", 100, 300)
textFont(arial24)
fill(0,255,255)
textSize(32)
text("Friends", 100, 350)
textFont(georgia16)
fill(0,255,255)
textSize(32)
text("Your score: "+str(score), 100, 450)
All the new commands here are documented in the Python Processing reference, in the Typography section.
You actually draw text to the screen with
the text() command, which you can see illustrated
on lines 13, 18, and 23. You can see that this command takes
a string of text that you want to display
(i.e., characters in between double-quotes, "
"), and two arguments that correspond to the x and
y location of this text on the screen.
If you want to render dynamic text onto the screen, use
the str() variable to convert a variable value into
a string, and concatenate two
string values together using +. You can see this in
action on line 23, which assumes that I have a variable
called score, which has some numeric value. (In
this case created on line 8.)
You can control what font to use to render this text by first
creating or loading the font that you plan to work with. There
are two ways to do this in Processing, illustrated on lines
4-6. Line 4 loads a predefined
.vlw file which has to have been created already
and saved into your sketch folder. You can create this file from
the PDE menu by going to "Tools" > "Create
Font...", specifying the font face and size you want, and
clicking "OK".
If you would prefer, you can create the font dynamically at
runtime using the createFont() command, as
illustrated on lines 5 & 6. In this way, you can create the
font when the program starts running, without having to create
the .vlw file in advance. You specify the font face
(which has to exist on your machine, exactly as typed here) and
the size. I'm guessing there might be some performance cost to
doing it this way because Processing has to create the font
while your code is running. I am assuming you would not notice a
different unless you were creating many fonts.
In either case, you either load or create the font, and assign
it to a variable. Similar to the process of
calling loadImage() and saving that image to a
variable which you can then draw later. In this case, you can
use any of these font variables later on when you are trying to
write text.
You specify which font to use with the textFont()
command, as you can see on lines 10, 15, and 20. This command
works similarly to other commands that control how later things
are drawn, like fill(), which I have also included
here to show how they are similar. textFont()
specifies the font that will be used to render any text that
comes after with the text() command, and that will
be the "active" font until you change it with another call
to textFont().
You can see that I am also using textSize(). That
specifies the size of the text that you are going to
write. Ideally, this value should probably be the same as the
value you used when you created or loaded the font. If you use a
different value, you may get some digital distortion, as you can
see this in action on line 4 in which I have created a font with
size 48, and on lines 12 and 13 in which I am then using that
font to draw text with size 128, giving some blurriness in the
draw window. (If I change textSize() to 48, or if I
create a new .vlw file with size 128, then this
distortion goes away.)
Thanks, Brandon, for the question!
(jump back up to table of contents)Next we looked at how you can have the user initiate a projectile that flies across the screen. For discussion, we imagined this was a game like an arcade basketball throw, in which the player has a container with some number of balls that they can pick up and throw. The balls roll back down to the container and they can pick them up and throw again. We started out with just one ball. (Thanks, Bibi, for this question!)
My sample code is here:
x = -10
y = 400
speed = 0
def setup():
size(800,800)
def draw():
global x, speed
background(255)
fill(0)
ellipse(x,y, 10,10)
x = x + speed
if x > width:
x = -10
speed = 0
def mousePressed():
global x, y, speed
if x < 0:
x = 0
y = mouseY
speed = 10
Lines 12 and 13 draw the ball at some x, y location. Then line
15 moves the ball with our basic "x = x +
1" pattern to increment a variable.
Lines 1 and 2 set the initial values of the ball location. Note
that initially the x value is -10. This is so that
the ball does not appear anywhere on the screen before the user
throws it. Also, speed is set to 0 so
that initially the ball is not moving.
After moving the ball on line 15, we use an if
statement to check if the ball has moved off to the right of the
screen, and if so, we reset its position back offscreen
with -10 and set its speed back to 0.
Now for the user to initiate the throw, we use a def
mousePressed block. In this event
handling block, we want to set
the xposition of the ball to 0, so it
is now visible on the left of the screen, and set
the speed to 10 so the ball moves
across the screen. (We also decided that we would set
the y value of the ball to be wherever the mouse
was when the user clicked.) But! we put these three
lines in an if statement block that checks if
the x value of the ball is less than zero, because
we only want the user to be able to throw the ball if its
position has been reset back to -10, i.e. in our
game concept, if the basketball has rolled back down and is in
the container again.
After finishing the above example I showed how you might implement this same interactive example but with multiple balls. That code looks like this:
x = []
y = []
speed = []
num_balls = 3
current_ball = 0
def setup():
size(800,800)
i = 0
while i < num_balls:
x.append(-10)
y.append(400)
speed.append(0)
i = i + 1
def draw():
background(255)
i = 0
while i < num_balls:
# draw each one:
fill(0)
ellipse( x[i],y[i], 10,10)
# move each one:
x[i] = x[i] + speed[i]
# check each one:
if x[i] > width:
x[i] = -10
speed[i] = 0
i = i + 1
def mousePressed():
global current_ball
if x[current_ball] < 0:
x[current_ball] = 0
y[current_ball] = mouseY
speed[current_ball] = 10
current_ball = ( current_ball + 1 ) % num_balls
Notice now on lines 1 - 3 that the
variables x, y, and speed are
all lists.
I am using the variable num_balls to specify how
many balls we are allowing the player to throw.
Then, in setup() on lines 12-17, we are looping
over the number of balls, and using append() to add
a value to each list for each ball — so an x
value, a y value, and a speed value
for each ball.
Next, in draw() I have a loop with the same values
(starting at i = 0, running while i <
num_balls, and incrementing by 1). Each
iteration, we are doing the same actions as in the single-ball
example, except that now we are using [i], and
taking those actions once per ball. So for each ball, we are
drawing it (lines 25-26), moving it (line 29), and checking if
it has gone off the screen (lines 32-34).
Finally, in def mousePressed(), we are doing the
same thing. Repeating the code from the single-ball example, but
now operating on list values instead of single-valued
variables. But wait! Which list item do we want to
operate on here? We are not in a loop, because this is where the
user throws a ball. So we only want to manipulate one. Which one
should it be? For this, I have added a new variable
called current_ball. This will keep track of the
next ball that the user is going to throw. Notice that on line 7
I initially set this to 0. Now, in
the mousePressed block, I'm going to use that
as the index of my lists. So that the first time the user throws
a ball, we manipulate all the first values of our three lists
(i.e., the 0 indexed values). Then, at the end
of def mousePressed, we increment this variable, so
that the next time the user presses the mouse, we will
manipulate the next ball values in our lists.
But wait again! There is one more issue here. If we
keep incrementing this variable,
eventually current_ball will become greater
than num_balls and we will get
an IndexError. The solution? Make sure that after
you increment current_ball, you check to reset it
to 0 if it has gotten too large. You could do that
like this:
current_ball = current_ball + 1
if current_ball > num_balls:
current_ball = 0
or, more concisely, you could write that like this:
current_ball = ( current_ball + 1 ) % num_balls
For a refresher of the modulo operator, refer back to
the Week 5 class notes, the
review of that during Week
6, or another example using this
from Week 6 on lists.
(jump back up to table of contents)
Sarah asked how we can draw raster images and have them move around. Here is a code snippet to do that, presented without comment:
imgX = 100
def setup():
global img
size(800,800)
img = loadImage( ... )
def draw():
global imgX
image(img,imgX,400)
imgX = imgX + 1
if imgX > width:
imgX = 0
Thanks, Sarah, for asking!
(jump back up to table of contents)
We also talked about how you can use functions with arguments and return values. I will share another code snippet without comment, because we didn't have time to talk about this in class today, but if you are curious about any of this code, please don't hesitate to ask! I would be happy to explain.
def setup():
size(600,600)
def draw():
my_function(5)
my_function(7)
print("The midpoint is: ", midpoint(10,20) )
def my_function(n):
rect(n,10,10,10)
def midpoint(x,y):
m = ( x + y ) / 2
return m
Thanks, Lizzy, for asking about this! I wish we'd had
time to get to it.
(jump back up to table of contents)
That's all, everyone. Good luck on the midterm! If you have any questions, don't hesitate to ask.