We are so excited to introduce discussion sections to CS111 this year! These are great opportunities to develop analytical and communication skills, which will help you engage with SRC content throughout the course more deeply. Discussion 1 is a case study activity. In a small group, you will read about a situation where the use of data impacts peopleโs lives. Then, you will answer questions about the caseโs stakeholders, potential consequences, the values at stake, and the responsibilities of data users. These discussions are not graded, but pay attention, as we will ask you to write a reflection on things you learned in them later on.
โ
For this lab, we suggest you have a shared Google Doc between you and your partner for written answers, and a Pyret file for your code.
The objective of this lab is to show how animations can be created from functions and conditionals. Animations also give us another example of how we can break down a seemingly complicated computation into smaller steps that we already know how to solve.
We will practice breaking down the problem of creating an animation step by step using the skills that we've developed so far. After that, we'll learn a Pyret tool (the reactor
) that can help us automate some of the process of creating an animation.
If you don't feel comfortable with the concept of a reactor after working on this lab, come to TA hours! The content of this lab will be important for Project 2, and your TAs would love to help in any way they can.
In this lab, we're going to animate a gif of the beloved 2000s character Eve alighting on a landing pad in Pyret โ perhaps you could imagine that you're an animation programmer at Pixar Animation Studios working on the sequel to the critically acclaimed film, Wall-E.
Take a few minutes to understand this gif:
Write down your answers to these questions in your Google Doc.
In class, we've talked about identifying the structure of a single image, then using that to write a program to produce the image. Here, we have an animation, not a single still image. What is the structure of an animation?
Discuss with your partner: what might the structure of an animation be? Are there simpler pieces that one could combine somehow to get an animation? Write down an answer in your Google Doc.
Only read on after you've written something down โฆ
An animation is actually a sequence of images that some tool (a program or film projector) flips through quickly (creating the illusion of motion). Here's an example sequence of these images (usually called frames) from our robot lander:
Discuss with your partner: answer the following questions in your google doc:
You will now start to build your animation! Smooth animations require many, many frames (Pixar movies use between 24-60 frames per second!). We could build our animation by writing expressions that generate images for each frame. But creating these images is repetitive โ since only the y
-coordinate of our robot is changing, do we really need to generate the frames one-by-one? What do we usually do when we find we are repeating the same code multiple times?
Task: Stop and discuss your response with your partner.
Only read on after you've discussed with your partner โฆ
Rather than generating frames one-by-one using expressions, you will build a function that can generate any valid frame for the animation.
Building Frames
The following section gives you the resources to create the Wall-E-themed frames we displayed earlier in the lab.
Think back to your list of the elements of the animation from earlier in the lab: we need the navy background, an image for the landing pad, and an image for the robot. The background is just a rectangle
.
Now we need Eve's image and the landing pad. Pyret has a useful function image-url
for loading images when you donโt want to build an entire graphic from scratch (URL stands for "Universal Resource Locator"โฆ a web address!). image-url
takes in the url of a picture from the web and outputs it as a Pyret Image. Once itโs loaded, you can manipulate it the way you would any other Image.
Our version used these images:
Youโre welcome to find (or build!) your own, or just copy our code to get started.
Building the Function
Task: First, think about the input and output of your function to produce a frame. Discuss with your partner what the function signature should be.
Task: Create a function called falling-robot
that takes one Number
input (representing the y
coordinate) and produces the frame image with the robot at this y
-coordinate.
Hint: You might find the function place-image
more useful than overlay-xy
, as it allows you to fix one image (e.g., the background) and put another (e.g., the landing pad) on top of it using a coordinate. You can nest calls to place-image
, just as you have before with overlay-xy
.
Note: The origin of the background grid (the point at (0,0)) is at the top left corner of the image. Therefore, increasing y
values is equivalent to moving down on the image.
Hint: You can use the scale
function to make your robot/landing pad larger or smaller.
Hint: Experiment first to create a single expression that generates the image that you want at some coordinate, then move that code into the body of the function and use the function input parameter for the y
coordinate instead.
Call a TA over once you've written your frame generator function.
With falling-robot
, we can generate an entire sequence of images, for example by writing:
This sequence of expressions creates all the frame images for an animation, but we still seem to be writing (roughly) the same expression over and over. If we want to automatically generate animations, it would be great to have a program generate the sequence, not just the individual images.
Task โ Discuss with your partner: Previously, we have looked at multiple expressions to see whether they differ in a few spots. This time, we want to ask a more sophisticated question: is there a pattern across those differences? Look at the sequence of falling-robot
calls just above. Do you see any sort of pattern across each consecutive pair of falling-robot
calls?
Hopefully you noticed that the y
coordinate is increasing by 20 from one expression to the next. The same computation (increase by 20) over and over? That also sounds like a function! Add the following code to your file.
Step back and think about what the combination of falling-robot
and update-coord
will let us do. If we have an initial value of the y
coordinate, we could use it to draw the first frame. We could use update-coord
to get the next coordinate and draw the second frame. We could repeat this sequence to continue generating images.
In other words, these two functions define an animation! If we could get Pyret to use our functions to generate the images and flip through them, we'd be done. Fortunately, Pyret has such a feature.
Task: Stop and note any questions you have about where we are at this point. Write them down and discuss them within your pair.
We now have two functions that work with y
-coordinates: falling-robot
produces an image at one y
-coordinate, while update-coord
produces the next y
-coordinate at which to draw an image. If we can make these two functions work together, we can get an animation without us having to create images by hand.
Luckily, Pyret has something called a Reactor
that coordinates these functions for us. Add the following code to your file, then run it:
You should now see something like the first animation we showed you! (the robot will fly past the landing pad and go off the bottom of the screen โ we'll fix that later)
A reactor is a special value in Pyret that has different components that play a part in creating the animation you see when you call interact
on the reactor. Let's look at what the components of the reactor are doing:
init: 0
โ the initial y-coordinate for our reactor; this is the initial value of what will be changing frame by frameto-draw: falling-robot
โ the function that returns an image based on the current y
-coordinateon-tick: update-coord
โ the function that produces the next y
-coordinate for our reactor, frame by frame (tick by tick). Its first input will be the init
value.Here's a visual of what the reactor is doing:
Task: Look at the Reactor diagram and the code. Note any observations or things you are curious about regarding reactors in your Google doc.
Call over a TA once you reach this point.
What's the difference between an animation and an interactive game? In a game, how elements move are influenced by what a player does (like pressing keys). So far, our reactor uses update-coord
to move the robot every few milliseconds. Now, we want to have the y
coordinate be influenced by key presses (keeping the robot in the air longer as it falls).
Specifically, we want to modify our reactor so that if a user presses the "b"
key, the robot gets a "boost", which reduces its y
-coordinate by 40 pixels. To do this, we need the following function, which Pyret will call whenever a key gets pressed. It returns the next y
coordinate based on which key got pressed:
Task: Create a good set of where
examples for this function.
Task: Write the function.
Task: Add the boost-robot
function to your reactor like this:
Try playing your new game!
Optional Task: If you want to, extend your boost-robot
to also recognize key "t"
, which "turbo boosts" the robot upwards 150 pixels. (Or you can go on to some of the other options to extend your animation.)
Reflecting on Functions
Let's step back. up until now, we have created functions for two main purposes:
While we motivated writing functions from the first purpose at the start of lab, reactors seem to use functions for a slightly different purpose: they bundle up a computation so that another piece of code (that you didn't write) can use it when needed. Here, the reactor needs to know what you want to do when a key is pressed, what image it should draw, etc. The reactor knows how to make animations (draw pictures, update coordinates, repeat), but only YOU (the animation designer) know the details that you want. Hence, the function becomes a communication mechanism: you give details to another piece of code that performs a common task.
Task: Discuss this with your partner: does this new use of functions make sense? What questions do you have, whether about functions or reactors? Put your questions in this Google Form (so Milda can review them).
Call over a TA once you reach this point.
Here are some additional features you can implement if you have time. You could also come up with your own (remember, you can include more behaviors with additional keys).
Wrap-around:
What happens if Eve misses the landing pad? Would Eve tragically crash to the ground? (Try this out using your current reactor.)
Modify your on-tick
function so that if Eve goes off the bottom of the screen, it starts again from the top (PHEW!).
Collisions:
Write a function found-landing-pad
that takes in a y
-coordinate representing the position of the robot and returns a Boolean
indicating whether Eve has landed on (or collided with) the landing pad.
Add it to your reactor like this:
Congratulations Image:
Finally, congratulate yourself for landing Eve on your landing pad by modifying to-draw
so that if there is a collision
(found-landing-pad
returns true), the image becomes a congratulatory image of your choice. In order to implement this, make sure to take out the stop-when
part of your reactor.
Call over a TA once you reach this point.
Thanks to your excellent animation skills, you were able to rescue Eve from missing their landing pad! Safely on your landing pad, you can now sit back, relax, and enjoy the movie.