Coding 6: Timer, Start Button, Labels
We will finally be implementing the timer which will count down from 30 seconds
And we will also create the start button and a label to display the score, highscore and time remaining
By now you should be familiar with creating a JLabel component.
The score label should be a global object, so we can access it from anywhere inside the Game class.
So let's define the JLabel object in global space:
private JLabel lblScore;
Now go to the initGUI() function, and initialise the lblScore object.
lblScore = new JLabel("Score: 0");
lblScore.setHorizontalAlignment(SwingConstants.TRAILING);
lblScore.setFont(new Font("Cambria", Font.BOLD, 14));
lblScore.setForeground(new Color(135, 206, 250));
lblScore.setBounds(423, 54, 144, 33);
contentPane.add(lblScore);
The code above should be pretty self-explanatory.
Let's do the exact same thing for our high-score label and time-remaining label:
private JLabel lblTimeLeft;
private JLabel lblHighscore;
Inside the initGUI() function:
lblTimeLeft = new JLabel("30");
lblTimeLeft.setHorizontalAlignment(SwingConstants.CENTER);
lblTimeLeft.setForeground(new Color(240, 128, 128));
lblTimeLeft.setFont(new Font("Cambria Math", Font.BOLD, 24));
lblTimeLeft.setBounds(232, 54, 144, 33);
contentPane.add(lblTimeLeft);
lblHighscore = new JLabel("Highscore: 0");
lblHighscore.setHorizontalAlignment(SwingConstants.TRAILING);
lblHighscore.setForeground(new Color(255, 255, 0));
lblHighscore.setFont(new Font("Cambria", Font.BOLD, 14));
lblHighscore.setBounds(433, 18, 134, 33);
contentPane.add(lblHighscore);
Notice that the argument values in setBounds is different for each object as this determines the size and location of the object in the window.
Before we carry on, let's also create the global variables that will store the highscore and time remaining:
Add the following to global space:
private int timeLeft = 30;
private int highscore = 0;
Create a JButton object
We will now create a 'Start' button, in a similar way of creating the JLabels above.
Define the JButton in global space:
private JButton btnStart;
Next, initialise the JButton object in the initGUI() function:
btnStart = new JButton("Start");
btnStart.setBackground(Color.WHITE);
btnStart.setBounds(32, 60, 110, 33);
contentPane.add(btnStart);
In order for the button to do something when it is clicked, we need to add an Action Event Listener to it
Go to the initEvents() function and add the event listener:
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
btnStart.setEnabled(false);
clearBoard();
genRandMole();
timer.start();
}
});
When the button is clicked, the following actions will occur:
(1) The start button is disabled
(2) The board is cleared and reset (all the labels set to empty hole)
(3) A random mole is generated
(4) The timer is started
There should be an error if you try to run the program now, as we haven't yet implemented the timer. So let's go ahead and do that now!
Define a timer object in global space:
private Timer timer;
Initialise the timer in the initEvents() function:
timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if(timeLeft == 0){
lblTimeLeft.setText("" + timeLeft);
timer.stop();
gameOver();
}
lblTimeLeft.setText("" + timeLeft);
timeLeft--;
}
});
The code above performs the following steps:
(1) Initialise a timer and an action listener, which is triggered every 1000ms (1 second)
(2) First, check if there is any time left
(3) If the time has run out (timeLeft = 0), then update the label, stop the timer and call the function: gameOver()
(4) Finally, update the label and decrease the timeLeft by 1 (this will essentially function as a countdown timer)
Implementing the gameOver() function
What should happen when the game is over and the time remaining is 0?
(1) Re-enable the 'start' button
(2) Check whether the high score has been beaten, update the highscore variable accordingly
(3) Display a messagebox to the user indicating that the game is over
(4) Reset all the variables: set score=0, timeLeft=30 (same with the corresponding labels)
(5) Clear the board
Create the gameOver() function:
private void gameOver(){
btnStart.setEnabled(true);
if(score > highscore){
highscore = score;
lblHighscore.setText("Highscore: " + highscore);
JOptionPane.showMessageDialog(this, "Your final score is: " + score, "You beat the high score!", JOptionPane.INFORMATION_MESSAGE);
}else{
JOptionPane.showMessageDialog(this, "Your final score is: " + score, "Game Over!", JOptionPane.INFORMATION_MESSAGE);
}
score = 0;
timeLeft = 30;
lblScore.setText("Score: 0");
lblTimeLeft.setText("30");
clearBoard();
}
JOptionPane is another Swing component which basically displays a message box
If you have gotten this far, great job!
You should now have a fully functional Whack A Mole game!
Wait a second, we are not finished yet...
There are still a few extra stuff we need to implement:
In the next coding lesson, we will cover:
(1) Styling the cursor into a hammer icon
(2) Saving the highscore to a text file
(3) Reading the highscore from the text file when the program is opened
Coding 6: Source Code
import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.border.EmptyBorder;
public class Game extends JFrame{
private JPanel panel;
private JLabel[] holes = new JLabel[16];
private int[] board = new int[16];
private int score = 0;
private int timeLeft = 30;
private int highscore = 0;
private JLabel lblScore;
private JLabel lblTimeLeft;
private JLabel lblHighscore;
private JButton btnStart;
private Timer timer;
private void gameOver(){
btnStart.setEnabled(true);
if(score > highscore){
highscore = score;
lblHighscore.setText("Highscore: " + highscore);
JOptionPane.showMessageDialog(this, "Your final score is: " + score, "You beat the high score!", JOptionPane.INFORMATION_MESSAGE);
}else{
JOptionPane.showMessageDialog(this, "Your final score is: " + score, "Game Over!", JOptionPane.INFORMATION_MESSAGE);
}
score = 0;
timeLeft = 30;
lblScore.setText("Score: 0");
lblTimeLeft.setText("30");
clearBoard();
}
private void pressedButton(int id){
int val = board[id];
//if val is 1 = mole
//if val is 0 = empty hole
if(val==1){
score++;
}else{ //val==0
score--;
}
lblScore.setText("Score: " + score); //update the score
clearBoard();
genRandMole();
}
private void initEvents(){
for(int i = 0; i < holes.length; i++){
holes[i].addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e){
JLabel lbl = (JLabel)e.getSource();
int id = Integer.parseInt(lbl.getName());
pressedButton(id);
}
});
}
btnStart.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
btnStart.setEnabled(false);
clearBoard();
genRandMole();
timer.start();
}
});
timer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
if(timeLeft == 0){
lblTimeLeft.setText("" + timeLeft);
timer.stop();
gameOver();
}
lblTimeLeft.setText("" + timeLeft);
timeLeft--;
}
});
}
private void initGUI(){
setTitle("Whack A Mole");
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 608, 720);
JPanel contentPane = new JPanel();
contentPane = new JPanel();
contentPane.setBackground(new Color(0, 51, 0));
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(null);
JLabel lblTitle = new JLabel("Whack A Mole");
lblTitle.setForeground(new Color(153, 204, 0));
lblTitle.setHorizontalAlignment(SwingConstants.CENTER);
lblTitle.setFont(new Font("Century Gothic", Font.BOLD, 20));
lblTitle.setBounds(0, 0, 602, 47);
contentPane.add(lblTitle);
panel = new JPanel();
panel.setBackground(new Color(0, 102, 0));
panel.setBounds(32, 105, 535, 546);
panel.setLayout(null);
contentPane.add(panel);
holes[0] = new JLabel("0");
holes[0].setName("0");
holes[0].setBounds(0, 396, 132, 132);
panel.add(holes[0]);
holes[1] = new JLabel("1");
holes[1].setName("1");
holes[1].setBounds(132, 396, 132, 132);
panel.add(holes[1]);
holes[2] = new JLabel("2");
holes[2].setName("2");
holes[2].setBounds(264, 396, 132, 132);
panel.add(holes[2]);
holes[3] = new JLabel("3");
holes[3].setName("3");
holes[3].setBounds(396, 396, 132, 132);
panel.add(holes[3]);
holes[4] = new JLabel("4");
holes[4].setName("4");
holes[4].setBounds(0, 264, 132, 132);
panel.add(holes[4]);
holes[5] = new JLabel("5");
holes[5].setName("5");
holes[5].setBounds(132, 264, 132, 132);
panel.add(holes[5]);
holes[6] = new JLabel("6");
holes[6].setName("6");
holes[6].setBounds(264, 264, 132, 132);
panel.add(holes[6]);
holes[7] = new JLabel("7");
holes[7].setName("7");
holes[7].setBounds(396, 264, 132, 132);
panel.add(holes[7]);
holes[8] = new JLabel("8");
holes[8].setName("8");
holes[8].setBounds(0, 132, 132, 132);
panel.add(holes[8]);
holes[9] = new JLabel("9");
holes[9].setName("9");
holes[9].setBounds(132, 132, 132, 132);
panel.add(holes[9]);
holes[10] = new JLabel("10");
holes[10].setName("10");
holes[10].setBounds(264, 132, 132, 132);
panel.add(holes[10]);
holes[11] = new JLabel("11");
holes[11].setName("11");
holes[11].setBounds(396, 132, 132, 132);
panel.add(holes[11]);
holes[12] = new JLabel("12");
holes[12].setName("12");
holes[12].setBounds(0, 0, 132, 132);
panel.add(holes[12]);
holes[13] = new JLabel("13");
holes[13].setName("13");
holes[13].setBounds(132, 0, 132, 132);
panel.add(holes[13]);
holes[14] = new JLabel("14");
holes[14].setName("14");
holes[14].setBounds(264, 0, 132, 132);
panel.add(holes[14]);
holes[15] = new JLabel("15");
holes[15].setName("15");
holes[15].setBounds(396, 0, 132, 132);
panel.add(holes[15]);
lblScore = new JLabel("Score: 0");
lblScore.setHorizontalAlignment(SwingConstants.TRAILING);
lblScore.setFont(new Font("Cambria", Font.BOLD, 14));
lblScore.setForeground(new Color(135, 206, 250));
lblScore.setBounds(423, 54, 144, 33);
contentPane.add(lblScore);
lblTimeLeft = new JLabel("30");
lblTimeLeft.setHorizontalAlignment(SwingConstants.CENTER);
lblTimeLeft.setForeground(new Color(240, 128, 128));
lblTimeLeft.setFont(new Font("Cambria Math", Font.BOLD, 24));
lblTimeLeft.setBounds(232, 54, 144, 33);
contentPane.add(lblTimeLeft);
lblHighscore = new JLabel("Highscore: 0");
lblHighscore.setHorizontalAlignment(SwingConstants.TRAILING);
lblHighscore.setForeground(new Color(255, 255, 0));
lblHighscore.setFont(new Font("Cambria", Font.BOLD, 14));
lblHighscore.setBounds(433, 18, 134, 33);
contentPane.add(lblHighscore);
btnStart = new JButton("Start");
btnStart.setBackground(Color.WHITE);
btnStart.setBounds(32, 60, 110, 33);
contentPane.add(btnStart);
setContentPane(contentPane);
}
private void clearBoard(){
for(int i = 0; i < 16; i++){
holes[i].setIcon(loadImage("/moleIn.png"));
board[i] = 0;
}
}
private void genRandMole(){
Random rnd = new Random(System.currentTimeMillis()); //seeding random with current time
int moleID = rnd.nextInt(16);
board[moleID] = 1;
holes[moleID].setIcon(loadImage("/moleOut.png"));
}
private ImageIcon loadImage(String path){
Image image = new ImageIcon(this.getClass().getResource(path)).getImage();
Image scaledImage = image.getScaledInstance(132, 132, java.awt.Image.SCALE_SMOOTH);
return new ImageIcon(scaledImage);
}
public Game() {
initGUI();
clearBoard();
initEvents();
}
public static void main(String[] args) {
Game frame = new Game();
frame.setVisible(true);
}
}