I am a self-taught coder, and have been learning Java for the last 2 months.
I have recently created a snake game in Java with the help of an online tutorial. I have rearranged the program into separate classes & methods. I am skeptical on how good of a programmer I am, and I code messy.
I don't really want help on how to make the game better in visuals, levels, or anything like that. I am asking those who are more advanced programmers to help me with implementing programming techniques, better organization, how to comment better, etc. Pretty much anything that will help me become a better programmer.
Also, I have commented a few things out of pure guess, so if it appears as if I have gotten something wrong, please correct me!
Also, any extra tips for my future projects would be much appreciated!
Node
public class Node {
private final int x;
private final int y;
public Node(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
Snake
import java.util.LinkedList;
public class Snake {
private LinkedList<Node> body = new LinkedList<Node>();
public boolean isEatFood(Node food) {
Node head = body.getFirst();
return Math.abs(head.getX() - food.getX()) + Math.abs(head.getY() - food.getY()) == 0;
}
public Node move(Direction direction) {
Node node = null;
int headX = this.body.getFirst().getX();
int headY = this.body.getFirst().getY();
switch(direction) {
case UP :
node = new Node(headX, headY - 1);
break;
case RIGHT :
node = new Node(headX + 1, headY);
break;
case DOWN :
node = new Node(headX, headY + 1);
break;
case LEFT :
node = new Node(headX - 1, headY);
break;
}
this.body.addFirst(node);
return body.removeLast();
}
public Node getHead() {
return body.getFirst();
}
public Node addTail(Node area) {
this.body.addLast(area);
return area;
}
public LinkedList<Node> getBody() {
return body;
}
}
Grid
import java.util.Arrays;
import java.util.Random;
public class Grid {
private boolean isCovered[][];
private final int width;
private final int height;
private int scores = 0;
private Snake snake;
private Node food;
private Direction snakeDirection = Direction.LEFT; // initial direction is LEFT
public boolean isDirectionChanged = false;
public Grid(int width, int height) {
this.width = width;
this.height = height;
isCovered = new boolean[width][height];
initSnake();
createFood();
}
private Snake initSnake() { // initial Snake has 3 Node
snake = new Snake();
for (int i = 0; i < 3; i++) {
snake.addTail(new Node(i + width / 2, height / 2));
isCovered[i + width / 2][height / 2] = true;
}
return snake;
}
public Node createFood() {
int x,y;
do {
x = new Random( ).nextInt(width);
y = new Random( ).nextInt(height);
} while (isCovered[x][y] == true);
food = new Node(x, y);
return food;
}
public boolean nextRound() { //follow the direction and move one step
if (isMoveValid(snakeDirection)) {
Node move = snake.move(snakeDirection);
if (snake.isEatFood(food)) { //if ate food, add the Node moved at tail
snake.addTail(move);
createFood();
System.out.println(++scores);
} else isCovered[move.getX()][move.getY()] = false;
return true;
} else return false;
}
private boolean isMoveValid(Direction direction) {
int headX = snake.getHead().getX();
int headY = snake.getHead().getY();
switch(direction) {
case UP :
headY--;
break;
case RIGHT :
headX++;
break;
case DOWN :
headY++;
break;
case LEFT :
headX--;
break;
}
if (headX < 0 || headX >= width || headY < 0 || headY >= height) return false;
if (isCovered[headX][headY] == true) return false;
isCovered[headX][headY] = true;
return true;
}
public void changeDirection(Direction newDirection) {
if (snakeDirection.compatibleWith(newDirection)) {
snakeDirection = newDirection;
isDirectionChanged = true;
}
}
public Snake getSnake() { return snake; }
public Node getFood() { return food; }
public int getWidth() { return width; }
public int getHeight() { return height; }
public int getScore() { return scores; }
}
GameController
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class GameController implements Runnable, KeyListener{
private final Grid grid;
private final GameView gameView;
private boolean running;
public GameController(Grid grid, GameView gameView) {
this.grid = grid;
this.gameView = gameView;
this.running = true;
}
@Override
public void run() {
while (running) {
try {
Thread.sleep(Math.max(50, 200 - grid.getScore() / 5 * 30)); //DEFAULT_MOVE_INTERVAL
} catch (InterruptedException e) {
break;
}
grid.isDirectionChanged = false;
if (grid.nextRound() == true) {
gameView.draw();
} else {
System.out.print("Congraduations! Your scores: " + grid.getScore());
gameView.showGameOverMessage();
running = false;
}
}
}
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (grid.isDirectionChanged == false) {
switch (keyCode) {
case KeyEvent.VK_UP :
grid.changeDirection(Direction.UP);
break;
case KeyEvent.VK_RIGHT :
grid.changeDirection(Direction.RIGHT);
break;
case KeyEvent.VK_DOWN :
grid.changeDirection(Direction.DOWN);
break;
case KeyEvent.VK_LEFT :
grid.changeDirection(Direction.LEFT);
break;
case KeyEvent.VK_SPACE :
break;
}
}
// repaint the canvas
}
@Override
public void keyReleased(KeyEvent e) {}
@Override
public void keyTyped(KeyEvent e) {}
}
SnakeApp
import javax.swing.*;
import java.awt.*;
public class SnakeApp implements Runnable {
private final int DEFAULT_GRID_WIDTH = 30;
private final int DEFAULT_GRID_HEIGHT = 30;
private GameView gameView;
private GameController gameController;
public void run() {
JFrame window = new JFrame("Snake Game"); //creat game window
Container contentPane = window.getContentPane();
// use Grid initialize the gamaView
Grid grid = new Grid(DEFAULT_GRID_WIDTH, DEFAULT_GRID_HEIGHT);
gameView = new GameView(grid);
gameView.init();
// set JPanel's size
gameView.getCanvas().setPreferredSize(new Dimension(DEFAULT_GRID_WIDTH * gameView.DEFAULT_NODE_SIZE,
DEFAULT_GRID_HEIGHT * gameView.DEFAULT_NODE_SIZE));
// add JPanel to windows
contentPane.add(gameView.getCanvas(), BorderLayout.CENTER);
// draw grid and snake
window.pack();
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
gameController = new GameController(grid, gameView);
window.addKeyListener(gameController);
// start the thread
new Thread(gameController).start();
}
public static void main(String[] args) {
//SnakeApp snakeApp = new SnakeApp();
//snakeApp.run();
SwingUtilities.invokeLater(new SnakeApp());
}
}
GameView
import javax.swing.*;
import java.awt.*;
public class GameView {
//use Graphics API draw pics
private JPanel canvas;
private final Grid grid;
public final int DEFAULT_NODE_SIZE = 15;
public GameView(Grid grid) {
this.grid = grid;
}
public void init() {
canvas = new JPanel() {
@Override
public void paintComponent(Graphics graphics) {
drawGridBackground(graphics);
drawSnake(graphics, grid.getSnake());
drawFood(graphics, grid.getFood());
}
};
}
public void draw() {
canvas.repaint();
}
public JPanel getCanvas() {
return canvas;
}
public void drawSnake(Graphics graphics, Snake snake) {
for (Node node : snake.getBody()) {
drawSquare(graphics, node, Color.GREEN);
}
}
public void drawFood(Graphics graphics, Node squareArea) {
drawCircle(graphics, squareArea, Color.RED);
}
public void drawGridBackground(Graphics graphics) {
for (int i = 0; i < grid.getWidth(); i++) {
for (int j = 0; j < grid.getHeight(); j++) {
drawSquare(graphics, new Node(i, j), new Color(127, 127, 127, 255));
}
}
}
private void drawSquare(Graphics graphics, Node squareArea, Color color) {
graphics.setColor(color);
int size = DEFAULT_NODE_SIZE;
graphics.fillRect(squareArea.getX() * size, squareArea.getY() * size, size - 1, size - 1);
}
private void drawCircle(Graphics graphics, Node squareArea, Color color) {
graphics.setColor(color);
int size = DEFAULT_NODE_SIZE;
graphics.fillOval(squareArea.getX() * size-1, squareArea.getY() * size-1, size, size);
}
public void showGameOverMessage() {
JOptionPane.showMessageDialog(null, "Game Over!", "GameOver", JOptionPane.INFORMATION_MESSAGE);
}
}
Direction
? \$\endgroup\$ – Timothy Truckle Feb 26 '17 at 13:32