2
\$\begingroup\$

This is my first graphical application I have made in my life (except HTML and Javascript-Applications, if that counts). It's a simple implementation of Cookie Clicker, the famous browser game. Unfortunately the size of the buttons equals the size of columns. That does not look good. I have seen a solution that uses two layouts. A GridLayout, and an additional FlotLayout. But the code looks that ugly, that I don't want use this way of programming.

I would be very thankful if you have tips for me how to improve the code quality! Programming a graphical application is much more harder and complex than writing textual applications.

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

public class CookieClicker extends JFrame {
    // non graphical variables
    private int cookies = 0;
    private int clicker = 1;
    private int clickerPrice = 20;

    // graphical variables
    int numberOfColumns = 5;

    Container container;

    JLabel cookieLabel;
    JButton increaseCookiesButton;

    JLabel clickerLabel;
    JButton increaseClickerButton;

    // buildings
    Building bakery;
    boolean bakeryUnlocked;

    Building robot;
    boolean robotUnlocked;

    Building factory;
    boolean factoryUnlocked;

    public CookieClicker() {
        container = getContentPane();
        container.setLayout(new GridLayout(5, 1));

        bakery = new Building("Bakery", 0, 1, 20);
        bakeryUnlocked = false;

        robot = new Building("Robot", 0, 5, 100);
        robotUnlocked = false;

        factory = new Building("Factory", 0, 10, 200);
        factoryUnlocked = false;

        // produce cookies by hand
        cookieLabel = new JLabel("Cookies: " + cookies);
        increaseCookiesButton = new JButton("Increase Cookies");
        increaseCookiesButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                cookies += clicker;
            }
        });

        // improve clicking production rate
        clickerLabel = new JLabel("Clicker Level: " + clicker);
        increaseClickerButton = new JButton("Improve Clicker (Costs: " + clickerPrice + ")");
        increaseClickerButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                increaseClicker();
            }

            private void increaseClicker() {
                if(cookies >= clickerPrice) {
                    clicker++;
                    cookies -= clickerPrice;
                    clickerPrice *= 2;
                    JOptionPane.showMessageDialog(null, "You have improved your clicker!");
                } else {
                    JOptionPane.showMessageDialog(null, "You have not enough money!");
                }
            }
        });

        java.util.Timer actualizeProgress = new java.util.Timer();
        actualizeProgress.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                cookieLabel.setText("Cookies: " + cookies);
                clickerLabel.setText("Clicker Level: " + clicker);
                increaseClickerButton.setText("Improve Clicker (Costs: " + clickerPrice + ")");
            }
        }, 0, 25);

        java.util.Timer getMoreBuildings = new java.util.Timer(); 
        getMoreBuildings.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                if (bakeryUnlocked == false && clicker >= 2) {
                    bakery.unlock();
                    bakeryUnlocked = true;
                }
                if (robotUnlocked == false && bakery.getLevel() >= 2) {
                    robot.unlock();
                    robotUnlocked = true;
                }         
                if (factoryUnlocked == false && robot.getLevel() >= 2) {
                    factory.unlock();
                    factoryUnlocked = true;
                }
            }
        }, 0, 2000);

        java.util.Timer produceWithBuildings = new java.util.Timer();
        produceWithBuildings.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                cookies += bakery.getProductionRate() + robot.getProductionRate() + factory.getProductionRate();
            }
        }, 0, 1000);

        container.add(cookieLabel);
        container.add(increaseCookiesButton);
        container.add(new JLabel("")); // blank label
        container.add(clickerLabel);
        container.add(increaseClickerButton);
    }

    public class Building {
        // non graphical variables
        private String name;
        private int level;
        private int productionRate;
        private int costs;

        // graphical variables
        JLabel label;
        JButton button;

        public Building(String name, int level, int productionRate, int costs) {
            // non graphical variables
            this.name = name;
            this.level = level;
            this.productionRate = productionRate;
            this.costs = costs;

            // graphical variables
            label = new JLabel();
            button = new JButton();
            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    improve();
                }
            });
        }

        public int getLevel() {
            return level;
        }

        public void unlock() {
            numberOfColumns += 3;
            container.setLayout(new GridLayout(numberOfColumns, 1));
            container.add(new JLabel(""));
            container.add(label);
            container.add(button);
            setSize(210, getHeight() + 120);
            actualize();
        }

        public void improve() {
            if(cookies >= costs) {
                level++;
                cookies -= costs;
                costs *= 2;
                JOptionPane.showMessageDialog(null, "You have improved the " + name + "!");
            } else {
                JOptionPane.showMessageDialog(null, "You have not enough money!");
            }
            actualize();
        }

        public int getProductionRate() {
            return productionRate * level;
        }

        public void actualize() {
            label.setText(name + " Prod. Rate: " + getProductionRate());
            button.setText("Improve (costs: " + costs + ")");
        }
    }

    public static void main(String[] args) {
        CookieClicker cookieClicker = new CookieClicker();
        cookieClicker.setTitle("Cookie Clicker");
        cookieClicker.setSize(210, 200);
        cookieClicker.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        cookieClicker.setVisible(true);
    }
}
\$\endgroup\$
7
\$\begingroup\$

Thanks for sharing your code.

It is a really good for the first attempt.

Nevertheless there is something to mention...

Naming

Finding good names is the hardest part in programming. So always take your time to think carefully of your identifier names.

Choose your names from the problem domain

You have some identifiers which are named after their technical implementation like this:

Container container;
JButton increaseCookiesButton;
JButton button;

They should have names that reveal their task within your application.

Container coockieDisplay;
JButton cookiesIncreaser;
JButton buildingImprover;

Naming Conventions

Please read (and follow) the Java Naming Conventions

eg.:
* Your boolean variables and methods returning a boolean should start with is, has, can or the like. * Method names should start with a verb. * Class and variable names should be nouns.

Don't surprise your readers

Also you have variable names that start with verbs like

JButton increaseCookiesButton;
Timer actualizeProgress = new java.util.Timer();
Timer getMoreBuildings = new java.util.Timer();

But according to the Java Naming Conventions, only methods should start with a verb. So they might better be named like this:

JButton cookiesIncreaser;
Timer progressUpdater = new java.util.Timer();
Timer buildingsUnlocker = new java.util.Timer();

Coding practice

Magic numbers

You code has some literal numbers with special meaning like here:

if (bakeryUnlocked == false
        && clicker >= 2) {

This should be extracted to constants with a name that expresses the meaning:

private static final int MINIMUM_UPGRADE_LEVEL = 2;
// ...
if (bakeryUnlocked == false
        && clicker >= MINIMUM_UPGRADE_LEVEL) {

Use of boolean

At some places you compare a boolean variable with a literal boolean value:

if (bakeryUnlocked == false
        && clicker >= 2) {
    bakery.unlock();

Don't do that use the boolean variable directly and use the negation operator if needed:

if (!isBakeryUnlocked 
        && clicker >= 2) {
    bakery.unlock();

Avoid unnecessary members

You have a variable container which is a member in your class CookieClicker. But in CookieClicker you only need it to initialize its content. You never use it outside the constructor. Therefore it should be a local variable in the constructor.

You use container in your other (named inner) class Building. But there you access the variable of Buildings outer class CookieClicker. There are some cases where this is OK, especially when the accessing class is an anonymous inner class. But in this case you should pass the container as constructor parameter to Building:

public CookieClicker() {
    container = getContentPane();
    container.setLayout(new GridLayout(5, 1));

    bakery = new Building("Bakery", 0, 1, 20, container);
    bakeryUnlocked = false;

    robot = new Building("Robot", 0, 5, 100, container);
    robotUnlocked = false;

    factory = new Building("Factory", 0, 10, 200, container);
// ...

public class Building {
    // ...
    // graphical variables
    JLabel label;
    JButton button;
    Container container;

    public Building(String name, int level, int productionRate, int costs, Container container) {
        //...
        // graphical variables
        this.container = container;
    //...

Same is true for the instances of JButton in this class.

Oddball solutions

You make a difference in your logic for the initial phase and after having unlocked the first building. Therefore you have similar code at two places.

I'd suggest to create one more instance of class Building to reuse its logic even for the initial phase.

Tell! Don't ask! - avoid feature envy

When unlocking the buildings you acquire the Buildings level property and make a decision. But accessing an objects property violates the information hiding principle / encapsulation. The Building class should know itself how to make that decision and provide a method hasReachedMinimumLevel():

   // in Building
    public boolean hasReachedMinimumLevel() {
        return 2<=level;
    }
   // ...
// in CoockieClicker
  if (!isRobotUnlocked && bakery.hasReachedMinimumLevel()) {
     robot.unlock();

OOP

Inheritance

In OOP we inherit from a super class if we extend its behavior. This is: we override a method to do something more and/or something different then the same method in the super class.

Your class extends JFrame but does not change a JFrame's behavior. You only configure its content. So your class should rather use a JFrame instead of extending it:

public class CookieClicker  {
    // non graphical variables
    // ...
    public CookieClicker(JFrame theFrame) {
      container = theFrame.getContentPane();        
    // ...

    public static void main(String[] args) {
        JFrame theFrame = new JFrame();
        CookieClicker cookieClicker = new CookieClicker(theFrame);
        theFrame.setTitle("Cookie Clicker");
        theFrame.setSize(210, 200);
        theFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        theFrame.setVisible(true);
    }       
}

avoid procedural approaches

Procedural approaches are not bad on their own.
But Java is an Object Oriented programming language and if you want to become a good Java programmer you should start looking for more OO-like solutions.

But OOP doesn't mean to "split up" code into random classes.

The ultimate goal of OOP is to reduce code duplication, improve readability and support reuse as well as extending the code.

Doing OOP means that you follow certain principles which are (among others):

  • information hiding / encapsulation
  • single responsibility
  • separation of concerns
  • KISS (Keep it simple (and) stupid.)
  • DRY (Don't repeat yourself.)
  • "Tell! Don't ask."
  • Law of demeter ("Don't talk to strangers!")

A good example is the way you unlock the buildings.

You introduce boolean variables to track the building states. A more OO-ish approach would be to hold the locked buildings in a List. Then I'd remove the first element from that list and unlock it until the list is empty:

Building dummyBuildingToAvoidAnotherOddBallSolution = new Building("",0,0,0){ // anonymous inner class
    @Override
    public int hasReachedMinimumLevel() {
        return 2 <= clicker;
    }
}
List<Building> lockedBuildings = 
     new ArrayList<>(
        Arrays.asList(dummyBuildingToAvoidAnotherOddBallSolution, bakery, robot, factory));

buildingsUnlocker.scheduleAtFixedRate(new TimerTask() {
    private final int FIRST_IN_QUEUE = 0; // avoid "magic number", cannot be static in non-static inner class
    Building activeBuilding = lockedBuildings.remove(FIRST_IN_QUEUE);
    @Override
    public void run() {
       if(!lockedBuildings.isEmpty()) {
         if(activeBuilding.hasReachedMinimumLevel()) {
            activeBuilding = lockedBuildings.remove(FIRST_IN_QUEUE);
            activeBuilding.unlock();
         }
       }
    }
}, 0, 2000);

With that approach you don't need the boolean variables at all. The logic itself becomes shorter and is able to deal with more buildings without any further modification.

\$\endgroup\$
  • \$\begingroup\$ Thank you for that excellent answer. But there is one point I would disagree. The logic that allows to unlock a building can't be implemented inside the building, because it depends on conditions that in turn depend on variables the object has no access to. This may could result into ugly looking code, or what do you think? \$\endgroup\$ – Dexter Thorn Jan 12 '18 at 20:07
  • \$\begingroup\$ You already track the buildings level inside the building, I already used it... \$\endgroup\$ – Timothy Truckle Jan 12 '18 at 20:28
  • 1
    \$\begingroup\$ Use a LinkedList instead of an ArrayList to have access to the method removeFirst() and truly avoid magic number. \$\endgroup\$ – Olivier Grégoire Oct 9 '18 at 15:21
1
\$\begingroup\$

I made some edits to your code for fun!

Check this link: https://drive.google.com/file/d/1BBblZPIUScPtr5kTHi0YAjA3phoeR2Gj/view?usp=sharing

The above link contains the whole project folder for the game. Note that this game needs images in a folder called Images and an audio file in a folder called Audio. The code for the game is written below.

import java.util.*;
import java.awt.*;
import java.awt.event.*;

import java.io.File;  //This library is used to search for the audio file in the game       
import java.io.IOException; //This library is used when exceptions or errors occur while using the File library.
import javax.sound.sampled.*; //This library is especially needed when dealing with audio in this program.
//The above three libraries have to be imported for the music in the game.

import javax.swing.*;

public class CookieClicker extends JFrame {
    // non graphical variables
    private int cookies = 0;
    private int clicker = 1;
    private int clickerPrice = 20;

    //These two variables are used to dictate the size of the JFrame.
    private static int screenWidth=420; 
    private static int screenHeight=400;

    private final static Font FONT=new Font("Serif", Font.BOLD, 14); //The fonts of most texts are set to this font. This is a bold Serif 14 font.
    private final static Color WINDOW_COLOR=new Color(125,125,125); //This gray color is applied to the windows in the game.
    private final static Color LABEL_COLOR= new Color(0, 102, 0) ; //This green color is applied to the labels.
    private final static Color BUTTON_COLOR=new Color(153,0,0); //This red color is applied to the buttons.
    // graphical variables
    int numberOfColumns = 7;

    Container container;

    JLabel cookieLabel;
    JButton increaseCookiesButton;

    JLabel clickerLabel;
    JButton increaseClickerButton;

    // buildings
    Building bakery;
    boolean bakeryUnlocked;

    Building robot;
    boolean robotUnlocked;

    Building factory;
    boolean factoryUnlocked;

    //This image of a notification is used
    public static ImageIcon notification=new ImageIcon("Images/notification.jpg"); 

    public CookieClicker() {
        container = getContentPane();
        /**The first parameter of GridLayout is set to the variable numberOfColumns, which is equal to 7. 
         * 7 columns is needed for the beginning of the game.
         */
        container.setLayout(new GridLayout(numberOfColumns, 1)); 
        container.setBackground(WINDOW_COLOR); //The container's background color is set to the window color.
        bakery = new Building("Bakery", 0, 1, 20, "Images/bakery.jpg");
        bakeryUnlocked = false;

        robot = new Building("Robot", 0, 5, 100, "Images/robot.jpg");
        robotUnlocked = false;

        factory = new Building("Factory", 0, 10, 200, "Images/factory.jpg");
        factoryUnlocked = false;

        // produce cookies by hand
        cookieLabel = new JLabel("Cookies: " + cookies);
        cookieLabel.setForeground(LABEL_COLOR);//The label's color is set to the variable LABEL_COLOR.
        cookieLabel.setFont(FONT); //The label's font is set to the variable FONT.

        increaseCookiesButton=new JButton(new ImageIcon("Images/cookie.jpg"));
        /**The button's foreground color is set to the variable BUTTON_COLOR.
         * Due to this, the button's text is the same as the color of BUTTON_COLOR.
         * BUTTON_COLOR is a red color, so the button's text is red.
         */
        increaseCookiesButton.setForeground(BUTTON_COLOR); 
        //The text of the button is set to "Increase cookies".
        increaseCookiesButton.setText("Increase cookies");
        //The button's font is set to the variable FONT.
        increaseCookiesButton.setFont(FONT);
        //By putting the increaseCookiesButton into the first parameter of the function, this button's background aspects will be hidden.
        transparentBackground(increaseCookiesButton);
        increaseCookiesButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                cookies += clicker;
            }
        });

        // improve clicking production rate
        clickerLabel = new JLabel("Clicker Level: " + clicker);
        clickerLabel.setForeground(LABEL_COLOR);
        ImageIcon clickerIcon=new ImageIcon("Images/clicker.jpg");
        //The label's font is set to the variable FONT.
        clickerLabel.setFont(FONT);
        increaseClickerButton = new JButton(clickerIcon);
        /**The button's foreground color is set to the variable BUTTON_COLOR.
         * Due to this, the button's text is the same as the color of BUTTON_COLOR.
         * BUTTON_COLOR is a red color, so the button's text is red.
         */
        increaseClickerButton.setForeground(BUTTON_COLOR);
        //By putting the increaseClickerButton into the first parameter of the function, this button's background aspects will be hidden.
        transparentBackground(increaseClickerButton);
        //The button's font is set to the variable FONT.
        increaseClickerButton.setFont(FONT);
        increaseClickerButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                increaseClicker();
            }


            private void increaseClicker() {
                /** This label is used to display a message to the user in the JOptionPane.
                 * The label's font is set to the variable FONT and the text color, or foreground color, of the label is set to the 
                 * variable LABEL_COLOR. 
                 */
                JLabel notificationLabel=new JLabel("");
                notificationLabel.setFont(FONT);
                notificationLabel.setForeground(LABEL_COLOR);
                if(cookies >= clickerPrice) {
                    clicker++;
                    cookies -= clickerPrice;
                    clickerPrice *= 2;
                    //The label's text is set to "You have improved your clicker!".
                    notificationLabel.setText("You have improved your clicker!");
                    /**The JOptionPane displays the notificationLabel in a plain message format. 
                     *The title of the window is Notification and an image of a notification is shown.
                     */
                    JOptionPane.showMessageDialog(null, notificationLabel, "Notification", JOptionPane.PLAIN_MESSAGE, notification);
                } else {
                     //The label's text is set to "You do not have enough money!".
                    notificationLabel.setText("You do not have enough money!");
                    /**The JOptionPane displays the notificationLabel in a plain message format. 
                     *The title of the window is Notification and an image of a notification is shown.
                     */
                    JOptionPane.showMessageDialog(null, notificationLabel, "Notification", JOptionPane.PLAIN_MESSAGE, notification);

                }
            }
        });

        java.util.Timer actualizeProgress = new java.util.Timer();
        actualizeProgress.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                cookieLabel.setText("Cookies: " + cookies);
                clickerLabel.setText("Clicker Level: " + clicker);
                increaseClickerButton.setText("Improve Clicker (Costs: " + clickerPrice + ")");
            }
        }, 0, 25);

        java.util.Timer getMoreBuildings = new java.util.Timer(); 
        getMoreBuildings.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                if (bakeryUnlocked == false && clicker >= 2) {
                    bakery.unlock();
                    bakeryUnlocked = true;
                }
                if (robotUnlocked == false && bakery.getLevel() >= 2) {
                    robot.unlock();
                    robotUnlocked = true;
                }         
                if (factoryUnlocked == false && robot.getLevel() >= 2) {
                    factory.unlock();
                    factoryUnlocked = true;
                }
            }
        }, 0, 2000);

        java.util.Timer produceWithBuildings = new java.util.Timer();
        produceWithBuildings.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                cookies += bakery.getProductionRate() + robot.getProductionRate() + factory.getProductionRate();
            }
        }, 0, 1000);

        container.add(cookieLabel);
        container.add(increaseCookiesButton);
        container.add(new JLabel("")); // blank label
        container.add(clickerLabel);
        container.add(increaseClickerButton);
    }

    public class Building {
        // non graphical variables
        private String name;
        private int level;
        private int productionRate;
        private int costs;

        // graphical variables
        JLabel label;
        JButton button;
        ImageIcon icon; //This icon is applied to the button


       //The parameter iconName is equal to the location of an image in the files.
        public Building(String name, int level, int productionRate, int costs, String iconName) {
            // non graphical variables
            this.name = name;
            this.level = level;
            this.productionRate = productionRate;
            this.costs = costs;

            // graphical variables
            label = new JLabel();
            icon=new ImageIcon(iconName); 
            button = new JButton(icon); //The button displays an icon image

            /** Here, the label's and button's fonts are both set to
             * the variable FONT. The button's color is set to BUTTON_COLOR
             * and the label's color is set to LABEL_COLOR.
             */ 
            label.setForeground(LABEL_COLOR); 
            button.setForeground(BUTTON_COLOR);
            label.setFont(FONT);
            button.setFont(FONT);

            button.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    improve();
                }
            });
       /**By adding the variable button as an argument for transparentBackground,
        * the background aspects of the button are hidden.
        */
            transparentBackground(button);
        }

        public int getLevel() {
            return level;
        }

        public void unlock() {
            numberOfColumns += 3;
            container.setLayout(new GridLayout(numberOfColumns, 1));
            container.add(new JLabel(""));
            container.add(label);
            container.add(button);
            setSize(screenWidth, getHeight() + 120);
            actualize();
        }

        public void improve() {
            /** This label is used to display a message to the user in the JOptionPane.
             * The label's font is set to the variable FONT and the text color, or foreground color, of the label is set to the 
             * variable LABEL_COLOR. 
             */
            JLabel notificationLabel=new JLabel("");
            notificationLabel.setFont(FONT);
            notificationLabel.setForeground(LABEL_COLOR);
            if(cookies >= costs) {
                level++;
                cookies -= costs;
                costs *= 2;
                //The label's text is changed to tell the user what has been improved.
                notificationLabel.setText("You have improved the " + name + "!");
                /**The JOptionPane displays the notificationLabel in a plain message format. 
                 *The title of the window is Notification and an image of a notification is shown.
                 */
                JOptionPane.showMessageDialog(null, notificationLabel, "Notification", JOptionPane.PLAIN_MESSAGE, notification);
            } else {
                //The label's text is changed to tell the user what has been improved.
                notificationLabel.setText("You have not enough money!");
                /**The JOptionPane displays the notificationLabel in a plain message format. 
                 *The title of the window is Notification and an image of a notification is shown.
                 */
                JOptionPane.showMessageDialog(null, notificationLabel, "Notification", JOptionPane.PLAIN_MESSAGE, notification);
            }
            actualize();
        }

        public int getProductionRate() {
            return productionRate * level;
        }

        public void actualize() {
            label.setText(name + " Prod. Rate: " + getProductionRate());
            button.setText("Improve (costs: " + costs + ")");
        }
    }

    public static void main(String[] args) {
        CookieClicker cookieClicker = new CookieClicker();
        cookieClicker.setTitle("Cookie Clicker");
        /**The width of the JFrame is set to screenWidth.
         * The height of the JFrame is set to screenHeight.
         */
        cookieClicker.setSize(screenWidth, screenHeight);
        //The background of the JFrame is set to the variable WINDOW_COLOR
        cookieClicker.setBackground(WINDOW_COLOR);
        cookieClicker.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        cookieClicker.setVisible(true);
        //Resizable is set to false so the user is prevented from changing the size of the JFrame.
        cookieClicker.setResizable(false);
        //This function is called to customize UI related to panes.
        optionPaneCustomization();
        //This function is called to play the music of the game.
        music();
    }

    //This function takes in a button as a parameter and makes the background hidden.
    public static void transparentBackground(JButton button) {
        button.setBorderPainted(false); //The border of the button is not painted.
    }

    //This function customizes UI related to panes.
    public static void optionPaneCustomization() { 
        //The background color of JOptionPanes are set to the variable WINDOW_COLOR.
        UIManager.put("OptionPane.background", WINDOW_COLOR);
        //The background color of JPanels are set to the variable WINDOW_COLOR.
        UIManager.put("Panel.background", WINDOW_COLOR);

    }

    //This function is used to play the music in the game.
    public static void music() { 
        /**The try and catch method is used to avoid compiler errors.
         * In the try method, the program tries to play music.
         */
        try {
            /**The variable audio is set to a .wav file. 
             * The .wav file is accessed so that this music file can be played.
            */
            AudioInputStream audio=AudioSystem.getAudioInputStream(new File("Audio/CookieClicker.wav")); 
            Clip soundClip=AudioSystem.getClip();
            soundClip.open(audio);
            //The music will loop endlessly until the program stops
            soundClip.loop(Clip.LOOP_CONTINUOUSLY); 
        } 
        /**In the catch method, the function audioError is called.
         * This function is only called when there has been an error
         * with playing the audio.
         */
         catch (UnsupportedAudioFileException e) {
            audioError();
        } 
         catch (IOException e) {
            audioError();
        } 
         catch (LineUnavailableException e) {
           audioError();
        }


    }
    /**In this function, a pop-up displays if an error with the audio in the game occurred.
     * The pop-up informs the user that an error with the audio in the game occurred.  
     */
    public static void audioError() {
        ImageIcon errorIcon=new ImageIcon("Images/error.jpg"); //This variable stores an error icon, which is a .jpg.
        /** This label is used to display a message to the user in the JOptionPane.
         * The label's font is set to the variable FONT and the text color, or foreground color, of the label is set to the 
         * variable LABEL_COLOR. 
         */
        JLabel notificationLabel=new JLabel("");
        notificationLabel.setFont(FONT);
        notificationLabel.setForeground(LABEL_COLOR);
        notificationLabel.setText("There has been an error with the audio!");
        /**The JOptionPane displays the notificationLabel in a plain message format. 
          *The title of the window is Error and an image of an error is shown.
          */
        JOptionPane.showMessageDialog(null, notificationLabel, "Error!", JOptionPane.PLAIN_MESSAGE, errorIcon);
    }
}

Edit: What I added to the original code is images for the buttons, stylized colors for all the elements in the GUI, and music. I also made other smaller edits as well.

\$\endgroup\$
  • 1
    \$\begingroup\$ While you did add comments, this doesn't really review the code. Please explain your reasoning (why it is better than the original) so that the author and other readers can learn from your thought process. Please read Why are alternative solutions not welcome? \$\endgroup\$ – Sᴀᴍ Onᴇᴌᴀ Aug 22 at 23:04
  • 1
    \$\begingroup\$ I added an edit explaining what major changes I made to the code @SᴀᴍOnᴇᴌᴀ \$\endgroup\$ – D-Studios Aug 22 at 23:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.