import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Random;
import javax.swing.*;
public class Board extends JPanel implements ActionListener, KeyListener {
// controls the delay between each tick in ms
private final int DELAY = 25;
// controls the size of the board
public static final int TILE_SIZE = 50;
public static final int ROWS = 12;
public static final int COLUMNS = 18;
// controls how many coins appear on the board
public static final int NUM_COINS = 5;
// suppress serialization warning
private static final long serialVersionUID = 490905409104883233L;
// keep a reference to the timer object that triggers actionPerformed() in
// case we need access to it in another method
private Timer timer;
// objects that appear on the game board
private Player player;
private ArrayList coins;
public Board() {
// set the game board size
setPreferredSize(new Dimension(TILE_SIZE * COLUMNS, TILE_SIZE * ROWS));
// set the game board background color
setBackground(new Color(232, 232, 232));
// initialize the game state
player = new Player();
coins = populateCoins();
// this timer will call the actionPerformed() method every DELAY ms
timer = new Timer(DELAY, this);
timer.start();
}
@Override
public void actionPerformed(ActionEvent e) {
// this method is called by the timer every DELAY ms.
// use this space to update the state of your game or animation
// before the graphics are redrawn.
// prevent the player from disappearing off the board
player.tick();
// give the player points for collecting coins
collectCoins();
// calling repaint() will trigger paintComponent() to run again,
// which will refresh/redraw the graphics.
repaint();
}
@Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// when calling g.drawImage() we can use "this" for the ImageObserver
// because Component implements the ImageObserver interface, and JPanel
// extends from Component. So "this" Board instance, as a Component, can
// react to imageUpdate() events triggered by g.drawImage()
// draw our graphics.
drawBackground(g);
drawScore(g);
for (Coin coin : coins) {
coin.draw(g, this);
}
player.draw(g, this);
// this smooths out animations on some systems
Toolkit.getDefaultToolkit().sync();
}
@Override
public void keyTyped(KeyEvent e) {
// this is not used but must be defined as part of the KeyListener interface
}
@Override
public void keyPressed(KeyEvent e) {
// react to key down events
player.keyPressed(e);
}
@Override
public void keyReleased(KeyEvent e) {
// react to key up events
}
private void drawBackground(Graphics g) {
// draw a checkered background
g.setColor(new Color(214, 214, 214));
for (int row = 0; row < ROWS; row++) {
for (int col = 0; col < COLUMNS; col++) {
// only color every other tile
if ((row + col) % 2 == 1) {
// draw a square tile at the current row/column position
g.fillRect(
col * TILE_SIZE,
row * TILE_SIZE,
TILE_SIZE,
TILE_SIZE
);
}
}
}
}
private void drawScore(Graphics g) {
// set the text to be displayed
String text = "$" + player.getScore();
// we need to cast the Graphics to Graphics2D to draw nicer text
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.setRenderingHint(
RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(
RenderingHints.KEY_FRACTIONALMETRICS,
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
// set the text color and font
g2d.setColor(new Color(30, 201, 139));
g2d.setFont(new Font("Lato", Font.BOLD, 25));
// draw the score in the bottom center of the screen
// https://stackoverflow.com/a/27740330/4655368
FontMetrics metrics = g2d.getFontMetrics(g2d.getFont());
// the text will be contained within this rectangle.
// here I've sized it to be the entire bottom row of board tiles
Rectangle rect = new Rectangle(0, TILE_SIZE * (ROWS - 1), TILE_SIZE * COLUMNS, TILE_SIZE);
// determine the x coordinate for the text
int x = rect.x + (rect.width - metrics.stringWidth(text)) / 2;
// determine the y coordinate for the text
// (note we add the ascent, as in java 2d 0 is top of the screen)
int y = rect.y + ((rect.height - metrics.getHeight()) / 2) + metrics.getAscent();
// draw the string
g2d.drawString(text, x, y);
}
private ArrayList populateCoins() {
ArrayList coinList = new ArrayList<>();
Random rand = new Random();
// create the given number of coins in random positions on the board.
// note that there is not check here to prevent two coins from occupying the same
// spot, nor to prevent coins from spawning in the same spot as the player
for (int i = 0; i < NUM_COINS; i++) {
int coinX = rand.nextInt(COLUMNS);
int coinY = rand.nextInt(ROWS);
coinList.add(new Coin(coinX, coinY));
}
return coinList;
}
private void collectCoins() {
// allow player to pickup coins
ArrayList collectedCoins = new ArrayList<>();
for (Coin coin : coins) {
// if the player is on the same tile as a coin, collect it
if (player.getPos().equals(coin.getPos())) {
// give the player some points for picking this up
player.addScore(100);
collectedCoins.add(coin);
}
}
// remove collected coins from the board
coins.removeAll(collectedCoins);
}
}