This program draws a train sitting on tracks when the program starts. When the user tries to drag the train, it crashes with a NullPointerException. The problem is that the car is declared twice, once as an instance variable and once as a variable local to the constructor. The constructor sets the local variable. When the contains method is called, it tries to use the instance variable, but that variable has not been set.
Please refer to the MovingTrain example from the previous class for the correct way to do this.
import java.awt.*;
import objectdraw.*;
/**
Demonstrates the variety of graphical constructs available in objectdraw
by drawing a picture using the shapes.
@author Barbara Lerner
*/
public class MovingTrain extends WindowController
{
// The train in the drawing
private Train train;
// The background
private FilledRect sky;
private FilledRect grass;
// Remember which object is being dragged, if either
private boolean trainGrabbed;
// Location where last drag started
private Location lastPoint;
// Colors for the background
private static final Color GRASS_COLOR = Color.GREEN;
private static final Color DARK_GRASS = GRASS_COLOR.darker();
private static final Color SKY_COLOR = Color.BLUE;
private static final Color DARK_SKY = SKY_COLOR.darker();
// Size of the window
private static final int WINDOW_WIDTH = 600;
private static final int WINDOW_HEIGHT = 400;
// Location of grass
private static final int GRASS_TOP = WINDOW_HEIGHT * 5 / 8;
// Train location
private static final int TRAIN_LEFT = 50;
private static final int TRACK_HEIGHT = WINDOW_HEIGHT * 7 / 8;
/**
* Draws a train of a fixed size at a fixed location when the program starts.
*/
public void begin () {
// Set the size of the window that we want to have. Note that the height includes
// the height of the menu bar above the drawing area. Therefore the drawing area will
// be slightly smaller than what we specify.
resize (WINDOW_WIDTH, WINDOW_HEIGHT);
// grass
grass = new FilledRect (0, GRASS_TOP, getWidth(), getHeight() - GRASS_TOP, canvas);
grass.setColor (GRASS_COLOR);
// sky
sky = new FilledRect (0, 0, getWidth(), GRASS_TOP, canvas);
sky.setColor (SKY_COLOR);
// The complex objects in the scene
train = new Train (TRAIN_LEFT, TRACK_HEIGHT, canvas);
// This is the train track that the car sits on. The parameters for a line are:
// x coordinate of first end point
// y coordinate of first end point
// x coordinate of second end point
// y coordinate of second end point
// canvas
// In this case, we need to be careful to pick coordinates that will draw
// a horizontal line directly beneath the train's wheels. We also want to be
// sure the line stretches the entire width of the window.
new Line (0, TRACK_HEIGHT, getWidth(), TRACK_HEIGHT, canvas);
}
/**
* On each mouse click produce a puff of smoke.
* @param point where the user clicked.
*/
public void onMouseClick (Location point) {
train.produceSmoke();
}
/**
* Begin a drag if the user presses the mouse button over the train or cloud
* @param point where the user pressed the mouse button down
*/
public void onMousePress (Location point) {
// Prepare to drag the train
if (train.contains (point)) {
trainGrabbed = true;
lastPoint = point;
}
}
/**
* Drag the train or cloud if either were selected
* @param point where the mouse is at the end of the drag
*/
public void onMouseDrag (Location point) {
// Drag the train
if (trainGrabbed) {
// The train only moves horizontally, so ignore how far the mouse moved vertically
double dx = point.getX() - lastPoint.getX();
train.move (dx);
lastPoint = point;
}
}
/**
* End the drag
* @param point where the mouse is when the button is released
*/
public void onMouseRelease (Location point) {
trainGrabbed = false;
}
}
import java.awt.*;
import objectdraw.*;
/**
A train consisting of multiple shapes. The train can move and change
color.
@author Barbara Lerner
*/
public class Train
{
// Locations and sizes of parts of the train
private static final int CAR_HEIGHT = 100;
private static final int CAR_WIDTH = 300;
private static final int WHEEL_SIZE = CAR_HEIGHT / 2;
private static final int SMOKESTACK_HEIGHT = CAR_HEIGHT / 2;
private static final int SMOKESTACK_WIDTH = CAR_WIDTH / 6;
private static final int SMOKE_HEIGHT = SMOKESTACK_HEIGHT;
private static final int SMOKE_WIDTH = SMOKESTACK_WIDTH / 2;
// Distance train moves as on each step
private static final int MOVE_AMOUNT = CAR_WIDTH / 10;
// Colors of the train parts
private static final Color CAR_COLOR = Color.RED;
private static final Color DARK_CAR = CAR_COLOR.darker();
private static final Color WHEEL_COLOR = Color.BLACK;
private static final Color DARK_WHEEL = WHEEL_COLOR.darker();
private static final Color SMOKESTACK_COLOR = Color.DARK_GRAY;
private static final Color DARK_SMOKESTACK = SMOKESTACK_COLOR.darker();
private static final Color SMOKE_COLOR = new Color (230, 230, 230);
private static final Color DARK_SMOKE = SMOKE_COLOR.darker();
// The parts of the train
private FilledRect car;
private FilledOval backWheel;
private FilledOval frontWheel;
private FilledRect smokestack;
// Portion of screen to draw in
private DrawingCanvas trainCanvas;
/**
* Draws a train.
* @param left the back of the train car
* @param trackHeight height of the train track the car sits on
* @param canvas where to draw
*/
public Train (double left, double trackHeight, DrawingCanvas canvas) {
FilledRect car = new FilledRect(left, trackHeight - CAR_HEIGHT - WHEEL_SIZE, CAR_WIDTH, CAR_HEIGHT, canvas);
car.setColor(CAR_COLOR);
backWheel = new FilledOval (left + WHEEL_SIZE, trackHeight - WHEEL_SIZE, WHEEL_SIZE, WHEEL_SIZE, canvas);
backWheel.setColor (WHEEL_COLOR);
frontWheel = new FilledOval (left + CAR_WIDTH - 2 * WHEEL_SIZE, trackHeight - WHEEL_SIZE, WHEEL_SIZE, WHEEL_SIZE, canvas);
frontWheel.setColor (WHEEL_COLOR);
smokestack = new FilledRect (left + CAR_WIDTH - 2 * SMOKESTACK_WIDTH, car.getY() - SMOKESTACK_HEIGHT, SMOKESTACK_WIDTH, SMOKESTACK_HEIGHT, canvas);
smokestack.setColor (SMOKESTACK_COLOR);
trainCanvas = canvas;
}
/**
* @return true if point is on the train. The smoke does not count as part of the train.
* @param point the location to check for containment
*/
public boolean contains (Location point) {
if (car.contains (point)) {
return true;
}
else if (backWheel.contains (point)) {
return true;
}
else if (frontWheel.contains (point)) {
return true;
}
else if (smokestack.contains (point)) {
return true;
}
// The point is not in any of the parts of the train
return false;
}
/**
* Move the train relative to its current location
* @param dx the distance to move right
* @param dy the distance to move down
*/
public void move (double dx) {
car.move (dx, 0);
backWheel.move (dx, 0);
frontWheel.move (dx, 0);
smokestack.move (dx, 0);
}
public FilledOval produceSmoke () {
FilledOval smoke = new FilledOval (smokestack.getX() + (SMOKESTACK_WIDTH - SMOKE_WIDTH) / 2,
smokestack.getY() - 1.5 * SMOKE_HEIGHT, SMOKE_WIDTH, SMOKE_HEIGHT, trainCanvas);
smoke.setColor(SMOKE_COLOR);
return smoke;
}
}