0

I am trying to build a bounce game in Java. My project has three classes ie the Main class which creates a new window(frame) where the game buttons and bounce objects are drawn. The GameInterface class which represents the properties of the frame being drawn and the RightPanel class which I created so that I could override the paint(Graphics) method to draw my bounce object. So far this is what I have managed to draw with the code. enter image description here You can see that I have two JPanels, one that holds my buttons and the other one that accepts the drawing of a round ball on it ie RightPanel I need help with the Button Event listeners to move the ball up and down and when user holds the button down, it needs to keep moving down until reaches the down order, sam for the up button. The code am using is provided below.

GameInterface class

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;

public class GameInterface extends JFrame {
    //we need this panel declaration in the class level for reference from other methods
    RightPanel rightpanel;
    //define the physical properties of the window
    public GameInterface(){
        setSize(new Dimension(600, 600));
        setResizable(false);
        setTitle("Bounce Game");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBackground(Color.black);
        //define a new JSplitPane and use it to add two JPanels
        JPanel leftpanel= new JPanel();
        //add buttons to the left panel programatically
        JButton up= new JButton("Move up");
        //set the event listeners for the buttons
        up.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //move my ball up
                //clear and redraw the ball while in a new position, use a timer or 
                 something

            }
        });
        JButton down = new JButton("Move down");
        down.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //move my ball down
                // rightpanel.getGraphics.fillColor(Color.RED);
                
            }
        });
        leftpanel.add(up);
        leftpanel.add(down);
        //add a new RightPanel with a drawn red object
        rightpanel= new RightPanel();
        JSplitPane splitpane= new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,leftpanel,rightpanel);
        this.add(splitpane);
        setVisible(true);
    }
}

RightPanel class

import javax.swing.*;
import java.awt.*;

public class RightPanel extends JPanel {
    //define the position where the circle will be drawn
    private int positionX=150;
    private int positionY=150;
    //I had an idea where we need a timer and then on delay we
    //decrement positionX by 3 for move down but can't figure out how to clear RightPanel
    private int radius=100;//as the shape is a circle
    //override the paint method to draw the bounce ball on the second panel

    @Override
    public void paint(Graphics g) {
        g.setColor(Color.RED);
        g.fillOval(positionX,positionY,radius,radius);
    }
}

Main class

public class Main
{
    public static void main(String args[]){
        new GameInterface();
    }
}

How do I add logic to my code to make it move the circle up an down, Thank You. I tried using a timer object to clear the panel and then redraw the ball in the new position of the ball but it draws a vertical bar, not clearing the original ball drawn.

TechGeek
  • 316
  • 1
  • 11
  • 2
    1. Never call `getGraphics()` on a component. 2. Override `paintComponent` not `paint` 3. Call the `super.paintComponent(g)` in your override. 4. Give the RightPanel class setter methods that allow you to change the positionX and positionY locations for drawing, 5. In the button listener, call an appropriate setter method, and then call `repaint()` on on the RightPanel instance after changing the positions. – Hovercraft Full Of Eels May 21 '22 at 18:38
  • Why is everyone saying that overriding paint should be avoided, is there a reason as to why? – TechGeek May 21 '22 at 18:51
  • Search on "Java Swing paint vs paintComponent". You'll find much information on double-buffering, which paintComponent does and paint does not, and on the responsibilities of both methods, the paint being greater, and so overriding it is far more dangerous, especially when you don't call the related super method (an error in your code, for example). – Hovercraft Full Of Eels May 21 '22 at 18:53
  • 1
    Refer to [Performing Custom Painting](https://docs.oracle.com/javase/tutorial/uiswing/painting/index.html) and [How to Use Swing Timers](https://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html) – Abra May 21 '22 at 18:53
  • Also [this link](https://stackoverflow.com/q/9389187/522444), and [this one](https://stackoverflow.com/questions/15103553/difference-between-paint-and-paintcomponent), and [these links](https://www.google.com/search?q=site%3Astackoverflow.com+java+swing+paint+vs+paintcomponent) – Hovercraft Full Of Eels May 21 '22 at 18:55
  • We got a problem, when I use the repaint method of the rightpanel object then the form draws the circle and deforms the entire form, creates two other buttons and panel but when I do `righpanel.paint(rightpanel.getGraphics())` it displaces the ball but the original ball is still there – TechGeek May 21 '22 at 19:11
  • You're still not calling the super's painting method, are you? Or not calling the correct one: please show your code. If you override paintComponent, call `super.paintComponent(g);` first line. – Hovercraft Full Of Eels May 21 '22 at 19:15
  • I implemented onPaint, it worked – TechGeek May 21 '22 at 19:16
  • But is there a way to detect when the ball has touched the lower border of the JFrame? – TechGeek May 21 '22 at 19:19
  • That is a separate problem, but your code is in control and is moving the ball, so check its position when you do this. You can get the JPanel's width and height via appropriate JPanel methods, and you know the ball's position radius (actually it should be called "diameter" because that is what the variable truly represents) – Hovercraft Full Of Eels May 21 '22 at 19:22

1 Answers1

3
  1. Never call getGraphics() on a component.
  2. Override paintComponent not paint
  3. Call the super.paintComponent(g) in your override.
  4. Give the RightPanel class setter methods that allow you to change the positionX and positionY locations for drawing,
  5. In the button listener, call an appropriate setter method, and then call repaint() on on the RightPanel instance after changing the positions.

For example:

The key code below is here in the ActionListener where you update the position values and call repaint:

moveRightBtn.addActionListener(e -> {
    // get and update the x position
    int x = drawOval.getPositionX();
    x += DELTA;

    // call the setter method
    drawOval.setPositionX(x);

    // request that Java repaint the JPanel
    drawOval.repaint();
});

and in the drawing JPanel's paintComponent method where you call the super's method and draw the oval:

@Override
protected void paintComponent(Graphics g) {
    // this is needed to do house-keeping painting, to clear "dirty" pixels
    super.paintComponent(g); 
    
    // this is needed to draw smooth graphics
    Graphics2D g2 = (Graphics2D) g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    
    g2.setColor(OVAL_COLOR);
    g2.fillOval(positionX, positionY, RADIUS, RADIUS);
}
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.*;

@SuppressWarnings("serial")
public class MoveCircle extends JPanel {
    private static final int DELTA = 5;
    private DrawOval drawOval = new DrawOval();

    public MoveCircle() {
        JButton moveRightBtn = new JButton("Move Right");
        moveRightBtn.addActionListener(e -> {
            // get and update the x position
            int x = drawOval.getPositionX();
            x += DELTA;

            // call the setter method
            drawOval.setPositionX(x);

            // request that Java repaint the JPanel
            drawOval.repaint();
        });
        JPanel buttonPanel = new JPanel();
        buttonPanel.add(moveRightBtn);

        setLayout(new BorderLayout());
        add(drawOval);
        add(buttonPanel, BorderLayout.LINE_START);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            MoveCircle mainPanel = new MoveCircle();

            JFrame frame = new JFrame("GUI");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.add(mainPanel);
            frame.pack();
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
        });
    }

}
@SuppressWarnings("serial")
class DrawOval extends JPanel {
    private static final int RADIUS = 100;
    private static final int PANEL_WIDTH = 600;
    private static final int PANEL_HEIGHT = 450;
    private static final Color OVAL_COLOR = Color.RED;
    private int positionX = 0;
    private int positionY = 0;
    
    public DrawOval() {
        setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
    }

    public int getPositionX() {
        return positionX;
    }

    public void setPositionX(int positionX) {
        this.positionX = positionX;
    }

    public int getPositionY() {
        return positionY;
    }

    public void setPositionY(int positionY) {
        this.positionY = positionY;
    }

    @Override
    protected void paintComponent(Graphics g) {
        // this is needed to do house-keeping painting, to clear "dirty" pixels
        super.paintComponent(g); 
        
        // this is needed to draw smooth graphics
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        
        g2.setColor(OVAL_COLOR);
        g2.fillOval(positionX, positionY, RADIUS, RADIUS);
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • can you edit to keep the circle moving as long as the button is long pressed and why annotation @SuppressWarnings("Serial"). just trying to understand – TechGeek May 21 '22 at 19:22
  • @TechGeek: again, a separate problem, but yes, this can be done using the button to call `start()` on a Swing [Swing Timer](http://docs.oracle.com/javase/tutorial/uiswing/misc/timer.html). I would get the button's model, its ButtonModel, and add a ChangeListener, and inside the listener check the value returned from the `isPressed()` method, and start or stop the timer based on it. – Hovercraft Full Of Eels May 21 '22 at 19:24
  • You are also right about this line ` g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);`. It draws a clearer image then the other one, the border is sharp the other one was blurry – TechGeek May 21 '22 at 19:39