Pinball Game (Versions 1 and 2)

advertisement
Session 12
Introduction to PinBallGame
(Chaper 7)
Pinball Game Construction Kit
Version 1
Some cool new features
• Replaced the fire button with a mouse
event.
• Multiple balls can be in the air at once.
• Control is no longer in the paint method.
• But before we talk about what Budd did
correctly, well…
Say It Once and Only Once!
In the interest of simplifying the number of files we need to run a
program, Budd has repeated code that can be reused by inheritance
(figure 7.3, p 102).
Gravity affects a PinBall in the same way that it affects a CannonBall.
The only differences between a PinBall and a CannonBall are its
size and its initial trajectory.
So, we can say:
public class PinBall extends CannonBall {
public PinBall( int sx, int sy ) {
super( sx, sy, 10,-2 + Math.random()/4, -15 );
}
}
Inheritance makes our job easier—as well as isolating repeated code.
Looks the same, but it’s not.
• Replaced the fire button with a mouse
event.
• Multiple balls can be in the air at once.
• Control is no longer in the paint method.
Handling Mouse Events
The CannonWorld handles only events that are
generated by the active components — a button
and a slider — that we added to it.
More generally, though, we will want to trap and
respond to any mouse action, anywhere within
the frame.
Any listener that wishes to monitor mouse activity
must implement the MouseListener interface:
public interface MouseListener {
public void mouseClicked ( MouseEvent e );
public void mouseEntered ( MouseEvent e );
public void mouseExited ( MouseEvent e );
public void mousePressed ( MouseEvent e );
public void mouseReleased( MouseEvent e );
}
The MouseListener interface is part of the package
java.awt.event. It specifies what an object must
do to be a mouse listener within the Java Event
Model.
How the MouseListener works.
Think back to how we implemented Button listeners:
private class FireButtonListener implements ActionListener {
We can do a similar thing for MouseListeners…
private class PinballGameListener implements MouseListener{
However, to implement an interface, the Java language
insists that the programmer provide a definition for all
operations.
But what if I don’t want all 5 mouse events??
MouseAdapter to the rescue
• To simplify this process, the Java library
provides a simple class named Mouse
Adapter. The class MouseAdapter
implements the MouseListener interface,
but uses an empty method for each
method.
• Rather than implement a MouseListener,
we may choose to implement a
MouseAdapter. (Let’s go back to the code
(also p 104))
Mouse Events in the Pin Ball
Game
In the PinBallGame class, we have the following class relationship:
MouseListener
implements
MouseAdapter
extends
MouseKeeper
• What does a MouseKeeper do?
• What does a MouseKeeper do?
– If the mouse is pressed in the“shooting area”,
then it creates and launches a new pinball.
• What does a MouseAdapter do?
• What does a MouseAdapter do?
– Nothing, in response to any sort of mouse
event.
• Why do you suppose that Java’s creators
call it an “adapter”?
• Why do you suppose that Java’s creators
call it an “adapter”?
– They use a common design from the world as
an analogy.
• Why do you suppose Java’s creators
bothered to define the MouseListener
interface? Why not have everyone extend
MouseAdapter?
• Why do you suppose Java’s creators
bothered to define the MouseListener
interface? Why not have everyone extend
MouseAdapter?
– Inheritance is not free. Why force
programmers who plan to implement most or
all of the interface to pay the extra price?
Another adaptor you might find
handy
In constructor
addWindowListener(new Closer());
As inner class
private class Closer extends WindowAdapter {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
Pinball Version 1
• Replaced the fire button with a mouse
event.
• Multiple balls can be in the air at once.
– Uses a Vector to contain many balls
• Control is no longer in the paint method.
Keeping track of multiple objects.
• In the past we have kept track of multiple
items through an array, e.g., in Multi-ball
world for chapter 5.
•
What are the limitations of using an
array?
Keeping track of multiple objects.
• In the past we have kept track of multiple
items through an array, e.g., in Multi-ball
world for chapter 5.
•
What are the limitations of using an
array?
– You have to state the number of elements at
construction time.
Keeping track of multiple objects.
• In the past we have kept track of multiple items
through an array, e.g., in Multi-ball world for
chapter 5.
•
What are the limitations of using an array?
– You have to state the number of elements at
construction time.
• If we don’t know the number in advance we can
use the concept of a Vector. (Let’s go back to
the code (also pp 102-103))
Using Vector
•
•
•
•
import java.util.Vector;
private Vector balls;
// holds only objects
balls = new Vector( ); // create a Vector
Using the Vector:
balls.addElement(newBall); // add element
balls.size( );
// return # elements
// Retrieving an element requires a cast
PinBall aBall = (PinBall) balls.elementAt (i);
Exercise
• Modify version 1 of the PinBallGame to
allow users only six shots.
• Add a Label at the top of the Frame that
displays the number of shots that have
been fired so far.
• If the user tries to fire again after six shots,
the label should display a “Sorry...”
message.
From javadoc for the Label class:
Constructor Summary
Label()
Constructs an empty label.
Label(String text)
Constructs a new label with the specified string of text, left
justified.
Method Summary
String getText()
Gets the text of this label.
void setText(String text)
Sets the text for this label to the specified text.
A Possible Solution
public class PinBallGame extends Frame { ...
private int numberOfShotsTaken;
private Label numberOfShotsLabel;
public PinBallGame () { ... // all the original code plus:
numberOfShotsTaken = 0;
numberOfShotsLabel = new Label( "Number Of Shots Taken: 0" );
add( "North", numberOfShotsLabel );
}
...
private class MouseKeeper extends MouseAdapter {
public void mousePressed (MouseEvent e) { ...
if ( (x > FrameWidth-40) ... {
if ( numberOfShotsTaken < 6 ) {
// make ball and thread, then:
numberOfShotsTaken++;
numberOfShotsLabel.setText("Number Of Shots Taken: " +
numberOfShotsTaken );
} else
numberOfShotsLabel.setText( "Sorry, but you have already [...]!" );
}
}
}
Threads of Execution
What? How? Why?
The Thread class provides methods to start, run,
sleep, and stop an independent path of
computation, among other things. The start()
method manages the overhead of threads for
us; we can simply watch them go! (This is similar
to the benefits we get from using Frames...)
The pinball game separates these responsibilities
into different objects:
• painting the frame
• controlling the movement/location of balls
Threads of Execution
This separation simplifies all of the methods
involved, for writing, reading, and modifying. The
cost is an increase in the number of methods
needed.
You may also suffer some separation anxiety. To
programmers used to writing the omniscient,
omnipotent main(), decentralizing control can
cause a sense of fragmentation. This will largely
go away as you gain OO and Java experience.
PinBall Version 2
• Adds targets for the
PinBalls to bounce
off of and score on
• Types of targets:
– Spring
– Wall
– Hole
– ScorePad
• What do all targets
have in common?
PinBallTarget Interface
interface PinBallTarget {
public boolean intersects (Ball aBall);
public void moveTo (int x, int y);
public void paint (Graphics g);
public void hitBy (Ball aBall);
}
Why use an interface?
– we want to process targets uniformly, e.g., check if a ball hit it
– the interface makes them the same “type” for storage in a Vector
Hole target
• structurally similar to a ball
– round like a ball
– has a location on the frame like a ball
• behavioral
– it must adhere to the interface
class Hole extends Ball implements PinBallTarget
Inherits moveTo and paint, but supplies
intersects and hitBy
More on Threads
• We can think of separate threads as separate
programs running concurrently.
• They don’t literally run at the same time (unless
you have a machine with more than one CPU).
Instead, one thread gets the CPU for a while,
then it gets put on hold while another thread
gets the CPU, and so on.
• When separate threads are running, sometimes
we need to worry about two threads taking
actions that conflict with one another. We can
use the keyword synchronized to have the JVM
help maintain order.
A Problem Caused by
Separate Threads of Control
More on Threads
• Example: The second version of the pin ball game keeps
track of the score the user earns for hitting targets in the
field of play. It keeps track of the score in an instance
variable named score:
private int score = 0;
• When a pin ball strikes a target, the target tells the pin
ball game to add its point total to the instance variable by
sending an addScore message:
public void addScore( int value ) {
score = score + value;
scoreLabel.setText( "score = " + score );
}
A Problem Caused by
Separate Threads of Control
The solution
synchronized public void
addScore( int value ) {
score = score + value;
scoreLabel.setText( "score = "
+ score );
}
The keyword synchronized is used to ask
Java to guarantee that only one thread at a time
can be executing the addScore() method.
PinBall Contruction Kit
Version 3
Understanding the PinBallGame Kit
• How is the “black box” in the PinBallGame drawn?
• What is the difference between the items outside the box
of the game window and the items inside the box?
• What messages can we send to a Peg, and where is
each behavior defined?
• What method is used to determine the number of
elements held in a Vector? What method is used to
access the values? What method is used to insert a new
value into the collection?
• What is the purpose of the PinBallTarget interface?
• Why can’t we do without it?
• Why is the PinBallGame instance stored in a class
variable instead of an instance variable? (See class
PinBallGame for the declaration, but study the code in
class ScorePad to find the reason.)
Download