Search code examples
javaswingjpanelpaintcomponentpong

Only able to add JLabel background or JPanel background


I am making my first game in Java. This is the first time using swing too. I am having difficulties adding objects correctly to the frame.

Some irrelevant code has been removed from other classes. There may be several components missing that are referenced.

Main:

public class Main { 
    Ball ball = new Ball();
    int yBall = 0;

    public static void main(String[] args) throws InterruptedException {
        Main main = new Main();
        main.run();
        while (true) {
            main.ball(100);
        }
    }
    public void run() throws InterruptedException {
        Data data = new Data();
        Frame frame = new Frame(700, 500);
        test.setAngleX();
        frame.add(data, BorderLayout.CENTER);
    }
}

When I add ball after data(background) my ball is visible on the frame, overlapping my data. If I would switch position here my data would be visible but not my ball, the ball still exists underneath. frame.add(ball); frame.setVisible(true);

public void ball(int yBall) throws InterruptedException {
    ball.Ball();
    yBall = ball.SendY();
}

This is my frame, no problems in here what I know:

public class Frame extends JFrame {
    private int frameWidth;
    private int frameHeight;
    int yBall = 0;

    public Frame(int frameWidth, int frameHeight) {
        this.frameWidth=frameWidth;
        this.frameHeight = frameHeight;

        setSize(new Dimension(frameWidth, frameHeight));
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    }
}

Here's my Ball. paintComponent is at the end, before that it is just working logics that I'm guessing will be irrelevant.

Ball:

public class Ball extends JLabel{

Graphics g1;

int x = 325, y = 225;
int speed = 1;
int angleX = 0;
int angleY = 0;

public Ball() {
}

public void setAngleX() {

    angleX = ThreadLocalRandom.current().nextInt(-5, 5 + 1);    
    angleY = ThreadLocalRandom.current().nextInt(-1, 1 + 1);    

    if (angleX == 0) {
        setAngleX();
    }

    if (angleY == 0) {
        setAngleX();
    }
}

public void move() {

    if (x + angleX < 0) {
        angleX = speed;
    } else if (x + angleX > getWidth() - 25) {
        angleX = -speed;
    } else if (y + angleY < 0) {
        angleY = speed;
    } else if (y + angleY > getHeight() - 25) {
        angleY = -speed;
    }

    x = x + angleX;
    y = y + angleY;
}

public void Ball() throws InterruptedException {
    move();
    repaint();

    int sleepSpeed = angleX * speed;
    if (sleepSpeed < 0) {
        sleepSpeed = -sleepSpeed;

        Thread.sleep(10*sleepSpeed);
    }
}


@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.setColor(Color.WHITE);
    g.fillOval(x, y, 25, 25);
}
public int SendY() {
    return y;
}

And finally my Data;

public class Data extends JPanel{
    private static final long serialVersionUID = 1L;
    private Graphics2D g2;
    public Data() {
    }
    @Override
    public void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(Color.BLACK);
        g2.fillRect(0, 0, getSize().width, getSize().height);
        g2.setColor(Color.lightGray);
        g2.fillRect(350, 0, 10, 400);
    }

So what happens is that I either get my data(background) on my frame or I get my Ball bouncing around as it should but on an empty background, depending on which one I add last in my main. I'm guessing it's bcz of overlapping but I can't find a solution working for my code, no matter how much I search..


Solution

  • that I either get my data(background) on my frame or I get my Ball bouncing around as it should but on an empty background, depending on which one I add last in my main.

    You can't add both components to the frame. you need to have a parent/child relationship.

    So the logic should be something like:

    background.add( ball );
    frame.add( background );
    

    Then the frame will be painted, then the background and finally the ball.

    Also, the Ball class will need to implement the getPreferredSize() method so component has a reasonable size. You should be painting the Ball at offset (0, 0) in the paint component method. When you want to move the ball on the background panel, then you just use setLocation(...) on the ball to reposition it on the background. When you create the Ball you would also need to set the size of the Ball to the preferred size so the Ball has a size/location to be painted on the background panel. The layout of the background panel would need to be set to null, so you can freely move the ball around the panel.

    You should NOT be extending JLabel, you are not using any features of the JLabel. You are doing custom painting so just extend JPanel or JComponent. Read the section from the Swing tutorial on Custom Painting for more information and working examples.

    Also, method names should NOT start with an upper case character. Ball() is not a proper method name. It looks like the class name. The method should be more descriptive.

    Or the other approach, instead of having two components, is to have a single component. Then the paintComopnent(...) method of that component would paint both:

    1. the background
    2. the ball

    Then this way you would paint the Ball relative to the background panel which is how your logic currently works.