In: Computer Science
This lab is designed to design an event driven animation
application which JavaFX application implementing a racing car
image. The car moves from left to right. When it hits the right
end, it restarts from the left and continues the same process. Let
the user pause/resume the animation with a button press/release and
increase/decrease the car speed by pressing the up and down arrow
keys.
The car used for this application has the following attributes.
Task(s)
Task 1: Create a JavaFX application called ‘CarRacing’.
Task 2A: Create a class called ‘CarPane’ which extends ‘Pane’,
which defines the attributes of the car. The class will have
multiple instance variables, which will be tied to width and height
of the pane. If the window
private double paneWidth = 200; // Width of the pane
private double paneHeight = 200; // Height of the pane
private double baseX = 0; // Initial X placement of Car
private double baseY = paneHeight; // Initial y placement of
Car
private Circle c1 = new Circle(baseX+15, baseY-5, 5); // Placement
of first wheel
private Circle c2 = new Circle(baseX+35, baseY-5, 5); // Placement
of second wheel
private Rectangle carBody = new Rectangle( baseX, baseY-20, 50,
10); // Car body
private Polygon carTop = new Polygon( baseX+10, baseY-20, // Car
top
baseX+20, baseY-30, baseX+30, baseY-30,
baseX+40, baseY-20);
Task 2B: Define a constructor which sets the color and places
the tires, the body and the top on the pane
public CarPane()
{
carBody.setFill( Color.CYAN );
carTop.setFill( Color.BLUE );
this.getChildren().addAll( c1, c2, carBody, carTop );
}
Task 2C: Define a CarPane method called setValues(), which
clears the previous painting of the car and re-paints the car with
the newest position.
public void setValues()
{
c1.setCenterX( baseX+15 );
c1.setCenterY( baseY-5 );
c2.setCenterX( baseX+35 );
c2.setCenterY( baseY-5 );
carBody.setX( baseX );
carBody.setY( baseY-20 );
carTop.getPoints().clear();
carTop.getPoints().addAll( baseX+10, baseY-20,
baseX+20, baseY-30, baseX+30, baseY-30,
baseX+40, baseY-20);
}
Task 3: Create a new Pane, along with a scene. Run the
application which should paint the pane with the automobile in the
lower left hand position. Try resizing the window, the car should
maintain its position.
// Create a new CarPane
CarPane car = new CarPane();
// Create a scene and place it in the stage
Scene scene = new Scene(car, 200, 200);
primaryStage.setTitle("Racing Car"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
Task 4: Create an animation, which initializes the animation,
sets the timeline cycle and starts the animation.
Timeline animation = new Timeline( new
KeyFrame(Duration.millis(100), e->car.move()));
animation.setCycleCount( Timeline.INDEFINITE );
animation.play(); // Start animation
The animation properties calls a method called move, which must be
defined in your car class. The move method should compare the
current baseX position against the paneWidth. If baseX gets to the
end of the current window, it should reset the baseX position to
the beginning of the window.
public void move()
{
if ( baseX > paneWidth )
{
baseX = -20;
}
else
{
baseX += 1;
}
setValues();
}
Rerun the application, the car will now start moving. If you make
the window wider, the car will still restart after it reaches the
initial pane width.
Task 5: To properly update the paneHeight and paneWidth when the
window is resized, you must provide addListener methods for both
the height and width.
scene.widthProperty().addListener( e->car.setW(
car.getWidth()));
scene.heightProperty().addListener( e->car.setH(
car.getHeight()));
The setW and setH methods need to be defined in the car class,
which will reset the paneWidth and paneHeight.
public void setW( double newWidth )
{
this.paneWidth = newWidth;
setValues();
}
public void setH( double newHeight )
{
this.paneHeight = newHeight;
setValues();
}
Rerun the application. Resize the window, the car will now go to
the end of the window before starting over.
Task 6: To pause the animation, we need to monitor the mouse
press. Add the following commands to the Start method.
car.setOnMousePressed( e->animation.pause());
car.setOnMouseReleased( e->animation.play());
Rerun the application. Press the mouse key, the animation should
stop. Release the mouse key, the animation should resume.
Task 7: Implement a keyboard handler to determine if the user
presses the UP or DOWN keys. If the UP key is pressed, the
animation rate will be increased. If the DOWN key is pressed, the
animation rate will be decreased.
car.requestFocus();
car.setOnKeyPressed(e ->
{
switch( e.getCode())
{
case UP:
animation.setRate( animation.getRate()+1 );
break;
case DOWN:
animation.setRate( animation.getRate()-1 );
break;
}};
Task 8: Implement new key handlers for LEFT and RIGHT keys. The LEFT key should decrease the baseY coordinates of the car so it gets higher on the screen. The RIGHT key should increase the baseY coordinates of the car so it gets lower on the screen. Create a setCarH() parameter that increases or decreases the baseY parameter by 10. Ensure that the car does not go out of bounds of the window.
Task 9: Implement a new key hander for the HOME key. The HOME key will reset the baseX parameter to 0 so the car starts over at the initial position. Create a resetCarW() method in the car class.
This lab is worth 20 points
To receive credit for this module’s lab, you must provide:
⦁ Final RacingCar application source code from Task 9
[RacingCar.java]
Here is the completed code for this problem. Comments are included, go through it, learn how things work and let me know if you have any doubts or if you need anything to change. If you are satisfied with the solution, please rate the answer. Thanks
// RacingCar.java (contains CarPane class as inner class)
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Polygon;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class RacingCar extends Application {
//CarPane class representing a Car object
public class CarPane extends Pane {
private double paneWidth = 200; // Width of the pane
private double paneHeight = 200; // Height of the pane
private double baseX = 0; // Initial X placement of Car
private double baseY = paneHeight; // Initial y placement of Car
private Circle c1 = new Circle(baseX + 15, baseY - 5, 5); // Placement of first wheel
private Circle c2 = new Circle(baseX + 35, baseY - 5, 5); // Placement of second wheel
private Rectangle carBody = new Rectangle(baseX, baseY - 20, 50, 10); // Car body
private Polygon carTop = new Polygon(baseX + 10, baseY - 20, // Car top
baseX + 20, baseY - 30, baseX + 30, baseY - 30,
baseX + 40, baseY - 20);
public CarPane() {
carBody.setFill(Color.CYAN);
carTop.setFill(Color.BLUE);
this.getChildren().addAll(c1, c2, carBody, carTop);
}
public void setValues() {
c1.setCenterX(baseX + 15);
c1.setCenterY(baseY - 5);
c2.setCenterX(baseX + 35);
c2.setCenterY(baseY - 5);
carBody.setX(baseX);
carBody.setY(baseY - 20);
carTop.getPoints().clear();
carTop.getPoints().addAll(baseX + 10, baseY - 20,
baseX + 20, baseY - 30, baseX + 30, baseY - 30,
baseX + 40, baseY - 20);
}
public void move() {
if (baseX > paneWidth) {
//wrapping from other end
baseX = -20;
} else {
//incrementing 5 spaces forward, 1 was a bit too slow
baseX +=5;
}
setValues();
}
public void setW(double newWidth) {
this.paneWidth = newWidth;
setValues();
}
public void setH(double newHeight) {
this.paneHeight = newHeight;
setValues();
}
//method to increase or decrease car's baseY
public void setCarH(double change){
//adding change to baseY
double result=baseY+change;
//if the car is still within range, assigning new value to baseY
//and setting values
if(result>=30 && result<paneHeight){
baseY=result;
setValues();
}
}
//method to reset car baseX to 0
public void resetCarW(){
baseX=0; //you may assign -20 if you prefer that
setValues();
}
}
@Override
public void start(Stage primaryStage) {
// Create a new CarPane
CarPane car = new CarPane();
Scene scene = new Scene(car, 200, 200);
primaryStage.setTitle("Racing Car"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
Timeline animation = new Timeline(new KeyFrame(Duration.millis(100), e -> car.move()));
animation.setCycleCount(Timeline.INDEFINITE);
animation.play(); // Start animation
scene.widthProperty().addListener(e -> car.setW(car.getWidth()));
scene.heightProperty().addListener(e -> car.setH(car.getHeight()));
car.setOnMousePressed(e -> animation.pause());
car.setOnMouseReleased(e -> animation.play());
car.requestFocus();
car.setOnKeyPressed(e -> {
switch (e.getCode()) {
case UP:
animation.setRate(animation.getRate() + 1);
break;
case DOWN:
if (animation.getRate() > 0) {
animation.setRate(animation.getRate() - 1);
}
break;
//key handler for left, right and home keys
case LEFT:
//decreasing height by 10
car.setCarH(-10);
break;
case RIGHT:
//increasing height by 10
car.setCarH(10);
break;
case HOME:
//resetting car position from left end
car.resetCarW();
break;
}
});
}
public static void main(String[] args) {
launch(args);
}
}
/*OUTPUT*/