Please follow me here!

Tuesday, June 11, 2013

JAVA Program: Circle Cannon Shooter (Shooting Game, Experimental Program on Graphics and Event Listeners)

Hello, everyone.


This is the last blog I will write that contains code I wrote myself but from one of my three JAVA courses that is shared to one of the communities on Google+ with over 37,000 members. 
Afterwards, any JAVA blog shared to that community will contain code from my own time at this time around instead of from what I had to work on for school.

This is a program where it was an experiment with GUI and event listeners; it was not mandatory for the assignment, but I actually decided to take the time to do it because I wanted to get the extra mark for showing at least the functionality of the program. It took me a full day and another four hours to develop this; even then, I did had some bugs I wanted to fix, but I knew I ran out of time, so I labelled the project "incomplete."

However, it doesn't necessarily mean that I can't show off what I did accomplish from this program as a Blogger post, so that's what I'm doing.

I'm going to show you guys a bunch of photos to show you how the program works, then I'll show you the source code to see the mechanics of the program as well as you having the ability to use it for a JAVA source code file of your own that you can compile and play yourself. (Take note that after the code, there are a few notes on the various problems of the code as well as what the general public reaction would be for anyone who loves 2D games.)

Anyway, here are the photos:

This is what you see in the window on startup.

The Help dialog box tells you what to do in the game.
 
The About dialog box tells you the general information of the program, and who did what in it.

This is the dialog box you see when you click on the Pause menu button and the game is not running.
Game menu from the menu bar.
Info menu from the menu bar.
Confirmation dialog for starting a new game as a game is in progress. There is a graphical bug shown: the bottom of the black line you see is not touching the bottom of the canvas as it is supposed to be.
Countdown sequence; notice the panel above the panel that says "Limit: 60.00." You need to hit as many balloons as you can in one minute!
The black dot you see is your cannonball you're trying to hit the red balloon at. Hit it, and you get 500 points!
Another graphical bug: instead of the balloon having every single part of it outside the area where the cannon lies and where the cannon can rotate, the balloon actually sits on top of it and therefore when you fire a cannonball, you won't be able to see the 'flight' animation; the balloon instantly pops.
The message at the top of the window is what you see when you're almost out of time.
The Status bar displays "Your time is up!" when the time limit is at zero.
The Status bar has its background to green and foreground to black when it's time to start firing. And notice, again, the balloon is overlapping with the cannon area instead of being separated or not together.
This is where you see the debris of the balloon when you pop it.
At the end of the game, you see your final score and accuracy.
Game Over, Player


Alright, let me share with you the code for this program. Unlike all the other code you've seen in my blog, this one contains more comments that it is somewhat closer to being a professional programmer where you have to label all the important components such as event listeners and what do they do in the game, but it isn't complete. The most important details I would want to put up as an independent programmer is details of when the programming began, or when the entire project began, testing, as well as other information in the stages of computer game development.

Now, any comments you will see that are not part of the code itself will be recognized by a different font family instead of the one I'm using, which is called FinkHeavy. I will display additional comments in Super Mario Script 3.


Below is a block of comments I usually putted in my programming assignments in my course. This one is for Assignment 8 out of 13. I was assigned in my latest JAVA course at my college.

/*
 *     Data Structures and Object-Oriented Programming - Assignment 8
 *     BONUS QUESTION (Based on Chapters 16 and 17)
 *
 *     Circle Cannon Shooter (Incomplete)
 *
 *     Programmed by Gregory Desrosiers
 *
 *     Start Date: April 4, 2013
 *     End Date: April 6, 2013
 *
 *     File Name: CircleCannonShooter.java
 *     Teacher: Amin Ranj Bar
 *
 *     Made in Montreal, Canada
 *     Programmed in Java, Version 6
 *
 *     Java © Oracle Corporation.
 *
 *     © 2013 Gregory Desrosiers / Champlain College Saint-Lambert
 *     ALL RIGHTS RESERVED.
 *
 *     Modification Date: April 8, 2013
*/


Below is a comment block that is supposed to present technical details of the program such as how many JAVA resource packages are imported for use that need to be copied and pasted in a special folder when the entire game is packaged for upload.

/*
 *     TECHNICAL DETAILS
 *    
 *     # of packages imported: 6
 *     Source Code Size:
 *     Compiled File Size:
 *
 */

// Resource Packages
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.text.DecimalFormat;
import java.util.Random;
import java.util.ArrayList;



The application is a Swing-based GUI application, so we let the program itself extend the capabilities the JFrame class have for us.

// Program Definition
public class CircleCannonShooter extends JFrame
{
      
       // ID Number
       private static final long serialVersionUID = 4317684775107426687L;

       /*
        * GUI Window Components
        */
      
       // System Panels
       private JPanel mainPanel;
       private JPanel topPanel;
       private GamePanel gamePanel; // Middle Panel
       private JPanel bottomPanel;
      
       // Toolbar Menu Components
       private JMenuBar toolBarMenu;
       private JMenu gameMenu;
       private JMenu infoMenu;
      
       /**
        * Game Menu Components
        */
       private JMenuItem newGameButton;
       private JMenuItem pauseButton;
       private JSeparator separatorBar;
       private JMenuItem quitButton;
      
       /**
        *     Info Menu Components
        */
      
       private JMenuItem helpButton;
       private JMenuItem aboutButton;
      
       // Top Panel Components
       private JPanel statusContainer;
      
       private JLabel statusLabel;
       private JLabel timeRemainingLabel;
      
       // Bottom Panel Components
       private JLabel scoreLabel;
      
       private Font labelFont = new Font("Consolas", Font.PLAIN, 12);
       private Font menuFont = new Font("Times New Roman", Font.PLAIN, 12);
       private Font gameOverFont = new Font("Consolas", Font.PLAIN, 12);
      
       /*
        * Game Settings & Components
        */
      
       // Game Timer
       private Timer timer;
       private final byte timerDelay = 10;
      
       private DecimalFormat timeFormatter = new DecimalFormat("00");
      
       private byte timeRemainingSeconds;
       private byte timeRemainingMilliseconds;
       private final byte maxMilliseconds = 99;
       private final byte minMilliseconds = 0;
      
       private final byte maxSeconds = 60;
      
"highScore" was meant to be a variable where if the user has his score higher than that at the end of the game, the variable's value would be replaced with the player's score.

       // Scores
       private int score;
       private int highScore = 5000;
      
       private int numberOfBalloonsHit;
       private int numberOfShotsMade;
      
       // Countdown to Starting Game
       private Timer startTimer;
       private final int startTimerDelay = 1;
       private final int initialMilliSecondsToStart = 5000;
       private int milliSecondsToStart;
      
       // Time Over Mechanics
       private Timer endTimer;
       private final byte endTimerDelay = 100;
       private int secondsSinceStartOfEndTimer = 0;
      
       // Game Panel Components & Mechanics
       private Color colorOfCannon = Color.BLACK;
       private Color colorOfPlayerCannonball = Color.BLACK;
       private Color colorOfBalloon = Color.RED;
      
       private Graphics cannonGraphicsDevice;
       private Graphics cannonBallGraphicsDevice;
       private Graphics balloonGraphicsDevice;
       private Graphics debrisGraphicsDevice;
      
       /**
        * Cannon Properties
        */
       private final byte cannonLength = 100;
      
       private int endX;
       private int endY;
      
       private double initialAngle = 0.0; // in radians
       private final double maxPositiveInitialAngle = (5 * Math.PI) / 12;
       // 75 degrees to the left of the perpendicular of the cannon's base.
       private final double maxNegativeInitialAngle = (-5 * Math.PI) / 12;
       // 75 degrees to the right of the perpendicular of the cannon's base.
       private double angleDuringShoot;
      
       private static ArrayList<Double> listOfAnglesForCannon;
       private static ArrayList<Integer> listOfXValuesForEdgeOfCannon;
       private static ArrayList<Integer> listOfYValuesForEdgeOfCannon;
      
      
       /**
        * Balloon Properties
        */
       private int balloonX;
       private int balloonY;
       private int balloonWidth;
       private int balloonHeight;
      
       private Random balloonGenerator;
      
       private int balloonDebrisX;
       private int balloonDebrisY;
      
       /**
        * Cannonball Properties
        */
      
       private Timer cannonBallTimer;
       private Timer balloonHitTimer;
       private int cannonBallX;
       private int cannonBallY;
       private final int cannonBallWidth = 10;
       private final int cannonBallHeight = 10;
       private int timeSinceBalloonHit = 0;
      
       private String[] hitMessages = {"BOOM!", "BINGO!", "Bullseye!", "Keep up the pace!", "That's one balloon down!", "Hit!", "A balloon has been popped!", "One stricken balloon!", "Balloon popped!",
                     "That's one for how many balloons you've popped!", "Points added to your score for popping!", "Ha! That's one balloon struck!", "Ball hits the balloon successfully!",
                     "Move your cannon around quickly for that next shot!", "Good shot!", "Keep it up!", "Alright, you got a balloon down!", "You got one!", "You have blown a balloon!", "Kaboom!", "BAM!",
                     "Keep going, you still have time!", "No limits on ammo; just keep shooting!", "You got a +1 on your Hit Balloon Count!", "Cannon ball intercepted the balloon!", "Kablooey", "Down!",
                     "Give it all you got!", "DESTORYED", "The balloon's going down!", "Show off your shooting!"};
       private Random messageSelector;
       private int messageIndex;
      
       private final byte defaultMaxNumberOfShots = 1;
       private byte numberOfShots = 0;
      
       // Boolean Flags
       private boolean gameOnMode = false;
       private boolean gameOnPause = false;
       private boolean gameNotRunning = true;
       private boolean balloonIsPainted = false;

       private String currentStatus = "";
      
       // GUI Builder
       public CircleCannonShooter()
       {
             
What we're simply doing here in this constructor is building the application; getting all the GUI components in place, including the menus, and then adding event listeners.

              /*
               * GUI Components Construction
               */
             
              // Panel Builds
              mainPanel = new JPanel();
              mainPanel.setLayout(new BorderLayout());
             
              topPanel = new JPanel();
              gamePanel = new GamePanel();
              bottomPanel = new JPanel();
             
             
              // Top Panel build
              topPanel.setLayout(new BorderLayout());
              topPanel.setBackground(Color.LIGHT_GRAY);
             
              statusContainer = new JPanel();
              statusContainer.setLayout(new BorderLayout());
              statusContainer.setBackground(Color.LIGHT_GRAY);
             
              statusLabel = new JLabel("   ");
              statusLabel.setHorizontalAlignment(SwingConstants.CENTER);
              statusLabel.setFont(labelFont);
              statusLabel.setBackground(Color.GRAY);
             
              timeRemainingLabel = new JLabel("Limit : " + timeRemainingSeconds + "." + timeFormatter.format(timeRemainingMilliseconds));
              timeRemainingLabel.setHorizontalAlignment(SwingConstants.CENTER);
              timeRemainingLabel.setFont(labelFont);
             
              statusContainer.add(statusLabel);
              topPanel.add(statusContainer, BorderLayout.NORTH);
              topPanel.add(timeRemainingLabel);
             
              // Game Panel Build
              gamePanel.addKeyListener(new KeyboardListener());
              gamePanel.setBorder(new javax.swing.border.LineBorder(Color.BLUE));
             
              // Bottom Panel build
              scoreLabel = new JLabel("Score: " + score + " Balloons Hit: " + numberOfBalloonsHit);
              scoreLabel.setFont(labelFont);
              bottomPanel.add(scoreLabel);
             
              // Main Panel Assembly
              mainPanel.add(topPanel, BorderLayout.NORTH);
              mainPanel.add(gamePanel);
              mainPanel.add(bottomPanel, BorderLayout.SOUTH);
             
              // Toolbar Menu build
              toolBarMenu = new JMenuBar();
              gameMenu = new JMenu("Game");
              infoMenu = new JMenu("Info");
             
              /*
               * Game Menu Build
               */
              newGameButton = new JMenuItem("New Game");
              pauseButton = new JMenuItem("Pause");
              separatorBar = new JSeparator();
              quitButton = new JMenuItem("Quit");
             
              newGameButton.addActionListener(new MenuBarListener());
              pauseButton.addActionListener(new MenuBarListener());
              quitButton.addActionListener(new MenuBarListener());
             
              newGameButton.setFont(menuFont);
              pauseButton.setFont(menuFont);
              quitButton.setFont(menuFont);
             
              newGameButton.setForeground(Color.RED);
              pauseButton.setForeground(Color.RED);
              separatorBar.setForeground(Color.BLUE);
              quitButton.setForeground(Color.RED);
             
              gameMenu.add(newGameButton);
              gameMenu.add(pauseButton);
              gameMenu.add(separatorBar);
              gameMenu.add(quitButton);
             
             
              /*
               * Info Menu Build
               */
             
              helpButton = new JMenuItem("Help");
              aboutButton = new JMenuItem("About");
             
              helpButton.addActionListener(new MenuBarListener());
              aboutButton.addActionListener(new MenuBarListener());
             
              helpButton.setFont(menuFont);
              aboutButton.setFont(menuFont);
             
              helpButton.setForeground(Color.RED);
              aboutButton.setForeground(Color.RED);
             
              infoMenu.add(helpButton);
              infoMenu.add(aboutButton);
             
              toolBarMenu.add(gameMenu);
              toolBarMenu.add(infoMenu);
             
              toolBarMenu.setBackground(Color.BLACK);
             
              add(mainPanel);
             
              gamePanel.setFocusable(true);
              gamePanel.requestFocusInWindow();
             
              /*
               * Game Core Setup
               */
              timer = new Timer(timerDelay, new TimerListener());
              startTimer = new Timer(startTimerDelay, new TimerListener());
              endTimer = new Timer(endTimerDelay, new TimerListener());
              cannonBallTimer = new Timer(20, new TimerListener());
              balloonHitTimer = new Timer(10, new TimerListener());
             
              balloonGenerator = new Random();
              messageSelector = new Random();
             
              gamePanel.createListOfAngles();
              gamePanel.createCannonBoundariesOfX();
              gamePanel.createCannonBoundariesOfY();
             
              // GUI Settings
             
              setJMenuBar(toolBarMenu);
              setTitle("Circle Cannon Shooter");
              setDefaultCloseOperation(EXIT_ON_CLOSE);
              setLocationRelativeTo(null);
              setSize(800, 600);
              setResizable(false);
              setVisible(true);
       }
      
       // Central Method (no arguments passed)
       public static void main(String[] args)
       {


 This is where the program will build itself up once it starts running. Also, I am setting the look and feel to the Windows theme, making sure that I put in a  try-and-catch clause in case the setLookAndFeel throws an exception saying it failed to load the theme up or ran into problems with setting it, especially with the string literal passed.

              try
              {
                     UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
              }
              catch(Exception e)
              {
                    
              }
             
              new CircleCannonShooter();
             
       }
      
This is how you see the interactive panel. It's an extended JPanel modified so that way it can display all the AWT-based graphics the game requires. This is the most complicated place to write the code because all the necessary graphical elements to be painted are based on timing and logic.

I did not use any arrays here, but considering that any computer games would be using graphical objects for everything you see, what I'm thinking is, the next time I design a game like this, one of the important things I have to do is utilize data structures to store all the important graphical objects there, and find ways to display certain shapes at all circumstances. If you have 10 motorcycles in a computer game, let's say, that each one moves on its own differently, then you have to create 10 "motorcycle" graphical objects.

       // Graphics Panel
       private class GamePanel extends JPanel
       {
              // Panel ID Number
              private static final long serialVersionUID = 310112728276495178L;

              public GamePanel()
              {
                     setBackground(Color.WHITE);
              }
              protected void paintComponent(Graphics g)
              {
                     super.paintComponent(g);
                     g.setColor(Color.WHITE);
                    
                     if (cannonGraphicsDevice == null && cannonBallGraphicsDevice == null && balloonGraphicsDevice == null)
                     {
                           cannonGraphicsDevice = g.create();
                           cannonBallGraphicsDevice = g.create();
                           balloonGraphicsDevice = g.create();
                           debrisGraphicsDevice = g.create();
                          
                           cannonGraphicsDevice.setColor(colorOfCannon);
                           cannonBallGraphicsDevice.setColor(colorOfPlayerCannonball);
                           balloonGraphicsDevice.setColor(colorOfBalloon);
                           debrisGraphicsDevice.setColor(colorOfCannon);
                     }
                    
                     if (startTimer.isRunning() || timer.isRunning() || endTimer.isRunning())
                     {
                           endX = calculateEndX();
                           endY = calculateEndY();
                          
                           cannonGraphicsDevice.drawLine(400, 447, endX, endY);
                     }
                     if (timer.isRunning())
                     {
                           if (!balloonIsPainted)
                           {
                                  do
                                  {
                                         balloonX = balloonGenerateX();
                                         balloonY = balloonGenerateY();
                          
                                         do
                                         {
                                                balloonWidth = balloonGenerateWidthAndHeight();
                                         }
                                         while (balloonWidth < 30);
                                        
                                         balloonHeight = balloonWidth;
                                  }     
                                  while(isBalloonInAreaOfCannon(balloonX, balloonY));
                                  balloonIsPainted = true;
                           }
                          
                           if(numberOfShots == 1)
                           {
                                  cannonBallGraphicsDevice.fillOval(cannonBallX, cannonBallY, cannonBallWidth, cannonBallHeight);
                                 
                                  if(cannonBallHitsBalloon())
                                  {     
                                         balloonIsPainted = false;
                                        
                                         balloonDebrisX = balloonX;
                                         balloonDebrisY = balloonY;
                                        
                                         timeSinceBalloonHit = 0;
                                         balloonHitTimer.start();
                                        
                                         balloonGraphicsDevice.clearRect(balloonX, balloonY, balloonWidth, balloonHeight);
                                        
                                         score += 500;
                                         numberOfBalloonsHit++;
                                        
                                         numberOfShots--;
                                         cannonBallTimer.stop();
                                        
                                        
                                  }
                           }
                          
                           if (timeSinceBalloonHit < 200)
                           {
                                 
                                  if (balloonDebrisX < 400 && balloonDebrisY > 100)
                                  {
                                         debrisGraphicsDevice.drawOval(balloonDebrisX, balloonDebrisY - 10, 10, 10);
                                         debrisGraphicsDevice.drawOval(balloonDebrisX + 10, balloonDebrisY, 10, 10);
                                         debrisGraphicsDevice.drawOval(balloonDebrisX, balloonDebrisY + 10, 10, 10);
                                  }
                                  else if (balloonDebrisX < 400 && balloonDebrisY > 50)
                                  {
                                         debrisGraphicsDevice.drawOval(balloonDebrisX + 10, (balloonDebrisY + 10), 10, 10);
                                         debrisGraphicsDevice.drawOval(balloonDebrisX + 10, balloonDebrisY, 10, 10);
                                         debrisGraphicsDevice.drawOval(balloonDebrisX, (balloonDebrisY + 10), 10, 10);
                                  }
                                  else if (balloonDebrisX > 400 && balloonDebrisY > 100)
                                  {
                                         debrisGraphicsDevice.drawOval(balloonDebrisX, balloonDebrisY - 10, 10, 10);
                                         debrisGraphicsDevice.drawOval(balloonDebrisX - 10, balloonDebrisY, 10, 10);
                                         debrisGraphicsDevice.drawOval(balloonDebrisX, balloonDebrisY + 10, 10, 10);
                                  }
                                  else if (balloonDebrisX > 400 && balloonDebrisY > 50)
                                  {
                                         debrisGraphicsDevice.drawOval(balloonDebrisX - 10, (balloonDebrisY - 10), 10, 10);
                                         debrisGraphicsDevice.drawOval(balloonDebrisX - 10, balloonDebrisY, 10, 10);
                                         debrisGraphicsDevice.drawOval(balloonDebrisX, (balloonDebrisY - 10), 10, 10);
                                  }
                                  else if (balloonDebrisX == 400 && balloonDebrisY > 50)
                                  {
                                         debrisGraphicsDevice.drawOval(balloonDebrisX, (balloonDebrisY + 10), 10, 10);
                                         debrisGraphicsDevice.drawOval(balloonDebrisX, (balloonDebrisY - 10), 10, 10);
                                  }
                                  else
                                         debrisGraphicsDevice.drawOval(balloonDebrisX, (balloonDebrisY + 10), 10, 10);
                                 
                                  repaint();
                           }
                           else
                           {
                                  debrisGraphicsDevice.clearRect(balloonDebrisX, balloonDebrisY, 10, 10);
                                  repaint();
                           }
                          
                           balloonGraphicsDevice.fillOval(balloonX, balloonY, balloonWidth, balloonHeight);
                     }
                    
                     scoreLabel.setText("Score: " + score + " Balloons Hit: " + numberOfBalloonsHit);
              }
             
              private void initializeGame()
              {
                     gamePanel.setFocusable(true);
                    
                     milliSecondsToStart = initialMilliSecondsToStart;
                    
                     numberOfBalloonsHit = 0;
                     numberOfShotsMade = 0;
                     numberOfShots = 0;
                    
                     timeRemainingSeconds = maxSeconds;
                     timeRemainingMilliseconds = minMilliseconds;
                     initialAngle = 0.0;
                     startTimer.start();
              }
             
              private void fire()
              {
                     if (numberOfShots != defaultMaxNumberOfShots)
                     {
                           numberOfShots++;
                            numberOfShotsMade++;
                    
                           angleDuringShoot = initialAngle;
                    
                           cannonBallX = endX - (cannonBallWidth / 2);
                           cannonBallY = endY - (cannonBallWidth / 2);
                    
                           cannonBallTimer.start();
                     }
              }
             
              private boolean cannonBallHitsBalloon()
              {
                     return (((cannonBallX > balloonX && cannonBallX < balloonX + balloonWidth) || (cannonBallX + cannonBallWidth > balloonX && cannonBallX + cannonBallWidth < balloonX + balloonWidth))
                                  &&
                                  ((cannonBallY > balloonY && cannonBallY < balloonY + balloonWidth) || (cannonBallY + cannonBallWidth > balloonY && cannonBallY + cannonBallWidth < balloonY + balloonWidth)));
              }
             
              private int calculateEndX()
              {
                     return (int)(400 - (Math.sin(initialAngle) * cannonLength));
              }
             
              private int calculateEndY()
              {
                     return (int)(447 - Math.cos(initialAngle) * cannonLength);
              }
             
              private int balloonGenerateX()
              {
                     return balloonGenerator.nextInt(600);
              }
             
              private int balloonGenerateY()
              {
                     return balloonGenerator.nextInt(347);
              }
             
              private int balloonGenerateWidthAndHeight()
              {
                     return balloonGenerator.nextInt(50);
              }
             
This inadequate method is supposed to check whether or not the balloon to be displayed is inside the area of the cannon by going through all possible angles of the cannon and where the end point would be, then using the coordinates of the balloon, check to see if the balloon is inside the cannon area.

              private boolean isBalloonInAreaOfCannon(int balloonX, int balloonY)
              {
                     boolean balloonIsInCannon = false;
                    
                     for (double x = maxPositiveInitialAngle; x <= maxNegativeInitialAngle; x -= (Math.PI / 90))
                     {
                           if (x > 0.0)
                           {
                                  if (((balloonX + balloonWidth) >= listOfXValuesForEdgeOfCannon.get(listOfAnglesForCannon.indexOf(x))
                                                &&
                                                (balloonY + balloonWidth) > listOfYValuesForEdgeOfCannon.get(listOfAnglesForCannon.indexOf(x))))
                                         balloonIsInCannon = true;
                           }
                           else
                           {
                                  if (((balloonX) <= listOfXValuesForEdgeOfCannon.get(listOfAnglesForCannon.indexOf(x))
                                                &&
                                                (balloonY + balloonWidth) > listOfYValuesForEdgeOfCannon.get(listOfAnglesForCannon.indexOf(x))))
                                         balloonIsInCannon = true;
                           }
                     }
                    
                     return balloonIsInCannon;
              }
             
              private void createListOfAngles()
              {
                     listOfAnglesForCannon = new ArrayList<Double>();
                    
                     for (double x = maxPositiveInitialAngle; x >= maxNegativeInitialAngle; x -= (Math.PI / 90))
                     {
                           listOfAnglesForCannon.add(x);
                     }
                    
              }
             
              private void createCannonBoundariesOfX()
              {
                     listOfXValuesForEdgeOfCannon = new ArrayList<Integer>();
                    
                     for (double x = maxPositiveInitialAngle; x >= maxNegativeInitialAngle; x -= (Math.PI / 90))
                     {
                           listOfXValuesForEdgeOfCannon.add((int)(400 - (Math.sin(x) * cannonLength)));
                     }
                    
              }
             
              private void createCannonBoundariesOfY()
              {
                     listOfYValuesForEdgeOfCannon = new ArrayList<Integer>();
                    
                     for (double x = maxPositiveInitialAngle; x >= maxNegativeInitialAngle; x -= (Math.PI / 36))
                     {            
                           listOfYValuesForEdgeOfCannon.add((int)(447 - Math.cos(initialAngle) * cannonLength));
                     }
              }
       }
      
This is the universal timing class; it's an event listener class that handles events fired by Timer objects and performs the appropriate steps for manipulation, interaction, and instant updating.

       // Timing Mechanism
       private class TimerListener implements ActionListener
       {
              public void actionPerformed(ActionEvent f)
              {
                     gamePanel.setFocusable(true);
                    
This block of if statements is for the countdown timer before the actual gameplay begins.

                     if (f.getSource() == startTimer)
                     {
                          
                          
                           if (milliSecondsToStart == 5000)
                           {
                                  helpButton.setEnabled(false);
                                  aboutButton.setEnabled(false);
                           }
                           else if (milliSecondsToStart > 3000)
                           {
                                  gamePanel.repaint();
                                  timeRemainingLabel.setText("Limit : " + timeRemainingSeconds + "." + timeFormatter.format(timeRemainingMilliseconds));
                                 
                                   statusContainer.setBackground(Color.BLACK);
                                  statusLabel.setForeground(Color.WHITE);
                                 
                                  currentStatus = "GET READY";
                           }
                           else if (milliSecondsToStart >= 2000)
                           {
                                  statusContainer.setBackground(Color.RED);
                                  statusLabel.setForeground(Color.WHITE);
                                 
                                  currentStatus = "3";
                           }
                           else if (milliSecondsToStart >= 1000)
                           {
                                  statusContainer.setBackground(Color.ORANGE);
                                  statusLabel.setForeground(Color.BLACK);
                                 
                                  currentStatus = "2";
                           }
                           else if (milliSecondsToStart >= 0)
                           {
                                  statusContainer.setBackground(Color.YELLOW);
                                  statusLabel.setForeground(Color.BLACK);
                                 
                                  currentStatus = "1";
                           }
                           else if (milliSecondsToStart <= 0 && milliSecondsToStart > -2000)
                           {
                                  statusContainer.setBackground(Color.GREEN);
                                  statusLabel.setForeground(Color.BLACK);
                                 
                                  currentStatus = "GO!";
                                 
                                  if (milliSecondsToStart == 0)
                                  {
                                        
                                         gamePanel.repaint();
                                         gamePanel.setFocusable(true);
                                         gamePanel.requestFocusInWindow();
                                        
                                  }
                                  timer.start();
                                 
                                 
                           }
                           else
                           {
                                  startTimer.stop();
                                  statusLabel.setFont(labelFont);
                                  statusContainer.setBackground(Color.LIGHT_GRAY);
                                 
                                  currentStatus = " ";
                           }
                          
                          
                           milliSecondsToStart -= 10;
                     }

This is what happens when the game is in progress, after the countdown phrase is over.

                     else if (f.getSource() == timer)
                     {
                          
                          
                           if (timeRemainingSeconds == maxSeconds && timeRemainingMilliseconds == minMilliseconds && timer.isRunning())
                           {     
                                  gameOnMode = true;
                                 
                                  helpButton.setEnabled(true);
                                  aboutButton.setEnabled(true);
                                 
                                  gamePanel.repaint();
                           }
                          
                           if (timeRemainingSeconds == 0 && timeRemainingMilliseconds == 0)
                           {
                                  gamePanel.setFocusable(true);
                                  gameOnMode = false;
                                  timeRemainingLabel.setText("Limit : " + timeRemainingSeconds + "." + timeFormatter.format(timeRemainingMilliseconds));
                                  timer.stop();
                                  endTimer.start();
                                 
                                  statusLabel.setForeground(Color.RED);
                                  statusContainer.setBackground(Color.BLACK);
                                 
                                  currentStatus = "Your time is up!";
                           }
                           else if (timeRemainingMilliseconds == minMilliseconds)
                           {
                                  timeRemainingSeconds--;
                                  timeRemainingMilliseconds = maxMilliseconds;
                                  timeRemainingLabel.setText("Limit : " + timeRemainingSeconds + "." + timeFormatter.format(timeRemainingMilliseconds));
                           }
                           else if (timeRemainingSeconds < 5)
                           {
                                  if (timeRemainingSeconds > 3)
                                         currentStatus = "You're running out of time!";
                                 
                                  if ((timeRemainingSeconds >= 0 && (timeRemainingMilliseconds % 2) == 0))
                                  {
                                        
                                         timeRemainingLabel.setForeground(Color.RED);
                                         timeRemainingLabel.setText("Limit : " + timeRemainingSeconds + "." + timeFormatter.format(timeRemainingMilliseconds));
                                         timeRemainingMilliseconds--;
                                  }
                                  else
                                  {
                                         timeRemainingLabel.setForeground(Color.BLACK);
                                         timeRemainingLabel.setText("Limit : " + timeRemainingSeconds + "." + timeFormatter.format(timeRemainingMilliseconds));
                                         timeRemainingMilliseconds--;
                                  }
                                        
                           }
                           else
                           {
                                  timeRemainingMilliseconds--;
                                  timeRemainingLabel.setText("Limit : " + timeRemainingSeconds + "." + timeFormatter.format(timeRemainingMilliseconds));
                                 
                           }
                          
                     }

This is where the final results come in once time runs out.

                     else if (f.getSource() == endTimer)
                     {
                           helpButton.setEnabled(false);
                           aboutButton.setEnabled(false);
                          
                           secondsSinceStartOfEndTimer += endTimer.getDelay();
                          
                           if     (secondsSinceStartOfEndTimer == 3000)
                           {
                                  double accuracy;
                                 
                                  if (numberOfShotsMade == 0)
                                         accuracy = 0.0;
                                  else
                                         accuracy = (100 * ((double)numberOfBalloonsHit/(double)numberOfShotsMade));
                                  currentStatus = "Final Score: " + score + " Accuracy: " + new DecimalFormat("00").format(accuracy) + "%";
                           }
                           if (secondsSinceStartOfEndTimer == 10000)
                           {
                                  statusLabel.setFont(gameOverFont);
                                  currentStatus = "GAME OVER";
                           }
                           else if (secondsSinceStartOfEndTimer == 15000)
                           {
                                  secondsSinceStartOfEndTimer = 0;
                                  gameOnMode = false;
                                  gameNotRunning = true;
                                  endTimer.stop();
                                 
                                  currentStatus = " ";
                                  statusLabel.setFont(labelFont);
                                  statusContainer.setBackground(Color.LIGHT_GRAY);
                                 
                                  helpButton.setEnabled(true);
                                  aboutButton.setEnabled(true);
                           }
                     }
                     else if (f.getSource() == cannonBallTimer)
                     {
                           cannonBallX -= (int)(Math.sin(angleDuringShoot) * 12);
                           cannonBallY -= (int)(Math.cos(angleDuringShoot) * 12);
                          
                           if (cannonBallY <= 0 || (cannonBallX <= 0 || cannonBallX >= 784))
                           {
                                  numberOfShots--;
                                  cannonBallTimer.stop();
                           }
                          
                           gamePanel.repaint();
                     }
                     else if (f.getSource() == balloonHitTimer)
                     {
                           if (timeSinceBalloonHit == 0)
                                  messageIndex = messageSelector.nextInt(hitMessages.length);
                          
                           timeSinceBalloonHit += balloonHitTimer.getDelay();
                          
                          
                          
                           if(timeSinceBalloonHit <= 1000 && !(timeRemainingSeconds == 0 && timeRemainingMilliseconds == 0) && !(timeRemainingSeconds < 5 && timeRemainingSeconds > 3) && !startTimer.isRunning())
                           {
                                  currentStatus = hitMessages[messageIndex];
                           }
                           else
                           {
                                  currentStatus = " ";
                                  balloonHitTimer.stop();
                           }
                     }
                    
                     statusLabel.setText(currentStatus);
              }
       }
      
This is how the player is able to control his cannon and when to fire a cannonball.

       // Keyboard Button Listener
       class KeyboardListener implements KeyListener
       {
              public void keyTyped(KeyEvent g)
              {
                          
              }
             
              public void keyReleased(KeyEvent f)
              {
                    
              }
             
              public void keyPressed(KeyEvent e)
              {
                    
                     if (gameOnMode && timer.isRunning())
                     {
                           if (e.getKeyCode() == KeyEvent.VK_LEFT && initialAngle < maxPositiveInitialAngle)
                           {
                                  initialAngle += (Math.PI / 36);
                           }
                           else if (e.getKeyCode() == KeyEvent.VK_RIGHT && initialAngle > maxNegativeInitialAngle)
                           {
                                  initialAngle -= (Math.PI / 36);
                           }
                           else if (e.getKeyCode() == KeyEvent.VK_UP || e .getKeyCode() == KeyEvent.VK_SPACE)
                           {
                                  gamePanel.fire();
                           }
                    
                           gamePanel.requestFocusInWindow();
                           gamePanel.repaint();
                     }
              }
       }
      
Finally, this is how the menu bar acts in reaction to the player's request to open or perform a command.

       // Menu Bar Action Listener
       private class MenuBarListener implements ActionListener
       {
              public void actionPerformed(ActionEvent e)
              {
                     if (e.getSource() == newGameButton)
                     {
                           int answerReturned = 0;
                          
                           if (gameOnMode)
                           {
                                  gameOnPause = true;
                                 
                                  timer.stop();
                                  answerReturned = JOptionPane.showConfirmDialog(null, "Are you sure you want to start a new game? You will lose all of your progress up to this point.", "Restarting Game", JOptionPane.YES_NO_OPTION);
                           }
                          
                           if (gameNotRunning)
                           {
                                  gameNotRunning = false;
                                  gameOnMode = true;
                                  gamePanel.initializeGame();
                           }
                           else if (gameOnMode && answerReturned == JOptionPane.YES_OPTION)
                           {
                                  if (pauseButton.getText().equals("Continue"))
                                         pauseButton.setText("Pause");
                                 
                                  gamePanel.initializeGame();
                           }
                     }
                     else if (e.getSource() == pauseButton)
                     {
                           if (gameOnMode == true && milliSecondsToStart <= 0)
                           {
                                  if (gameOnPause == false)
                                  {
                                         gameOnPause = true;
                          
                                         if (timer != null && gameOnMode && gameOnPause)
                                         {
                                                if (timer.isRunning())
                                                       timer.stop();
                                         }     
                          
                                         statusLabel.setText("Game Paused");
                                         pauseButton.setText("Continue");
                                  }
                                  else
                                  {
                                         gameOnPause = false;
                                 
                                         if (timer != null && gameOnMode && !gameOnPause)
                                         {
                                                if (!timer.isRunning())
                                                       timer.start();
                                        
                                         }
                                 
                                         statusLabel.setText("   ");
                                         pauseButton.setText("Pause");
                                  }
                           }
                           else
                                  JOptionPane.showMessageDialog(null, "The game is not running right now and therefore will not pause.");
                     }
                     else if (e.getSource() == quitButton)
                     {
                           int answerReturned = 0;
                          
                           if (gameOnMode)
                           {
                                  gameOnPause = true;
                                 
                                  timer.stop();
                                  answerReturned = JOptionPane.showConfirmDialog(null, "Are you sure you want to quit? You will lose all of your progress up to this point.", "Restarting Game", JOptionPane.YES_NO_OPTION);
                           }
                          
                           if (gameNotRunning)
                           {
                                  quit();
                           }
                           else if (gameOnMode && answerReturned == JOptionPane.YES_OPTION)
                           {
                                  quit();
                           }
                     }
                     else if (e.getSource() == helpButton)
                     {
                           if (timer.isRunning())
                           {
                                         timer.stop();
                                        
                                         gameOnPause = true;
                                         statusLabel.setText("Game Paused");
                                         pauseButton.setText("Continue");
                           }
                          
                           JOptionPane.showMessageDialog(null, "Click on \"New Game\" to start a new game. Pay attention to the status bar at the top of the screen.\nAs soon as you see \"go,\" hit as many balloons as you can in 60 seconds." +
                                         "\n\nUse the left and right arrow keys to turn your cannon left and right.\nFire a cannonball with the Up Arrow Key or Space Bar. You can only fire one cannonball at a time." +
                                         "\nThe more balloons you pop, the higher your score will be.\n\nIf you need to pause your game while playing, click on the \"Pause\" button in the Game Menu." +
                                         "\n\nHave fun playing!", "Help Dialog", JOptionPane.INFORMATION_MESSAGE);
                     }
                     else if (e.getSource() == aboutButton)
                     {
                           if (timer.isRunning())
                           {
                                         timer.stop();
                                        
                                         gameOnPause = true;
                                         statusLabel.setText("Game Paused");
                                         pauseButton.setText("Continue");
                           }
             
                          
                           JOptionPane.showMessageDialog(null, "Circle Cannon Shooter\nIntroduction to JAVA Programming (by Daniel Liang): Chapter 16 - Exercise 23\n© 2013 Pearson Education Inc.\n\n" +
                                                                                 "Programmed by Gregory Desrosiers (April 4th, 2013 - April 6th, 2013)\n\nThis was a programming assignment for Amin Ranj Bar's Data Structures\n" +
                                                                                    "and Object-Oriented Programming at Champlain College Saint Lambert.\n\n© 2013 Gregory Desrosiers / Champlain College Saint Lambert.\n" +
                                                                                 "ALL RIGHTS RESERVED.", "About", JOptionPane.INFORMATION_MESSAGE);
                                                                             
                     }
                    
              }
             
              public void quit()
              {
                     JOptionPane.showMessageDialog(null, "Thank you for using the program. So long!");
                     System.exit(0);
              }
       }

This listener class is omitted because I wasn't able to create a class where the program behaves in accordance with what's going on with the mouse pointer.

       // Mouse Listener (for pausing game during gameplay)
       /*private class ProgramMouseListener extends MouseAdapter
       {
              public void mouseClicked(MouseEvent b)
              {
                    
                     System.out.println(b);
                    
                     /*if ()
                     {     
                           gameOnPause = true;
                          
                           if (timer != null && gameOnMode && gameOnPause)
                                  timer.stop();
                          
                           statusLabel.setText("Game Paused");
                           pauseButton.setText("Continue");
                          
                     }
                     else if ((b.getXOnScreen() >= 0 || b.getXOnScreen() <= 800) && (b.getYOnScreen() >= 0 || b.getYOnScreen() <= 600))
                     {
                           gameOnPause = false;
                          
                           if (timer != null && gameOnMode && !gameOnPause)
                                  timer.start();
                     }*/

}

// End of Program Definition

Now that you've seen the code, let me tell you a couple of things about the many different kinds of bugs visible from the program itself, including those you've seen from the photos themselves.

The first thing you notice is, after the game begins, when you click on the "Quit" option from the "Game" menu, we see two bugs: the bottom of the player cannon (a 1-px thick black line at the bottom of the graphics canvas) is not touching the southern border of the canvas, and the time limit is not paused; it is still running regardless if a confirmation dialog box is opened.

Second bug is the possibility of having one red balloon overlapping the tip of the cannon (on the most outer part of the black line) and not giving the cannon enough time for the player to see the movement animation.

Third bug: for the first five seconds of gameplay after you see the word "go," you cannot pause the game.

Fourth: If you click on "New Game" and select yes within the first several seconds of the game, the actual game timer will keep running even if you are at the countdown phrase; the game timer is not supposed to run until the countdown phrase is completed.

Fifth: The limited graphical techniques on the debris caused by the explosion of the balloon.

Finally, sixth: The time limit runs longer than the actual 60 seconds it's supposed to run. It's an issue with the main Timer object that controls the entire game.

You can use the code at your convenience and see the other bugs for yourself.


Here's what I think of the game in terms of public reputation. I'll split the review up into five categories and give out the rating of each, hopefully getting an average rating at the end. So, here we go.


Gameplay:
You are only limited to this one bland game of just knocking static red balloons down over and over again for a time limit, with some interface and logical bugs. You do not have a challenge mode, an endurance mode, a speedrun mode, a campaign mode, a local multiplayer mode, nor an online multiplayer mode. (1.5 / 10)

Graphics:
Bland, 2D, and repetitively boring. Only selected limited colors and no setting to change them; you can't even apply gradient, rainbow, or thematic styles. You only have red balloons, a white background, a black cannon and cannonball, and a not-so-catchy interface, though the countdown is somewhat a plus. (1 / 10)

Audio:
No sound and music. You need to play your own music from an external player and make up your own sounds. (0 / 10)

Presentation:
Not so attracting and catchy; very bland, dull, and lacks variety, clearness, and people's interest for more colors and styles. (1 / 10)

Lasting Appeal:
Nothing too special; in fact, nothing too normal as well. All it is is just an appaling experimental game with too many things to be fixed. The game is not even finished. (1 / 10)

Overall Rating: 0.9 / 10
"APPALING" (GameSpot Raters would say it 'ABYSMAL.")

Well, that does it for this blog! I hope you guys had fun checking this out and hope to be in touch with you soon for more blogs! Remember, the first thing you do for something new is not perfect, but as you practice, things will get better like what I was doing with this! Okay, I'll see you around!!

I have a whole bunch of other blogs that talk about JAVA Programming for your interest; come check them out on my blog site!


Facebook: https://www.facebook.com/pages/Gregory-Desrosiers/171954446270382?ref=hl
Twitter: http://www.twitter.com/GregoryDes
Google+: Search up "Gregory Desrosiers" in the Search Engine, then click on the profile that has a photo of me with two thumbs up.
YouTube: http://www.youtube.com/user/Bammer2001


Please read this Blogger post: http://gregorypdesrosiersmontreal.blogspot.ca/2013/07/the-asperger-computer-make-donation-to.html

Google+ Followers

Popular Posts by Gregory