Lab 2: Flying over Lake Michigan
Beginning Student with List Abbrev.
(require 2htdp/image)
(require 2htdp/universe)
This lab is an introduction to universe programming with a secondary emphasis on compound structs. For today's exercise, you will build an interactive scene displaying a flight over Lake Michigan as seen from an airplane window. After putting together some basic animation, you will modify the code to give your user (to a very limited extent) the ability to control the plane's flight. The simple toolkit you'll start learning today opens the door to a challenging and essentially limitless realm of interactive visual software.

Animation Essentials

DrRacket's universe library enables you to create interactive animations. In the universe terminology, a world is a value that characterizes the modeled world at a given instant. The world may be modeled by a simple atomic piece of data, such as a number, or by a piece of compound data, as in today's exercise. The display is updated periodically to reflect the state of the world as it changes. Note that a "world" is not a particular data structure; it is whatever it needs to be to serve the application at hand. Today's world model is a custom-designed struct, discussed in more detail below.

A universe consists of the following components:

  1. the value of the world in its initial state. For today's lab, we will supply you with an initial world value named init-world.
  2. a "drawing function" of type world -> scene to render the world as a picture. (A scene is very much like an image, but engineered for use in animations. You operate on scenes with a different set of operators than images.) Today we'll call this function render.
  3. a "time-step function" of type world -> world that consumes a world, and produces the value of the world as it stands one time-step later. Today we'll call this function tick.
Additionally, you can write different functions to react to different user events, such as keystrokes. Today you'll write a function read-key : world key -> world to change the world (or not) depending on keystrokes given by the user.

You will need to refer to the 2htdp/universe and 2htdp/image documentation while you work on this project.

Getting Started

Download airplane-starter.rkt and open it in Dr. Racket. As before, put your name and the name of the lab in a comment at the top. Read the code and try to understand what it means. To assist your understanding, evaluate the defined names and call functions freely in the interactions pane.

Pay special attention to the data definition for world. A world consists of three clouds and a sun. We will follow this convention: the cloud c1 is closest to the viewer, c2 is next closest, then c3. The sun is, of course, the farthest object in the sky, and must appear behind all clouds at all times.

Part 1: Render the World

Call background with different arguments to understand what it does.

Read about the place-image function. You will need it.

Write a function place-sun : sun scene -> scene to draw a given sun in a given scene. Evaluate

(place-sun init-sun (background bg-width bg-height))
to make sure it's working.

Write a similar function place-cloud : cloud scene -> scene. Note we have provided a function cloud-pic to draw clouds of a given size. Try it out with different clouds to make sure it too is working.

Write a function overlay-wing : scene -> scene to draw a wing on top of the given scene. We have a provided a wing-pic to use as the wing image to overlay.

Use all these pieces to write a function render : world -> scene that displays the whole world: the sky and water, the sun, three clouds, and the wing in the extreme foreground. Use the constants bg-width and bg-height in drawing the background. It is important to draw the objects in the right order. Draw the sun first, for example, to make sure no clouds can appear behind the sun and destroy the realism (such as it is) of the illusion of depth.

You can test your function by rendering init-world.

Part 2: Animate!

You will now add movement to the scene. The clouds should pass by the window at different rates of speed so there is a sense of depth to the animation. The sun and wing will both stay put.

Begin by implementing the following functions:

  1. move-x : num pos -> pos. Add the given number to the x-coordinate of the pos argument. For example, (move-x 1 (make-pos 1 2)) should return (make-pos 2 2).
  2. move-y, similar.
  3. move-cloud-x : num cloud -> cloud. A call to (move-cloud i c) should move the cloud c i pixels along the x axis.

Put this together into tick : world -> world to manage the time-step changes to the state of the world. In one time step the clouds should all move to the left at different rates of speed -- c1 fastest, then c2, then c3 -- and the sun should remain where it is.

Having written render and tick, you will be able to view your animation as follows. Paste this code at the bottom of your definitions:

(big-bang init-world (on-tick tick) (to-draw render))
Click run and enjoy!

Part 3: Take Control

You will now modify the program to support simple up-and-down control of your airplane. The modification centers around a function

read-key : world key -> world
The function should behave as follows: The wing should never move; nor should the sun.

In read-key, you can test the key argument by testing it with the key=? predicate and the strings "down" and "up". In other words, if the user has just pressed the up arrow, the test

(key=? k "up")
for key k should evaluate to true.

Note that, once again, to simulate depth, the closest cloud should always move up (and down) faster than the next closest.

Now modify the call to big-bang as follows:

(big-bang init-world (on-tick tick) (to-draw render) (on-key read-key))

Enjoy again!

Enhancements

This a project that is well-suited to various enhancements. Here are a few suggestions, but understand this first:

Ideas:

Document any enhancements clearly in your comments so we can have a look.

Submit

Submit your work by clicking the CS151 Handin button on the DrRacket toolbar by 10 PM on Sunday (Oct 10). Make sure your work is submitted as Lab2. As always, feel free to mail the lab instructors or the mailing list with any questions.