Overview
Learning Objectives
Students learn how to connect keypress events with their functions
Students get practice with piecewise functions
Evidence Statements
Product Outcomes
Students implement the entire Sam the Butterfly activity from Bootstrap:Algebra with a character of their choice
Materials
Pens/pencils for the students, fresh whiteboard markers for teachers
Class poster (List of rules, design recipe, course calendar)
Editing environment (Pyret Editor)
Student workbooks
Language Table
Preparation
The Moving Character file preloaded on students’ machines
We’ve already seen one kind of interactivity in our programs: getting the next state from the current state on a tick-event. This is perfect for animations that happen on their own, without any user intervention. In a game, that might be clouds moving across the sky or a ball bouncing on its own. An important kind of behavior in interactive programs is to respond user input, such as keypresses. A keypress, like the tick of a clock, is a kind of event, and we’ll re-use the idea of an event handler like on-tick and a function like next-state-tick. For key-events, the event handler is called on-key, and our function next-state-key will compute the next state from the current one after a key event. We’re going to use this idea to build up a reactor with a character moving in two dimensions, where the movement is triggered by keypresses.
Open up the Moving Character template file.
It contains a data block for representing a character’s position (CharState) that has an x and y position.
Write an example instance of a char-state where both the x field and the y field are between 100 and 500. Give it the name middle.
We’ve filled in a picture of Sam the Butterfly from Bootstrap:Algebra. There is a drawing function called draw-state provided that simply draws the character image on a white background at the x and y coordinate in a CharState.
Run the program, and use draw-state to draw the example instance you created above. Did it appear where you expected?
This is a reminder that it’s often useful, when working on programs that use data to represent positions in an image, to make sure we understand what values in the data structure correspond to which drawing behavior.
Write an example instance that represents the butterfly in the top-right corner of the window. Give it a meaningful name of your own choice. Re-run the program, and check using draw-state that it showed up where you expect.
There is also a contract for a function next-state-key, which looks like:
How does the contract of next-state-key differ from the contract of next-state-tick in your previous programs?
It is different from the contract for next-state-tick (which handles tick events) in an important way. When a key event happens, the next state may differ depending on which key was pressed. That means the next-state-key function needs both the current state and which key was pressed as parts of its domain. That’s why next-state-key has an additional String input, which represents the key pressed by the user.
Create an example instance that corresponds to the position 5 pixels to the right of the example instance you wrote above. Use draw-state to check it, as before.
This gives us a good input and output test for the examples block when working on next-state-key. What call to next-state-key should connect these two example instances?
Use the Design Recipe to fill in your examples and definition of next-state-key. Use the sample instances you created before in the examples block.
It’s an important point that next-state-key takes in an extra piece of information: the pressed key. This makes it much richer in terms of its purpose statement, which should describe what different keys ought to do to the state of the reactor.
Students will create something like this completed file by adding a next-state-key function
Once you’ve implemented next-state-key, experiment with it in the interactions pane:
Try draw-state(next-state-key(middle, "left")). How is the output different from draw-state(middle)?
Try using a few different calls to next-state-key to move the character several times, then draw it. For example:
As with ticks-events, we can manually pass keypress strings into this function, see what the next state would be, and even draw that state to see what it looks like. That’s great, but we still want to hook this function up to a reactor, so that it actually handles keypresses from a user playing the game. To do this, we need to create a reactor use on-key to specify that our next-state-key function should be called when the user presses a key (we don’t need to specify an on-tick handler, since for now the only movement in our program comes from keypresses). Our reactor with a to-draw and on-key handler looks like this:
Make your program create a reactor by that uses the on-key handler with the next-state-key function you just implemented. Run the program and use interact(char-react) to start the reactor. Does it work the way you expected? If it doesn’t, check:
Does the program have any typos or syntax errors?
Do the examples of next-state-key match what you expect, creating a new char instance with appropriate x and y values?
Do the examples pass the implementation of next-state-key?
Did you remember to add on-key to the reactor?
Did you remember to re-run the program and use interact to start the animation?
With this working, you can see the behind-the-scenes work that was going on in Sam the Butterfly from Bootstrap:Algebra. To get to the same point as in Bootstrap:Algebra, we’d next implement is-onscreen to check if Sam has left the board, and use it in next-state-tick.
Act out a reactor with key-events. You will need four students: one who acts as the next-state-key function, one who acts as the keyboard (you could also have the class act as a keyboard by having students shout out keys), one who acts as the reactor, and one who acts as the draw-state function. Give each student a few sheets of paper and something to write with.
When a key is "pressed" by the keyboard, the reactor write doesn the current state and the key that was pressed, then shows their paper to next-state-key.
next-state-key produces a new state based on the current state and the key, writes it down, and then hands the new state back to the reactor.
The reactor discards their old state, replacing it with the new one, and shows the new one to draw-state.
draw-state produces an image for the reactor to post, and draws it on paper. They hand the image to the reactor, who holds it up as the new frame in the animation.
We recommend not having a next-state-tick function for this activity, to keep the focus on key events. You can add a on-tick handler in a separate stage when talking through games which have both time- and key-based events.
Optional: implement boundaries to keep character onscreen, using the same ideas as safe-left and safe-right from before. You can also write safe-top and safe-bottom, and use all of them to keep the character fully on the screen.
Optional: use num-to-string and text to display the position at the top of the window.