I'm making baby steps in Java programming and I just ended coding basic game: Snake. Could you share with me what is needed to improve in this code? What about OOP here?
GameBoard.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.LinkedList;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class GameBoard extends JFrame {
//setting board size
public static int boardWidth = 1000;
public static int boardHeight = 800;
private JPanel scorePanel;
public static void main(String[] args) {
new GameBoard();
}
public GameBoard() {
super("Snake Game");
this.setSize(boardWidth, boardHeight);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
DrawingTheBoard.theSnake.changeDirection(Direction.LEFT);
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
DrawingTheBoard.theSnake.changeDirection(Direction.UP);
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
DrawingTheBoard.theSnake.changeDirection(Direction.RIGHT);
} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
DrawingTheBoard.theSnake.changeDirection(Direction.DOWN);
}
}
@Override
public void keyReleased(KeyEvent e) {
}
});
DrawingTheBoard gamePanel = new DrawingTheBoard();
this.add(gamePanel, BorderLayout.CENTER);
scorePanel = new JPanel();
scorePanel.add(gamePanel.scoreLabel, BorderLayout.CENTER);
this.add(scorePanel, BorderLayout.PAGE_END);
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5);
executor.scheduleAtFixedRate(new RepaintTheBoard(this), 0, 1000, TimeUnit.MILLISECONDS); // updating board each 100MS
this.setVisible(true);
}
}
class RepaintTheBoard implements Runnable {
private GameBoard theBoard;
public RepaintTheBoard(GameBoard theBoard) {
this.theBoard = theBoard;
}
public void run() {
theBoard.repaint();
}
}
class DrawingTheBoard extends JComponent {
static Snake theSnake = new Snake(GameBoard.boardWidth, GameBoard.boardHeight);
static SnakeState theState = new SnakeState();
private LinkedList<Point> body;
private Point apple;
public JLabel scoreLabel;
public int score = 0;
public DrawingTheBoard() {
apple = new Apple().getPointOfApple();
scoreLabel = new JLabel("Score: " + Integer.toString(score));
scoreLabel.setFont(new Font("Serif", Font.PLAIN, 40));
}
public void paint(Graphics g) {
body = theSnake.getBody(); // making copy of List from Snake to draw it on screen
Graphics2D g2D = (Graphics2D) g;
g2D.setBackground(Color.BLACK);
g2D.fillRect(0, 0, getWidth(), getHeight());
g2D.setPaint(Color.WHITE);
for (Point partOfBody : body) {
g2D.fillRect(partOfBody.getX(), partOfBody.getY(), 10, 10); // painting body of rectangles 10x10
}
theSnake.update(apple);
// checking if is gameOver
if (theState.checkIfSnakeIsDead(body)) {
theSnake.restartSnake();
score = 0;
setScoreLabel(score);
}
if (theSnake.hasEaten()) {
apple = new Apple().getPointOfApple(); // if snake has eaten apple creating new one
theSnake.setHasEatenApple(false);
score += 10;
setScoreLabel(score); // adding 10 to score
}
g2D.fillRect(apple.getX(), apple.getY(), 10, 10);
}
public void setScoreLabel(int newScore) {
scoreLabel.setText("Score: " + Integer.toString(newScore));
}
}
Apple.java
import java.util.Random;
public class Apple {
private int bWidth = GameBoard.boardWidth;
private int bHeight = GameBoard.boardHeight;
private int randomXPos;
private int randomYPos;
private Point pointOfApple;
Random r = new Random();
public Apple(){
randomXPos = r.nextInt((bWidth-10)/10)*10;
randomYPos = r.nextInt((bHeight-110)/10)*10;
pointOfApple = new Point(randomXPos,randomYPos);
}
public Point getPointOfApple(){
return pointOfApple;
}
public int getRandomXPos(){
return randomXPos;
}
public int getRandomYPos(){
return randomYPos;
}
}
Snake.java
import java.awt.*;
import java.util.LinkedList;
public class Snake extends Rectangle {
private LinkedList<Point> body; // list holding points(x,y) of snake body
private Direction headDirection;
private boolean hasEatenApple = false;
private int bWidth;
private int bHeight;
public Snake(int bWidth, int bHeight) {
this.bWidth = bWidth;
this.bHeight = bHeight;
body = new LinkedList<>();
restartSnake();
}
public LinkedList<Point> getBody() {
return (LinkedList<Point>) body.clone();
}
public void restartSnake() {
body.clear();
//creating 3-part starting snake
body.addFirst(new Point(this.bWidth / 2, this.bHeight / 2));
body.add(new Point((this.bWidth / 2) - 10, this.bHeight / 2));
body.add(new Point((this.bWidth / 2) - 20, this.bHeight / 2));
this.headDirection = Direction.RIGHT;
}
public void changeDirection(Direction theDirection) {
if (theDirection != headDirection.opposite())
this.headDirection = theDirection;
}
//updating localisation of snake
public void update(Point applePoint) {
if (headDirection == Direction.DOWN) {
addPartOfBody(0, 10, applePoint);
} else if (headDirection == Direction.UP) {
addPartOfBody(0, -10, applePoint);
} else if (headDirection == Direction.LEFT) {
addPartOfBody(-10, 0, applePoint);
} else if (headDirection == Direction.RIGHT) {
addPartOfBody(10, 0, applePoint);
}
}
public void addPartOfBody(int x, int y, Point applePoint) {
body.addFirst(new Point(body.get(0).getX() + x, body.get(0).getY() + y));
//if new point of head of snake is point of apple don't reduce last part of snake
if (body.get(0).getX() != applePoint.getX() || body.get(0).getY() != applePoint.getY()) {
body.removeLast();
} else {
hasEatenApple = true;
}
}
public boolean hasEaten(){
return hasEatenApple;
}
public void setHasEatenApple(boolean hasEatenApple){
this.hasEatenApple = hasEatenApple;
}
}
SnakeState.java
import java.util.LinkedList;
public class SnakeState {
private int bWidth = GameBoard.boardWidth;
private int bHeight = GameBoard.boardHeight;
private static boolean isDead = false;
public boolean checkIfSnakeIsDead(LinkedList<Point> body) {
//checking if head of snake is out of board
if (body.get(0).getX() < 0 || body.get(0).getX() > bWidth-10
|| body.get(0).getY() < 0 || body.get(0).getY() > bHeight-110) {
return true;
}
//checking if head of the snake have collision with others parts of his body
for (int i = 1; i < body.size(); i++) {
if (body.get(i).getX() == body.get(0).getX() && body.get(i).getY() == body.get(0).getY())
return true;
}
return false;
}
}
Point.java
public class Point {
private int x;
private int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Direction.java
public enum Direction {
LEFT {
Direction opposite() {
return RIGHT;
}
},
RIGHT {
Direction opposite() {
return LEFT;
}
},
UP {
Direction opposite() {
return DOWN;
}
},
DOWN {
Direction opposite() {
return UP;
}
};
abstract Direction opposite();
}