CS 161 Lab #3

September 14th

Goals:

This week you'll extend the Circle class to create an Asteroids-like "boulder" that moves across the screen and wraps around when it hits the edge. This will give you some practice with fields, constructors, conditionals, and output. (The code we wrote Wednesday using if and println is online for reference.)

Bouncing Some Boulders:

The Circle class is a good starting point for what we need: it can draw itself on the screen and update its position in response to calls to methods like moveUp(), etc. But we want our "boulders" to be able to move in any direction, and to keep on moving in straight lines. Thus, we'll add two new fields to keep track of the velocity in the X direction and Y direction. We'll think of these as the number of pixels on the screen to move in the X and Y directions each time the boulder's updatePosition method is called. For greater precision, we'll store these in fields of type double so that they don't have to be integer values. (That is, a boulder could move 4.7 pixels horizontally at each update rather than just 4 or 5.)
  1. Take a moment to introduce yourself to your partner(s). After social pleasantries are complete, pick one member of the team to be the "typer". They'll share their screen while editing the lab code in BlueJ. Group members should contribute equally while working through the problems below and discuss all code to be written, though only the "typer" will be able to edit code. Resist the temptation to have both members work simultaneously in BlueJ — you're much more likely to "drift apart" over the course of the lab if you do so. The goal here is to have a partner who's engaged on exactly the same step of the lab as you are.

    Consider letting the less experienced member of the group do the typing if it seems like there's a mismatch in experience or comfort levels. That will help make sure they don't get left behind. Worst case, flip a coin to see who does the typing. Or have Java generate a random boolean value. (Type "(new java.util.Random()).nextBoolean()" in the codepad without the double quotes.)

    At the conclusion of the lab, make arrangements for the typer to share a copy of the code with the other member(s) of the group if desired. (E.g. email it, or put it on a shared Google drive, etc.) My solutions to the lab will get posted as well.

  2. Download the BouncingBoulders project and extract its contents, then start BlueJ and open the project.
  3. Take a moment to familiarize yourself with the Java code in the Boulder class. Notice, in particular, the two new fields at the top to keep track of a boulder's velocity.
  4. Write the code that goes in the body of the constructor. It should set up the state of the object as specified by the user, given the values they pass in, and also make sure that the boulder is black. At this point you should be able to create Boulder objects and interact with them. (Make sure you test your code before you proceed!)
  5. At the moment, the printStatus method just prints the size of the window in which our boulders are being drawn. Add some additional output so that it also prints the boulder's position and velocity as shown in the output below. Note that printStatus is making calls to methods in the Canvas class to see how big it is. You'll need to use this same trick a bit later in the lab.
    > Boulder b = new Boulder(100, 200, 7.3, 12.6, 50);
    > b.printStatus();
    Boulder is on a canvas that's 800 by 600
    It's at 100.0, 200.0 with velocity 7.3, 12.6
    
    The blue box above is showing commands to be typed into the codepad window (the lines starting with ">"), as well as the printed output that these commands would produce. The printed output will actually appear in the terminal window, not the codepad, but it's easier to describe when it's all shown in one box. (Previous versions of BlueJ used to have a marker that looked like the ">" for lines typed into the codepad, so I've continued using it even though BlueJ changed its formatting.)
  6. There's an updatePosition method that advances a boulder a bit farther along its path each time the method is called: it erases the the boulder, updates its position by the appropriate distance horizontally and vertically, then draws the boulder in its new location. Create a boulder and call updatePosition on it enough times to see what happens when it reaches the edge of the screen. The easiest way to do this is to use the codepad, since you can use the "up arrow" on the keyboard to quickly repeat the most recent command.
    > Boulder b = new Boulder(100, 200, 7.3, 12.6, 50);
    > b.makeVisible();
    > b.updatePosition();
    > b.updatePosition();
    > b.updatePosition();
    > b.updatePosition();
    > b.updatePosition();
    > b.updatePosition();
    ...
    
  7. Now fix the logic in updatePosition so that it wraps around when it hits the edge of the screen. There are four cases you need to consider: moving horizontally off of the left edge or the right edge, and moving vertically off of the top or the bottom. To make things even more interesting, more than one of those conditions might occur on a given position update!

    Don't feel like you need to fix all four of these cases at once. Pick one (moving off of the right edge, for example) and try to get that case working first. Think about how you'd write the test for an if statement to detect when a boulder's position was off of the right side of the screen. If it did go off of the screen, how would you fix its position? (In other words, what should the code look like inside the body of the if statement?) Add an if statement to updatePosition to try to handle that one case. Test it and make changes if necessary, then think about how (and where) to add code to fix another of the four cases, etc.

    The output below is showing the results of a test where two of those conditions occur at the same time. A boulder is positioned near the bottom right corner, with a velocity that carries it off the bottom of the screen and off of the right edge of the screen on its next position update. Notice, also, that when it wraps around it doesn't just start at the left edge of the screen or the top. It was at an X coordinate of 780, so a velocity of 40 carries it past the right edge (20 pixels away), and it continues on another 20 pixels after it wraps around to the left edge of the screen. Ideally your code will detect both of those on a single call to updatePosition and wrap the position back to the top left corner as shown in the printStatus results after the move.

    > Boulder b = new Boulder(780, 580, 40, 40, 20);
    > b.makeVisible();
    > b.printStatus();
    Boulder is on a canvas that's 800 by 600
    It's at 780.0, 580.0 with velocity 40.0, 40.0
    > b.updatePosition();
    > b.printStatus();
    Boulder is on a canvas that's 800 by 600
    It's at 20.0, 20.0 with velocity 40.0, 40.0
    
  8. The BouncingBoulders class has a method that takes a pair of Boulder instances and makes repeated position updates to them. It lets you watch the boulders wander around the screen for long enough that they should wrap a few times. You don't need to understand how the method works, but you should still be able to call it. Create an instance of BouncingBoulders first. Then make two separate Boulder objects. Finally, call the testBoulders method on the BouncingBoulders instance and pass it the two Boulder objects as parameters.

    You can do this entirely via BlueJ's point-and-click interface, or via the codepad. If using point-and-click, just type the boulder objects' names as the inputs to testBoulders. In the codepad you could do:

    > Boulder b1 = new Boulder(100, 50, 10, 10, 20);
    > Boulder b2 = new Boulder(100, 80, 10, -10, 20);
    > BouncingBoulders bb = new BouncingBoulders();
    > bb.testBoulders(b1, b2);
    
If time permits, try some of the following exercises:


Brad Richards, 2023