Breakout


Revised July 2003

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, unless you wish to deal with it in more detail.

Besides the code and javadoc linked from the problem sets page, Breakout has the following resources:

Intro/Prelab

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. [img: screenshot of breakout]

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:
Okay, so on to the meatier bits of how Breakout works.

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.

To further prepare for the lab, you should familiarize yourself with the BreakoutUI, Board, and BoardPanel classes, and how they interact.

Questions

BreakoutComponent's paint(Graphics g) method:
  1. What methods are available to you in Graphics that let you draw things on the screen?
BreakoutComponent's hit(bc) method:
  1. What kind of information can you get about the BreakoutComponent that just hit your Brick?
    • What are some ways you could use this information to change the behavior or appearance of your Brick after being hit? (To more fully answer this question, you might be interested in checking out the documentation for the java classes java.awt.Dimension and java.awt.Point.)
  2. What information does the ball receive from your Brick by calling its hit method?
  3. (Bonus) The Wall interface adds a method that tells whether things should die when they hit it -- killing() -- that lets a particular Wall act as the floor. A ball dies when it is hit by a Wall whose killing() method returns true. How might you create a Brick that also kills balls when hit?
Setting up Breakout:
  1. How do you create a BreakoutUI? A Board?
  2. How do you add a Brick to a Board?
  3. What else do you have to do to get a Board to show up in the Breakout window?

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.
  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 using the no-args constructor.
  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 from the boards directory and play some breakout... you know you want to.

Three

Building a Brick

Everything that you see on the breakout Board implements an interface called BreakoutComponent. 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, do 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.


For now, let's get our Brick into a board.

Four

Adding our Brick to a Board

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 look at the javadoc for Board. Notice that it can either use a no-args constructor, or take a BoardPanel object as a constructor argument. A 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. Lucky for us, if you peek at the javadoc for BreakoutUI, a way to get ahold of the right BoardPanel object is provided. In your main method, create a new Board object using the BoardPanel you get from BreakoutUI.getBoardPanel().
  2. Now we can use one of Board's add methods to add a new instance of our Brick to the Board. There are two choices; 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. augh! our Brick isn't there! Well, that's because we didn't load our Board into the UI. We created a Board. but we didn't do a key part of the java that happens when you click the "Load" button. If you take a look at the javadoc for BreakoutUI(again...) you may notice a conspicuous-looking method called load. Use this method to load the Board into the BreakoutUI we're using.
  4. Now try running breakout. Your Brick should appear in the upper left corner of the Board. 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

Go look at the javadoc for java.awt.Graphics, and see what kinds of things you can draw with it. How would you draw a solid brick? A solid brick with a different colored outline? 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.

You can also override other BasicBrick methods, if you like. 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. 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, Point) to add components to the Board. (auto-layout-add is disabled while in-game)

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!

The polymorphism features in breakout are terribly entertaining, so you can definitely start there. 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 though, 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 at the beginning.

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.

Some Ideas(not that you creative people really need any)