Home > Software engineering >  How to animate two separate balls Java AWT & Swing
How to animate two separate balls Java AWT & Swing

Time:01-18

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 under ball.action(); in the actionPerformed 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
  • Related