0

I'm trying to make a Brick Breaker game where the ball which is initially on the paddle(referred to as BAR in the code below) is launched with a speed(approx 100) in some random direction, if it hits any brick on the screen the brick disappears and the ball changes direction accordingly.

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import java.awt.event.KeyEvent;
import java.awt.event.*;

public class Brick_Breaker extends Canvas implements KeyListener{
    
    int BRICK_WIDTH=60,BRICK_HEIGHT=30,X=0,Y=0;
    int BAR_X = 390,BAR_Y=560,BAR_WIDTH=120,BAR_HEIGHT=20,BAR_DX=10;
    int BALL_X=440,BALL_Y=540,BALL_SIZE=20,BALL_DX=0,BALL_DY=0;
    int LEFT_WALL=100,RIGHT_WALL=800,TOP_WALL=10;
    boolean GAME_STATE=false;
    int BAR_STATE=0;
    
    
    static int[][] y = new int[10][5];
    static int[][] x = new int[10][5];
    static int[][] status = new int[10][5];
    
    static Canvas canvas;
    static JFrame frame;
    @Override
    public void keyReleased(KeyEvent ke){}
    @Override
    public void keyTyped(KeyEvent ke){}
    
    public void STaRT(){
        frame = new JFrame("My Drawing");
        frame.addKeyListener(this);
        canvas = new Brick_Breaker();
        canvas.setSize(900, 600);
        frame.add(canvas);
        frame.pack();
        frame.setVisible(true);
    }
    
    
    @Override
    public void update(Graphics g){
        BALL_X += BALL_DX;
        BALL_Y += BALL_DY;
        for(int i=0;i<10;i++){
            for(int j=0;j<5;j++){
                if(BALL_X>x[i][j]&&BALL_X<(x[i][j]+BRICK_WIDTH)&&BALL_Y<=y[i][j]+BAR_HEIGHT+BALL_SIZE && BALL_Y>y[i][j]&&BALL_DY<0){
                    BALL_DY=-BALL_DY;
                    status[i][j]-=1;
                }else if(BALL_X>x[i][j]&&BALL_X<(x[i][j]+BRICK_WIDTH)&&BALL_Y>=y[i][j]-BALL_SIZE && BALL_Y<y[i][j]+BAR_HEIGHT&&BALL_DY>0){
                    BALL_DY=-BALL_DY;
                    status[i][j]-=1;
                }else if(BALL_X+BALL_SIZE>=x[i][j]&&BALL_X<(x[i][j]+BRICK_WIDTH)&&BALL_Y>=y[i][j] && BALL_Y<y[i][j]+BAR_HEIGHT&&BALL_DY<0){
                    BALL_DX=-BALL_DX;
                    status[i][j]-=1;
                }else if(BALL_X>x[i][j]&&BALL_X-BALL_SIZE<=(x[i][j]+BRICK_WIDTH)&&BALL_Y>=y[i][j] && BALL_Y<y[i][j]+BAR_HEIGHT&&BALL_DY<0){
                    BALL_DX=-BALL_DX;
                    status[i][j]-=1;
                }else if(BALL_X-BALL_SIZE<LEFT_WALL||BALL_X+BALL_SIZE>RIGHT_WALL){
                    BALL_DX=-BALL_DX;
                }else if(BALL_Y-BALL_SIZE<TOP_WALL){
                    BALL_DY=-BALL_DY;
                }
            }
        }
        BAR_X = BAR_X +BAR_STATE*BAR_DX;
        repaint();
    }
    public static void main(String[] args) {
       Brick_Breaker bb = new Brick_Breaker();
       bb.STaRT();
        for(int i=0;i<10;i++){
            for(int j=0;j<5;j++){
                x[i][j] = 300+60*j;
                y[i][j] = 50+30*i;
                status[i][j] = 1;
            }
        }
    }
    
    @Override
    public void keyPressed(KeyEvent ke){
        if(!GAME_STATE){
            BALL_DX = (int)(100*Math.random());
            BALL_DY = (int)(Math.sqrt(100*100-BALL_DX*BALL_DX));
            GAME_STATE = true;
        }else if(ke.getKeyCode() == KeyEvent.VK_LEFT){
            BAR_STATE=-1;
        }else if(ke.getKeyCode()==KeyEvent.VK_RIGHT){
            BAR_STATE=1;
        }
    }
    
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.setColor(Color.white);
        g.drawLine(LEFT_WALL,600, LEFT_WALL, TOP_WALL);
        g.drawLine(RIGHT_WALL, 600, RIGHT_WALL, TOP_WALL);
        g.drawLine(LEFT_WALL,TOP_WALL,RIGHT_WALL,TOP_WALL);
        g.fillRect(BAR_X,BAR_Y,BAR_WIDTH,BAR_HEIGHT);
        g.setColor(Color.red);
        g.fillOval(BALL_X, BALL_Y, BALL_SIZE, BALL_SIZE);
        for(int i=0;i<10;i++){
            for(int j=0;j<5;j++){
                if(status[i][j]>0){
                    X = x[i][j];Y=y[i][j];
                    g.setColor(Color.white);
                    g.fillRect(X, Y, BRICK_WIDTH, BRICK_HEIGHT);
                    g.setColor(Color.red);
                    g.fillRect(X+3, Y+3, BRICK_WIDTH-6, BRICK_HEIGHT-6);
                }
            }
        }
        setBackground(Color.black);
    }
}

Here the update function doesn't seem to update at all. I'm not really sure on how the method works as well. Any help would be appreciated

3
  • 2
    1. Get rid of your use of Canvas, update, and the paint method. Instead, extend JPanel and override its paintComponent, being sure to call the super's method first thing in your override. Commented Sep 25, 2020 at 11:18
  • 2
    You should not override methods if you don't understand what they do or are prepared to take over there responsibility. update is used as part of the painting workflow, but since you're not calling super.update nothing will get painting. Because of the way painting works in AWT and Swing, the paint routines are not called on a regular bases, so they are bad choice for trying to update the state, besides, painting should be reserved for painting and nothing else Commented Sep 25, 2020 at 11:22
  • 2
    I'd recommend having a look at Performing Custom Painting for a better understand of how painting works. Canvas is also not double buffered, so you'll have to deal with that and you really should be using key bindings over KeyListener Commented Sep 25, 2020 at 11:24

1 Answer 1

2

Canvas is a pretty low level component and one which I'd only consider using if I needed to use a BufferStrategy.

update is called as part of the paint phase and because you've overridden it without calling it's super implementation, you broke the paint workflow.

AWT and Swing generally employ a passive rendering system, this means that updates only occur when the system decides it needs to happen, so overriding update the way you have doesn't really make sense and isn't going to help you.

A better place to start is with a JPanel, the main reason is because it's double buffered and saves you hassles of trying to figure out how to eliminate flickering.

A good place to start is Performing Custom Painting and Painting in AWT and Swing to gain a better understanding of how painting works (and how you should work with it).

KeyListener is also a poor choice for input monitoring, seriously, just do some searching on SO for all the reasons why.

A better, and generally less problematic approach is to use key bindings as well.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        int BRICK_WIDTH = 60, BRICK_HEIGHT = 30, X = 0, Y = 0;
        int BAR_X = 390, BAR_Y = 560, BAR_WIDTH = 120, BAR_HEIGHT = 20, BAR_DX = 1;
        int BALL_X = 440, BALL_Y = 540, BALL_SIZE = 20, BALL_DX = 0, BALL_DY = 0;
        int LEFT_WALL = 100, RIGHT_WALL = 800, TOP_WALL = 10;
        boolean GAME_STATE = false;
        int BAR_STATE = 0;

        int[][] y = new int[10][5];
        int[][] x = new int[10][5];
        int[][] status = new int[10][5];

        private Timer timer;

        public TestPane() {
            for (int i = 0; i < 10; i++) {
                for (int j = 0; j < 5; j++) {
                    x[i][j] = 300 + 60 * j;
                    y[i][j] = 50 + 30 * i;
                    status[i][j] = 1;
                }
            }
            BALL_DX = 0; //(int) (100 * Math.random());
            BALL_DY = -1;//(int) (Math.sqrt(100 * 100 - BALL_DX * BALL_DX));
            setBackground(Color.BLACK);

            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "left");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "right");

            ActionMap am = getActionMap();
            am.put("left", new KeyAction(this, -1));
            am.put("right", new KeyAction(this, 1));
        }

        @Override
        public void addNotify() {
            super.addNotify(); //To change body of generated methods, choose Tools | Templates.
            if (timer == null) {
                timer = new Timer(5, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        tick();
                    }
                });
                timer.start();
            }
        }

        @Override
        public void removeNotify() {
            super.removeNotify();
            if (timer != null) {
                timer.stop();
                timer = null;
            }
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(900, 600);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);

            Graphics2D g2d = (Graphics2D) g.create();
            g2d.setColor(Color.white);
            g2d.drawLine(LEFT_WALL, 600, LEFT_WALL, TOP_WALL);
            g2d.drawLine(RIGHT_WALL, 600, RIGHT_WALL, TOP_WALL);
            g2d.drawLine(LEFT_WALL, TOP_WALL, RIGHT_WALL, TOP_WALL);
            g2d.fillRect(BAR_X, BAR_Y, BAR_WIDTH, BAR_HEIGHT);
            g2d.setColor(Color.red);
            g2d.fillOval(BALL_X, BALL_Y, BALL_SIZE, BALL_SIZE);
            for (int i = 0; i < 10; i++) {
                for (int j = 0; j < 5; j++) {
                    if (status[i][j] > 0) {
                        X = x[i][j];
                        Y = y[i][j];
                        g2d.setColor(Color.white);
                        g2d.fillRect(X, Y, BRICK_WIDTH, BRICK_HEIGHT);
                        g2d.setColor(Color.red);
                        g2d.fillRect(X + 3, Y + 3, BRICK_WIDTH - 6, BRICK_HEIGHT - 6);
                    }
                }
            }

            g2d.dispose();
        }

        protected void tick() {
            BALL_X += BALL_DX;
            BALL_Y += BALL_DY;
            for (int i = 0; i < 10; i++) {
                for (int j = 0; j < 5; j++) {
                    if (BALL_X > x[i][j] && BALL_X < (x[i][j] + BRICK_WIDTH) && BALL_Y <= y[i][j] + BAR_HEIGHT + BALL_SIZE && BALL_Y > y[i][j] && BALL_DY < 0) {
                        BALL_DY = -BALL_DY;
                        status[i][j] -= 1;
                    } else if (BALL_X > x[i][j] && BALL_X < (x[i][j] + BRICK_WIDTH) && BALL_Y >= y[i][j] - BALL_SIZE && BALL_Y < y[i][j] + BAR_HEIGHT && BALL_DY > 0) {
                        BALL_DY = -BALL_DY;
                        status[i][j] -= 1;
                    } else if (BALL_X + BALL_SIZE >= x[i][j] && BALL_X < (x[i][j] + BRICK_WIDTH) && BALL_Y >= y[i][j] && BALL_Y < y[i][j] + BAR_HEIGHT && BALL_DY < 0) {
                        BALL_DX = -BALL_DX;
                        status[i][j] -= 1;
                    } else if (BALL_X > x[i][j] && BALL_X - BALL_SIZE <= (x[i][j] + BRICK_WIDTH) && BALL_Y >= y[i][j] && BALL_Y < y[i][j] + BAR_HEIGHT && BALL_DY < 0) {
                        BALL_DX = -BALL_DX;
                        status[i][j] -= 1;
                    } else if (BALL_X - BALL_SIZE < LEFT_WALL || BALL_X + BALL_SIZE > RIGHT_WALL) {
                        BALL_DX = -BALL_DX;
                    } else if (BALL_Y - BALL_SIZE < TOP_WALL) {
                        BALL_DY = -BALL_DY;
                    }
                }
            }
            BAR_X = BAR_X + BAR_STATE * BAR_DX;
            repaint();
        }

        class KeyAction extends AbstractAction {

            private TestPane source;
            private int delta;

            public KeyAction(TestPane source, int delta) {
                this.source = source;
                this.delta = delta;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                source.BAR_STATE = delta;
            }

        }
    }
}

I've messed with delta values, because the ball and bar disappeared real quick

Sign up to request clarification or add additional context in comments.

2 Comments

Hey, thanks a lot, the answer helps learn a lot of stuff. There's one thing I dont understnd though- what is addNotify() and removeNotify()?
Best place is to read the docs, but addNotify is called in response to the component been added to a container and removeNotify is called in response to the component been removed

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.