So I'm playing around with java GUI. I'm doing a program that bounces a ball when it hits the end of the window. How do I go about updating the secondBall
to update and show its new location? secondBall.action();
does not work. Refer to THIS DOES NOT WORK in the below code.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// Ball. This class describes the concept of ball. A ball has a position
// (given by a coordinate (x,y)), a velocity (given by a
// differential (dx,dy)) and a color.
class Ball {
// Default constants (for all instances of Ball)
static int defaultDiameter = 10;
static Color defaultColor = Color.yellow;
static Rectangle defaultBox = new Rectangle(0,0,100,100);
// Position
private int x, y;
// Speed and direction
private int dx, dy;
// Diameter (size)
private int diameter;
// Color
private Color color;
// Bounding rectangular area within which the ball bounces
private Rectangle box;
// Construction of new balls requires position and direction
public Ball( int x0, int y0, int dx0, int dy0 ) {
x = x0;
y = y0;
dx = dx0;
dy = dy0;
color = defaultColor;
diameter = defaultDiameter;
}
// Set new color
public void setColor( Color c ) {
color = c;
}
// Set new bounding rectangular area
public void setBoundingBox( Rectangle r ) {
box = r;
}
// Set new diameter
public void setDiameter(int newDiameter) {
diameter = newDiameter;
}
// Draw a ball on given graphic area
public void paint( Graphics g ) {
// Change to the color of the ball
g.setColor( color.WHITE );
// The ball is represented as a filled circle, i.e. an ellipse (oval)
// with equal height and width
g.fillOval( x, y, diameter, diameter );
}
// Constrain the ball within the rectangular area. Update the speed
// if necessary.
void constrain() {
// Give absolute coordinates of the rectangular area
int x0 = box.x;
int y0 = box.y;
int x1 = x0 box.width - diameter;
int y1 = y0 box.height - diameter;
// Change speed and direction if the ball is outside of it
// rectangular area
if (x < x0) {
dx = Math.abs(dx);
}
if (x > x1) {
dx = -Math.abs(dx);
}
if (y < y0) {
dy = Math.abs(dy);
}
if (y > y1) {
dy = -Math.abs(dy);
}
}
// Move the ball with the current direction and speed one step
public void action() {
x = x dx;
y = y dy;
constrain();
}
}
// The BallPanel class defines a drawing surface where the balls are drawn. The
// inherits the JPanel class and implements ActionListener. By
// implement ActionListener you can let a Timer with even
// spaces give a 'tick' when updating the panel should be done.
class BallPanel extends JPanel implements ActionListener {
// Width and height
private int width, height;
// A ball
private Ball ball;
private Ball secondBall;
// Timer. Sends a signal every 50 milliseconds to the panel which
// sent along as ActionListener.
private Timer timer = new Timer(50, this);
// Initialize the attributes
public BallPanel(int width, int height) {
// Find out the width and height of the artboard
this.width = width;
this.height = height;
// Create a new ball
ball = new Ball( width / 10, height / 5, 5, 5 );
secondBall = new Ball( width / 10, height / 5, 5, 5 );
// Set the ball's rectangular bounding box
ball.setBoundingBox( new Rectangle( 0, 0, width, height ) );
// Start the timer.
timer.start();
}
// Update (called on repaint, repaint())
public void paintComponent( Graphics g ) {
// Clear the entire drawing surface (with black color)
g.setColor( Color.black );
g.fillRect( 0, 0, getWidth(), getHeight() );
// Draw the ball (on black background)
ball.paint(g);
ball.setDiameter(15);
secondBall.paint(g);
secondBall.setDiameter(10);
}
// When we get a signal from the timer...
public void actionPerformed(ActionEvent e) {
if(width != getWidth() || height != getHeight())
wasResized();
ball.action();// Do whatever is relevant with the ball
// secondBall.action(); THIS DOES NOT WORK
repaint(); // Automatically make another call
// paintComponent()
}
// Called if window resizes
public void wasResized( ) {
//width = newWidth;
// height = newHeight;
ball.setBoundingBox(new Rectangle(0, 0, getWidth(), getHeight()));
}
}
// This class defines the window created by the program. One
// window (JFrame) is created where an instance of BallPanel (the artboard)
// placed.
public class BallWorld extends JFrame {
// Create a panel
private BallPanel panel = new BallPanel (180, 180);
public BallWorld() {
// Add the ball panel to the center of the frame.
Container c = getContentPane();
c.add(panel, BorderLayout.CENTER);
setSize(200, 200); // Frame size.
setLocation(100, 100); // So that the frame does not end up in a corner.
setVisible(true); // Make the frame visible.
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
// This method is started by the Java virtual machine when java is called
// BallWorld
public static void main(String args[]) {
// This command provides better animation in a part
// Olympics. Comment if the ball moves jerkily.
// System.setProperty("sun.java2d.opengl", "true");
BallWorld world = new BallWorld();
}
}
When trying to animate the secondBall
object I just run in to errors.
I tried adding secondBall.action();
right under ball.action();
in the actionPerformed
method.
CodePudding user response:
When trying to animate the
secondBall
object I just run in to errors.
I tried adding
secondBall.action();
right underball.action();
in theactionPerformed
method.
You should post the error details in your question.
When I ran your code (including the secondBall.action()
line) I got:
Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException: Cannot read field "x" because "this.box" is null
at swingprj/swngtsts.Ball.constrain(BallWorld.java:64)
at swingprj/swngtsts.Ball.action(BallWorld.java:88)
at swingprj/swngtsts.BallPanel.actionPerformed(BallWorld.java:139)
at java.desktop/javax.swing.Timer.fireActionPerformed(Timer.java:311)
at java.desktop/javax.swing.Timer$DoPostEvent.run(Timer.java:243)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:771)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:722)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:716)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:741)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
The first line in method constrain()
(of class Ball
) is throwing the exception, i.e. this line:
int x0 = box.x;
That's because box
is null and it is null because you didn't call method setBoundingBox
for secondBall
in the constructor of class BallPanel
.
So in order to fix your NullPointerException
, just add this line to BallPanel
class constructor:
secondBall.setBoundingBox();
Here is your code with the added line (indicated by the comment: ADDED THIS LINE)
package swngtsts;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
// Ball. This class describes the concept of ball. A ball has a position
// (given by a coordinate (x,y)), a velocity (given by a
// differential (dx,dy)) and a color.
class Ball {
// Default constants (for all instances of Ball)
static int defaultDiameter = 10;
static Color defaultColor = Color.yellow;
static Rectangle defaultBox = new Rectangle(0, 0, 100, 100);
// Position
private int x, y;
// Speed and direction
private int dx, dy;
// Diameter (size)
private int diameter;
// Color
private Color color;
// Bounding rectangular area within which the ball bounces
private Rectangle box;
// Construction of new balls requires position and direction
public Ball(int x0, int y0, int dx0, int dy0) {
x = x0;
y = y0;
dx = dx0;
dy = dy0;
color = defaultColor;
diameter = defaultDiameter;
}
// Set new color
public void setColor(Color c) {
color = c;
}
// Set new bounding rectangular area
public void setBoundingBox(Rectangle r) {
box = r;
}
// Set new diameter
public void setDiameter(int newDiameter) {
diameter = newDiameter;
}
// Draw a ball on given graphic area
public void paint(Graphics g) {
// Change to the color of the ball
g.setColor(color.WHITE);
// The ball is represented as a filled circle, i.e. an ellipse (oval)
// with equal height and width
g.fillOval(x, y, diameter, diameter);
}
// Constrain the ball within the rectangular area. Update the speed
// if necessary.
void constrain() {
// Give absolute coordinates of the rectangular area
int x0 = box.x;
int y0 = box.y;
int x1 = x0 box.width - diameter;
int y1 = y0 box.height - diameter;
// Change speed and direction if the ball is outside of it
// rectangular area
if (x < x0) {
dx = Math.abs(dx);
}
if (x > x1) {
dx = -Math.abs(dx);
}
if (y < y0) {
dy = Math.abs(dy);
}
if (y > y1) {
dy = -Math.abs(dy);
}
}
// Move the ball with the current direction and speed one step
public void action() {
x = x dx;
y = y dy;
constrain();
}
}
// The BallPanel class defines a drawing surface where the balls are drawn. The
// inherits the JPanel class and implements ActionListener. By
// implement ActionListener you can let a Timer with even
// spaces give a 'tick' when updating the panel should be done.
class BallPanel extends JPanel implements ActionListener {
// Width and height
private int width, height;
// A ball
private Ball ball;
private Ball secondBall;
// Timer. Sends a signal every 50 milliseconds to the panel which
// sent along as ActionListener.
private Timer timer = new Timer(50, this);
// Initialize the attributes
public BallPanel(int width, int height) {
// Find out the width and height of the artboard
this.width = width;
this.height = height;
// Create a new ball
ball = new Ball(width / 10, height / 5, 5, 5);
secondBall = new Ball(width / 10, height / 5, 5, 5);
// Set the ball's rectangular bounding box
ball.setBoundingBox(new Rectangle(0, 0, width, height));
secondBall.setBoundingBox(new Rectangle(0, 0, width, height)); // ADDED THIS LINE
// Start the timer.
timer.start();
}
// Update (called on repaint, repaint())
public void paintComponent(Graphics g) {
// Clear the entire drawing surface (with black color)
g.setColor(Color.black);
g.fillRect(0, 0, getWidth(), getHeight());
// Draw the ball (on black background)
ball.paint(g);
ball.setDiameter(15);
secondBall.paint(g);
secondBall.setDiameter(10);
}
// When we get a signal from the timer...
public void actionPerformed(ActionEvent e) {
if (width != getWidth() || height != getHeight())
wasResized();
ball.action();// Do whatever is relevant with the ball
secondBall.action(); //THIS DOES NOT WORK
repaint(); // Automatically make another call
// paintComponent()
}
// Called if window resizes
public void wasResized() {
// width = newWidth;
// height = newHeight;
ball.setBoundingBox(new Rectangle(0, 0, getWidth(), getHeight()));
}
}
// This class defines the window created by the program. One
// window (JFrame) is created where an instance of BallPanel (the artboard)
// placed.
public class BallWorld extends JFrame {
// Create a panel
private BallPanel panel = new BallPanel(180, 180);
public BallWorld() {
// Add the ball panel to the center of the frame.
Container c = getContentPane();
c.add(panel, BorderLayout.CENTER);
setSize(200, 200); // Frame size.
setLocation(100, 100); // So that the frame does not end up in a corner.
setVisible(true); // Make the frame visible.
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
// This method is started by the Java virtual machine when java is called
// BallWorld
public static void main(String args[]) {
// This command provides better animation in a part
// Olympics. Comment if the ball moves jerkily.
// System.setProperty("sun.java2d.opengl", "true");
BallWorld world = new BallWorld();
}
}
Refer to What is a NullPointerException, and how do I fix it?
CodePudding user response:
...nice, and do not forget to modify bounding area for second ball, too.
public void wasResized() {
// width = newWidth;
// height = newHeight;
ball.setBoundingBox(new Rectangle(0, 0, getWidth(), getHeight()));
secondBall.setBoundingBox(new Rectangle(0, 0, getWidth(), getHeight()));
//Adding new bounding to secondball