Engineering Computing

PI: Laboratory 5: Graphics, Painting, and Containment

Overview

In this lab, you will get to know some key parts of the java AWT, namely painting and containment. Polymorphism also plays a small role, but you may choose to deal with it in more detail if you have time.

As usual, you should read the general information handout for the collaboration policy. For this assignment, you may discuss the project in as much detail as you like with your classmates, but you should do the writeup on your own. You may also get comments from other students on all portions of your writeup before turning it in, if you wish. Please include the names of anyone with whom you collaborate, in any way, on this assignment, and indicte the nature of the collaboration. If you do not collaborate with anyone on any part of this assignment, please state so explicitly. [Failure to include this information is a violation of the collaboration policy.]

You will be turning in the code you write for this lab. This code must be your own. We do encourage you, however, to ask your classmates for help debugging. Helping someone else debug their code is a great way to improve your own programming skills.

Breakout resources:


Contents


Intro

Concept

We're going to be writing part of the code for a Breakout game. Specifically, the code that defines how a particular brick behaves.

If you're not familiar with the game Breakout, the basic idea is this: You're in a box that contains a paddle, a ball, and rows of bricks. You can move the paddle from side to side, but not up or down, and it is your only method of controlling the movement of the ball. The object of the game is to use the ball to destroy all of the bricks, without letting the ball hit the ground.

Basic Gameboard Pieces

In our version of Breakout, we have defined several different sets of behavior for you. Every game object(balls, bricks, etc) implements the BreakoutComponent interface. Most objects do this via a sub-interface that more specifically classifies what kind of object it is. For example, the Brick interface is a subclass of BreakoutComponent that exists so that the Board can tell whether you've won the game(cleared all the Brick objects) or not.

Here is a brief summary of the Breakout interfaces you might find it useful to know about:
  • BreakoutComponent - all game objects implement this interface in some fashion.
  • Rebounding - all game objects that need to do rebound checks(ie, moving objects that bounce off of things) are required to implement this interface.
  • Brick - BreakoutComponent - When the Board doesn't have any Brick objects left, you have won the game. No different than BreakoutComponent.
  • Ball - BreakoutComponent and Rebounding - When the Board doesn't have any Ball objects left, you have lost the game. Every tick, a Ball object checks to see if it has hit anything, and if it has, calls that BreakoutComponent's hit method using itself as the argument. Adds no additional functionality to the BreakoutComponent and Rebounding interfaces.
  • Wall - BreakoutComponent - A generally benign object that serves to keep the Ball inside the visible space. A Wall whose killing() method returns true(generally the Wall acting as the floor) has the added functionality that when it is hit, it hits back. Has added functionality to the standard BreakoutComponent by way of the killing() method.
  • Paddle - BreakoutComponent - Used to keep a Ball object from hitting the floor. No different than BreakoutComponent.
(Don't forget -- the javadoc can be your friend!)

How Breakout Works (and paint too)

You have a Board. A Board keeps track of all the game objects(the Bricks, Balls, etc) but the Board doesn't really know the difference between a brick, a ball, a wall, or a paddle -- it just has a list of generic BreakoutComponent objects. The BreakoutComponent interface guarantees that everything on the Board will be able to answer some basic queries about what size it is, where it is on the board, whether it's an ellipse or a rectangle or whatever, and some other things that you can look at on the BreakoutComponent javadoc if you wish. One of the more important methods it defines in the context of this lab, though, is paint.

paint is a funky sort of method. Unlike most of the methods you've seen so far, you the programmer are not the one who decides when and where this method is called. In fact, you'd probably find it pretty difficult to call this method explicitly on any BreakoutComponent, even if you tried, because you're missing the key ingredient: a Graphics. A Graphics object is what does most of the work in a paint method -- it knows how to draw ovals, rectangles, lines, text, and even images. But unfortunately, you can't create a Graphics out of thin air like you might do new String() or new Frame(). Go ahead, check the javadoc. The constructor for Graphics is protected: you're not allowed to create one directly, which means you can't call paint.

Who, then, does call paint?! This is what makes paint so neat. Java itself decides when paint gets called. paint happens any time a window, or part of a window, needs to be painted -- like when it first appears, or when you momentarily drag something over top of it, and drag it away again.

So... what happens if you want something to be animated? If you can't tell your component to paint, how do you change what shows up on the screen after it paints it the first time?

It would be really aggravating if the answer to this question was, "You don't. Tough." But then, people probably wouldn't use Java for as many entertaining things as they do. Luckily, all components have a nifty "hidden" method called repaint() that Java handles all by itself -- you don't have to write a word of code for it. When you call repaint on a Component (a java.awt.Component, not a BreakoutComponent -- sadly, BreakoutComponent is only a shadowy simulation of what a Component ought to be) Java knows to go and find the Graphics it used to paint this Component last time, and uses the Component's paint method to see how it ought to be painted. repaint() is like the Component saying, "Hey! I changed! I need to be painted again!" just like what happens when you cover up the Component for a bit and then uncover it again.

In Breakout, we don't really need to worry about repaint()(though certainly don't forget what you just learned!). In Breakout, the Board is getting automatically repainted every few milliseconds. World, the object that controls the "clock" for Breakout, knows that things are moving all the time, and is constantly running a loop telling everything on the board to update themselves, and telling the GUI components to repaint(which, in turn, tell all the BreakoutComponents they know about to repaint as well). Part of the target exercise of this lab is to understand the basics of writing the paint(Graphics g) method of a Brick object. Take a moment to browse the javadoc for Graphics in the Java API.
  • Q: What methods are available to you in Graphics that let you draw things on the screen?
  • Q: If I handed you a Graphics object, how would you use it to draw a 20x40 oval? What code would you add to make it draw this oval in red?

Other important Breakout classes

How to control the painting of the gameboard objects is what we're most concerned about with this lab. However, in order to get Breakout to run, we need to briefly look at some of the beefier classes that help control how the whole game works(rather than just how one particular gameboard looks over its lifetime). These classes are BreakoutUI, Board, BoardPanel, and World.

We already talked about World somewhat, but you might want to look at its introduction in the javadoc to get a better idea of the activity that happens during each "tick" of the game. (Remember the loop we put in the run() methods in NodeNet? each "tick" is one iteration of World's run() loop.) You won't have to worry about most of it -- all we're really interested in for now is the very last step where World redraws the board. Note that we don't have to create a World: that's BreakoutUI's job.

BreakoutUI and BoardPanel are a pair of classes that form the user interface for Breakout. BreakoutUI houses the actual JFrame that is the window you see on the screen, and BoardPanel is a section of the frame that houses the gameboard. Creation of the BoardPanel is also BreakoutUI's job, so we don't have to worry about that either. Jot down the constructor and methods available in BreakoutUI. We'll come back to them shortly.

The Board class is getting much closer to the stuff we're actually going to be working with during this lab. We already know that a Board object is the thing that actually keeps track of each individual brick, ball, paddle, or wall. Look in the javadoc for Board's constructor.
  • Q: What argument does Board's constructor take?
  • Q: How might you get ahold of this argument? (Hint: Go back and look at BreakoutUI's methods) With this in mind, which do you have to create first, BreakoutUI or Board?
  • Q: BreakoutUI needs to know about any Board you create before it can display the Board in its BoardPanel. How do you tell BreakoutUI about your Board?
While we've got the javadoc for Board handy, take a look at the available methods. (there are a lot of them!) In particular, look at the ones that start with the word add, since those methods will be very important during this lab.
  • Q: What kinds of objects can you add to a Board?
  • Q: If you wanted to add a Brick object to a Board, what code would you write? What if you wanted to add a Brick object to the Board at the location (50, 30)? (Hint: you may want to look at the javadoc for java.awt.Point)


The Lab

One

Getting started with Main

The first thing you need to do is set up a file called Main.java, which will define a class with a special method that starts the whole thing running.
  1. Create a file named Main.java and add it to your project. Make it part of the breakout package by putting
    package breakout;
    at the top of the file.
    • The only method you need in the Main class is the "main" method:
      public static void main(String[] args) {}
  2. Put something inside the main method to check and see if we've set things up correctly. If you want to be boring and get on with the rest of the lab, Hello World is quick and easy, and your next step is here. The rest of us are going to try something more interesting:
    boolean running=true;
    while(running) {
    String instruction =
      javax.swing.JOptionPane.showInputDialog("Now what?"); if("stop".equals(instruction)) {
    running = false;
    }
    }
    Alright, a quick explanation:
    We basically created a while loop that keeps bringing up a dialog ask box until you answer "stop".  javax.swing is a java package that does a lot of similar things to java.awt, but with lots more cool features -- like JOptionPane. JOptionPane is a really nifty tool that does a lot of the simpler dialog boxes and option panes automatically, like the ask box we're using here. showInputDialog brings up a dialog box with a prompt and a place for the user to enter text. The String you pass into the method becomes the prompt, and the String the method returns is whatever it gets from the user. We test if that text is the String "stop", and if it is, we stop, and if it isn't, we keep asking until the user complies.  :-)
    Set breakout.Main as the target of this project, and run it. It should bring up the pretty little ask box, and keep bothering you with it until you enter "stop".

Two

Set up a BreakoutUI
  1. In order to get Breakout to appear, we need to create an instance of BreakoutUI, the user interface engine for Breakout. Change the body of the main method so that it creates a new BreakoutUI object. If you need to, refer back to the BreakoutUI javadoc to make sure you've got the constructor right.
  2. Run Breakout. You should see a nice little window with some text telling you to load a Board. Click the load button to select a boardfile(you can download these from the resource list if they didn't come with the code) and play some breakout... you know you want to.

Three

Building a Brick

Everything that you see on the breakout Board implements the BreakoutComponent interface. BreakoutComponent defines some basic functionality such as location getters/setters, painting behavior, and ingame activity.

Take a look at the javadoc for BasicBrick. First check out the interface it implements -- Brick.  Brick is an interface that extends BreakoutComponent without adding any functionality, and basically just lets the Board know that this particular BreakoutComponent is a Brick. Why this is useful: when all the Brick objects in the Board are gone, you've won the game.
  1. Create a new Brick class that extends BasicBrick. For now, the only methods we're going to write are a constructor and a new paint method.
  2. Write a constructor that passes a width and a height to the super-constructor. You may have noticed in the javadoc that BasicBrick doesn't have a no-args constructor. If we don't explicitly call the super-constructor like this, java will try to automatically call a no-args constructor on BasicBrick, which doesn't exist(and we can't have that). You can choose whether or not your new brick's constructor takes a width and a height, or whether it doesn't take any arguments and just calls the superconstructor with some default size in mind.
  3. Write a paint method. This is what the UI will use to draw the brick. For now, use the Graphics object that gets passed to paint to draw something basic, like
    g.drawRect(0, 0, this.size.width, this.size.height);
    This will draw the outline of a rectangle at the brick's location [Note: the (0,0) is with respect to this brick, not the whole panel] with the same width and height as this Brickthis.size is a field belonging to BasicBrick -- but since it is protected instead of private, we can use it directly here.

Four

Adding our Brick to a Board

Remember that Breakout uses a class called Board to keep track of all the game pieces -- bricks, balls, paddles, and walls. When we clicked "Load" before, Breakout looked in a configuration file to get the information it needed to build and load a new Board object. We're going to use a simpler process to build our first board.
  1. First, take a closer look at the javadoc for Board. Notice that it takes a BoardPanel object as a constructor argument. Remember that BoardPanel is the little section of the BreakoutUI that the Board gets painted on, and Board needs to know how big it is before it will let you add bricks to it. In your prelab, you figured out how to get ahold of the right BoardPanel object to pass into the constructor. Use this to create a new Board object in your main method.
  2. Now we can use one of Board's add methods to add a new instance of our Brick to the Board. By looking at the javadoc, we see that there are two choices for basic adding; one takes just a BreakoutComponent and the other takes a BreakoutComponent and a Point. The latter lets you place bricks exactly where you want them; the former utilizes a rudimentary layout manager to arrange bricks on a grid based on the largest Brick in the Board. Since our new Brick extends BasicBrick, it is already a BreakoutComponent and can just be added directly. Pick one of the two add methods, and add your Brick to the Board.
  3. Run breakout to see what we've done. If you don't see any bricks, don't be surprised -- we forgot something! The prelab also mentioned something about letting the BreakoutUI know what Board you were using... we need to do this before our Board will show up in our BreakoutUI. Add the code to load the Board into the BreakoutUI into your main method.
  4. Now try running breakout. Your Brick should appear in the upper left corner of the Board(or the location of whatever Point you specified) and the text at the top of the frame should be telling you to press "Start" instead of "Load".

Five

Because breakout is less fun without a paddle & a ball

Let's make our new Board actually playable.
  1. Conveniently, Board provides two methods, addDefaultWalls(), and addDefaultBallPaddle(), that will fill out the rest of the pieces necessary for breakout. Call these methods on your Board before you load it into the UI, and this time when you run breakout, you can click Start, and actually play.

Six

Because a flat black rectangle is boring

Revisit the prelab activity where you listed the methods in Graphics that allow you to draw things on the screen, and look for ways to spiff up our brick. How would you draw a solid brick? A solid brick with a different colored outline from its insides? A brick with... polka dots? Stripes? The Olin "O"? Play around, create a couple different Brick classes that look neat. Add multiple instances of different bricks to the Board, and use them all together in a single Board.

Once you have completed a few brick classes that draw themselves different ways, you have completed the target exercise for this lab. If you have extra time, though, there's always...

Gravy!

There is lots of room for expansion in this lab if you have time... We'd suggest reading through this section for ideas, and picking a couple to work on that interest you.

Other Brick functionality

There are several methods in BasicBrick other than paint that beg to be overridden. Most notably, the behavior that happens when a brick is hit by a ball:
boolean hit(BreakoutComponent)
This method tells the Brick which BreakoutComponent just hit it. It should return true if the Brick died and needs to be removed from the Board, and false if it should stay put. You can experiment with bricks that don't die unless you hit them several times, or maybe even bricks that spawn new bricks -- after a game has started, you can use Board.add(BreakoutComponent, java.awt.Point) to add components to the Board. (auto-layout-add is disabled while in-game)

Polymorphism

The polymorphism features in breakout are terribly entertaining, so you can definitely take a look at that. Remember that since all the members of the board have to implement BreakoutComponent, they all have a set of common methods that you can use regardless of what class they might be from -- that's what polymorphism means, really: using interfaces to give several different types of objects a guaranteed similar behavior, so you can treat them all as a single group. This could definitely be useful, for example, in doing neat things with the hit(BreakoutComponent bc) method.

If you get bored with playing with bricks, or if you're getting annoyed with having to maintain 20 add() statements, you'll probably be interested in how to write a Board file -- like the one we loaded in part Two of the Lab.

Boardfiles

Check out the format of the exampleboard.brd file in the Board directory. It's massively documented, but should you need a hand figuring something out, don't hesitate to ask someone. Try duplicating whatever Board you've got set up in your main method using a boardfile, run it, and load it.

Okay, so you just managed to achieve something you'd already had five minutes ago... great. But now, it's really easy to change up the ball and paddle, and keep track of large numbers of bricks in the Board with fewer keystrokes.

The Rebounding Interface

The tricky thing about moving objects like balls is that they have to figure out what to do when they hit something. This behavior is defined in the Rebounding interface. Luckily, the functionality you need for things that bounce is already covered for you in BasicBall, so you're probably alright just extending that class. If you want to make something that moves, but doesn't need to rebound off of anything, you probably don't need to implement Rebounding -- just put the movement in the update() method.

More Ideas

  • Remember ballworld? Something tells me we can apply this here...
  • Adjustable velocity balls
  • Flying bricks!
  • Paddles with ammunition
  • A round paddle -- so you can aim the ball better
  • etc, etc. Impress your friends.


Post-Lab, AKA What To Turn In

Your completed assignment should include:
  • On the front page, how much out-of-class time (approximately) you've spent on reading, on preparation of the homework, in lab, and on other non-class-time course-related activities (and what). These times should include work from last Wednesday 5pm to this Wednesday at 5pm.
  • Your pre-lab work.
  • A discussion of how you spent your time in lab, including observations on how well your actual activities matched your development plan.
  • A record of your observations, particularly those that surprised you.
  • Your check-out question and its answer.
  • The most unexpected thing you've learned in PI so far.
Questions, comments, gripes and other communication to pi-staff@lists.cognition.olin.edu
This course is a part of Lynn Andrea Stein's Rethinking CS101 project at the Computers and Cognition Laboratory and the Electrical and Computer Engineering area at Franklin W. Olin College of Engineering. Olin College Logo