0

This is the main file where I've created the main method. I've added the JFrames here to make it easier to understand. I created a boolean variable in the second file, and whenever the spacebar is pressed, the variable updates to true. I then use the variable by creating an object in the main class and then add conditional statements to make 1 frame visible or vice versa.

import javax.swing.JFrame;  

public class MainCode{
    public static void main(String args[]){  

        JFrame f=new JFrame();
        Gameplay GP = new Gameplay();

        GameplayLv2 g2 = new GameplayLv2();
        JFrame frame2 = new JFrame();


        
   
        f.setBounds(10,10,1000,700);
        f.setTitle("Breakout Ball");
        f.setResizable(false);
        f.setVisible(true);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.add(GP);


        if(GP.flagCheck==true){
        f.setVisible(false);
        f.remove(GP);

        frame2.setBounds(10,10,1000,700);
        frame2.setTitle("Breakout Ball");
        frame2.setResizable(false);
        frame2.setVisible(true);
        frame2.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame2.add(g2);
        }           
        }

}

This is the game file, level 1. The issue that I keep facing is at the bottom of this code, the keyEvent block.

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

import java.awt.Color;
import java.awt.Graphics;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.Timer;
import javax.swing.JFrame;

 public class Gameplay extends JPanel implements KeyListener, ActionListener{
     

     public boolean play=false;  //prevents game from starting on its own...
     public int score=0;        //scorebar will be 0 by default...

     public int totalBlocks = 30;  //no.of tiles/blocks player must knock down...

     public Timer speed;           //speed of ball...
     public int delay=5;           //speed of ball....

     public int playerX = 300;     //starting position of the slider..

     public int ballPosX = 210;    //starting position of the ball..(x-axis)
     public int ballPosY = 350;    //starting position of the ball..(y-axis)

     public int ballDirX = -1;     //direction for ball to start moving in...(x axis)
     public int ballDirY = -2;     //direction for ball to start moving in...(y axis) //ALSO CONTROLS SPEED OF BALL ALONG Y-AXIS

     public boolean flagCheck;
      
     MapGen map;

     Color ball_color = new Color(255, 252, 37);        //Setting custom colors for every element using RGB values...
     Color border_color = new Color(232, 20, 171);
     Color border2_color = new Color(20, 232, 213);
     Color slider_color = new Color(0, 240, 255);
     Color bg_color = new Color(11,10,30);
     Color tiles_color = new Color(255, 255, 255);

     JFrame frame1 = new JFrame();

    
     


    
    public Gameplay(){
         map = new MapGen(3,10);
         addKeyListener(this);
         setFocusable(true);
         setFocusTraversalKeysEnabled(false);
         speed = new Timer(delay,this);
         speed.start();  
         flagCheck = false; 
     }


     public void paint(Graphics g){

        g.setColor(bg_color);
        g.fillRect(1, 1, 1000, 700);
        
        //drawing the map(tiles)
        map.draw((Graphics2D)g);

        // adding borders so that whenever ball hits border, game ends..
         g.setColor(border_color);
         g.fillRect(0, 0, 10, 1000);

         g.setColor(border_color);
         g.fillRect(975, 0, 10, 1000);

         g.setColor(border2_color);
         g.fillRect(0, 0, 1000, 10);

        // editing the slider/player...
        g.setColor(slider_color);
        g.fillRect(playerX, 620, 120, 5);

        // editing the ball...
        g.setColor(ball_color);
        g.fillOval(ballPosX, ballPosY, 20, 20);

        //scoring system
        g.setColor(Color.white);
        g.setFont(new Font("helvetica", Font.BOLD, 20));
        g.drawString("SCORE:"+score, 800, 650);

        
        if(ballPosY>670){                                     //what'll happen when the ball moves out of screen?(anything after y-axis670)? This...
            play=false;
            ballDirX=0;
            ballDirY=0;

            g.setColor(bg_color);                          //this BG will act as a layer on top of the previous screen, hence, covering unnecessarry elements...
            g.fillRect(1, 1, 1000, 700);

            g.setColor(slider_color);
            g.setFont(new Font("helvetica", Font.BOLD, 25));     //scoreboard display after game ends..
            g.drawString("SCORE:"+score, 440, 200);
            
             g.setColor(slider_color); 
             g.setFont(new Font("helvetica", Font.BOLD, 40));     //game over text displayed after game ends..
             g.drawString("GAME OVER!", 370, 300);
            
            g.setColor(slider_color); 
            g.setFont(new Font("helvetica", Font.BOLD, 40));      //restart text displayed after game ends..
            g.drawString("Press enter to retry", 260, 405);
            

        }

        if(score==30){  //make it 150                                   //what'll happen when the score is 150, ie win
            play=false;
            ballDirX=210;
            ballDirY=350;
            

            g.setColor(bg_color);                          //this BG will act as a layer on top of the previous screen, hence, covering unnecessarry elements...
            g.fillRect(1, 1, 1000, 700);

            g.setColor(slider_color);
            g.setFont(new Font("helvetica", Font.BOLD, 25));     //scoreboard display after game ends..
            g.drawString("SCORE:"+score, 420, 200);
            
             g.setColor(slider_color); 
             g.setFont(new Font("helvetica", Font.BOLD, 40));     //game over text displayed after game ends..
             g.drawString("YOU WIN", 400, 300);
            
            g.setColor(slider_color); 
            g.setFont(new Font("helvetica", Font.BOLD, 40));      //go to next level text displayed after game ends..
            g.drawString("Press space to continue to next level", 155, 405);

        }
         
        g.dispose();
     }
      


     @Override
    public void actionPerformed(ActionEvent e) {
        speed.start();

        if(play==true){

            if(new Rectangle(ballPosX, ballPosY, 20, 20).intersects(new Rectangle(playerX, 620, 120, 10)))   
            //creates an invisible rectangle around the slider and ball which helps with interaction control
            {
                ballDirY=-ballDirY;
            }

            A: for(int i=0; i<map.map.length; i++){                        //one of the maps are MapGen map; while the other map is from @mapGen class to access the double dimensional array...
                for(int j=0; j<map.map[0].length; j++){
                    if(map.map[i][j]>0){
                        int tX = j*map.tW+80;
                        int tY = i*map.tH+50;
                        int tW = map.tW;
                        int tH = map.tH;

                        Rectangle rect = new Rectangle(tX, tY, tW, tH);                 //creates invisible rects around tiles to detect interaction with ball...
                        Rectangle ballRect = new Rectangle(ballPosX, ballPosY, 20, 20);
                        Rectangle tileRect = rect;

                        if(ballRect.intersects(tileRect)){                              //removes one triangle everytime interaction takes place and increases the score by 5...
                            map.setTileValue(0, i, j);
                            totalBlocks--;
                            score+=5;
                        

                        if(ballPosX+19 <= tileRect.x || ballPosX+1 >= tileRect.x + tileRect.width){    //specifies which direction the ball must head to after interacting with the tiles...
                            ballDirX=-ballDirX;
                        }
                        else{
                            ballDirY=-ballDirY;
                        }
                        break A;
                        }
                        
                    }

                }
            } 

        ballPosX+=ballDirX;
        ballPosY+=ballDirY;


         //specifies where the ball will have to shift position. In this case, on hitting either of the borders, position(x-y-coordinates) will change....
        if(ballPosX<0)
        ballDirX=-ballDirX;             
        if(ballPosY<0)
        ballDirY=-ballDirY;
        if(ballPosX>960)
        ballDirX=-ballDirX;      
        }

        repaint();                                    //will re-draw every element(slider etc)... 
    }


    @Override
    public void keyTyped(KeyEvent e) {}                 //unnecessarry methods that when removed produced error...so..yeah...
    @Override
    public void keyReleased(KeyEvent e) {}
    

    @Override
    public void keyPressed(KeyEvent e) {

        
        if(e.getKeyCode()==KeyEvent.VK_RIGHT)
        {
            if(playerX>=855)                  //prevents the paddle/slider from moving out of the screen
            playerX=855;

            else
            moveRight();

        }
        
        if(e.getKeyCode()==KeyEvent.VK_LEFT)  
        {
            if(playerX<=10)                   //prevents the paddle/slider from moving out of the screen
            playerX=10;

            else
            moveLeft();

        }
        if(e.getKeyCode()==KeyEvent.VK_ENTER){       //What actions must take place when "ENTER" key is pressed. In this context, restarting  of game...
            if(!play){
                play=false;
                 ballPosX = 210;   
                 ballPosY = 350;   

                 ballDirX = -1;    
                 ballDirY = -2;

                 playerX = 300;
                 score=0;
                 totalBlocks=30;
                 map=new MapGen(3,10);

                 repaint();

            }        
        }

        if(e.getKeyCode()==KeyEvent.VK_SPACE){    //what actions must take place when "SPACE" key is pressed. In this context, moving to new level-to be implmented...     
                if(!play && score==30){    
                    flagCheck=true;
                    System.out.println(flagCheck); //console stuff
                }        
        }  
    }

    public void moveRight(){            //helps with the movement of slider. +values move right on x-axis whereas -values move left on x-axis
        play=true;
        playerX+=40;
    }
    public void moveLeft(){
        play=true;
        playerX-=40;
    } 
      
}
MED
  • 1
  • 1
    1) Use key bindings over a key listener. 2) Use layouts. 3) [Edit] to add a [mre]. It does not take 2 classes & over 300 lines of code to show a key listener failing. They can do that in 20 LOC. – Andrew Thompson Dec 06 '21 at 05:16
  • The long answer is, not like that. Apart from using key bindings, you shouldn't need to switch windows, instead, have a "master" panel which does the switching for you. Also, don't override `paint` and don't call `repaint` from within a running painting pass – MadProgrammer Dec 06 '21 at 07:28

1 Answers1

0

You're running in an event driven environment, that is, something happens and then you respond to it.

You've taken a procedural approach to your design. Showing a window and some how hoping that the state of the window "block" the execution workflow and allow you to monitor some other state. This is not how (non modal) windows work.

When you call setVisible, it will return (almost) immediately, the display of the window will occur at some point in the future after that, depending on the OS and other overheads.

This means that GP.flagCheck will also fail.

Instead, stop trying to do this with windows. It's annoying (to the user). Instead, just switch the panels, for example...

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
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 MasterPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MasterPane extends JPanel {

        public MasterPane() {
            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "spaced");
            am.put("spaced", new AbstractAction() {

                private int counter = 0;

                @Override
                public void actionPerformed(ActionEvent e) {
                    JPanel next = null;
                    if (counter == 0) {
                        counter = 1;
                        next = new LevelTwoPane();
                    } else if (counter == 1) {
                        counter = 0;
                        next = new LevelOnePane();
                    }
                    if (next != null) {
                        removeAll();
                        add(next);
                        revalidate();
                        repaint();
                    }
                }
            });

            setLayout(new BorderLayout());
            add(new LevelOnePane());
        }

    }

    protected abstract class AbstractGamePane extends JPanel {

        private Timer timer;

        protected abstract void tick();

        protected void start() {
            if (timer != null) {
                timer.stop();
                timer = null;
            }
            timer = new Timer(5, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    tick();
                }
            });
            timer.start();
        }

        protected void stop() {
            if (timer == null) {
                return;
            }
            timer.stop();
            timer = null;
        }

        @Override
        public void addNotify() {
            super.addNotify();
            start();
        }

        @Override
        public void removeNotify() {
            super.removeNotify();
            stop();
        }

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

    public static class LevelOnePane extends AbstractGamePane {

        protected static final String TEXT = "Level one";

        private int xPos = 0;
        private int xDelta = 1;

        @Override
        protected void tick() {
            xPos += xDelta;
            FontMetrics fm = getFontMetrics(getFont());
            if (xPos > getWidth() - fm.stringWidth(TEXT)) {
                xPos = getWidth() - fm.stringWidth(TEXT);
                xDelta *= -1;
            } else if (xPos < 0) {
                xPos = 0;
                xDelta *= -1;
            }
            repaint();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            FontMetrics fm = g2d.getFontMetrics();
            int yPos = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
            g2d.drawString(TEXT, xPos, yPos);
            g2d.dispose();
        }

    }

    public class LevelTwoPane extends AbstractGamePane {

        protected static final String TEXT = "Level two";

        private int xPos = 0;
        private int xDelta = 1;

        @Override
        protected void tick() {
            xPos += xDelta;
            FontMetrics fm = getFontMetrics(getFont());
            if (xPos > getWidth() - fm.stringWidth(TEXT)) {
                xPos = getWidth() - fm.stringWidth(TEXT);
                xDelta *= -1;
            } else if (xPos < 0) {
                xPos = 0;
                xDelta *= -1;
            }
            repaint();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            FontMetrics fm = g2d.getFontMetrics();
            int yPos = ((getHeight() - fm.getHeight()) / 2) + fm.getAscent();
            g2d.drawString(TEXT, xPos, yPos);
            g2d.dispose();
        }

    }
}

Even this is unnecessarily heavy handed. An overall better solution would be to use a model/view/controller.

The model would carry all the information about the level that the view needed to render it and the controller would manage the interaction between the view and the model (ie the user taps a button, the view tells the controller and the controller makes decisions about what needs to be done, like updating the model or moving to the next level or what ever).

You really should take a look at How to Use Key Bindings, KeyListeners are just trouble waiting to happen.

You can also have a look at Detecting multiple keypresses in java for a more detailed example

You will note that the MainPane has a binding to Space, but I would also set up key bindings, probably in the AbstractGamePane, assuming all game levels have the same controls, to manage the game play

You should also take a look at How to Use Swing Timers for a way to manage a "game loop" better. Calling repaint within a running paint pass is asking for trouble (it will end up running your CPU hot)

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366