0

I found this codes online but they were originally in a JFrame. Right now what i am trying to do is that i want to make this JFrame into a panel. I think i made the correct changes already for the adding of the panels. However when i run it has an "java.lang.NullPointerException" error and my eclipse will say that there is an error at SnakeGamePanel.java:339 which is the repaint() thing inside the Startgame() method. The below codes are my 3 panels, SnakeGamePanel, BoardPanel, SidePanel. If i have added the panels wrongly please guide me on how to add the BoardPanel and SidePanel into the SnakeGamePanel.

Source of game:http://psnbtech.blogspot.sg/2012/10/tutorial-java-snake-game-remake.html

SnakeGamePanel:

package gamesUI;

import javax.swing.JFrame;
import javax.swing.JPanel;

import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.Random;

public class SnakeGamePanel extends JPanel {

    /**
 * Create the panel.
 */
/**
 * The Serial Version UID.
 */
private static final long serialVersionUID = 6678292058307426314L;

/**
 * The number of milliseconds that should pass between each frame.
 */
private static final long FRAME_TIME = 1000L / 50L;

/**
 * The minimum length of the snake. This allows the snake to grow
 * right when the game starts, so that we're not just a head moving
 * around on the board.
 */
private static final int MIN_SNAKE_LENGTH = 5;

/**
 * The maximum number of directions that we can have polled in the
 * direction list.
 */
private static final int MAX_DIRECTIONS = 3;

/**
 * The BoardPanel instance.
 */
private BoardPanel board;

/**
 * The SidePanel instance.
 */
private SidePanel side;

/**
 * The random number generator (used for spawning fruits).
 */
private Random random;

/**
 * The Clock instance for handling the game logic.
 */
private Clock logicTimer;

/**
 * Whether or not we're running a new game.
 */
private boolean isNewGame;

/**
 * Whether or not the game is over.
 */
private boolean isGameOver;

/** 
 * Whether or not the game is paused.
 */
private boolean isPaused;

/**
 * The list that contains the points for the snake.
 */
private LinkedList<Point> snake;

/**
 * The list that contains the queued directions.
 */
private LinkedList<Direction> directions;

/**
 * The current score.
 */
private int score;

/**
 * The number of fruits that we've eaten.
 */
private int fruitsEaten;

/**
 * The number of points that the next fruit will award us.
 */
private int nextFruitScore;

/**
 * Creates a new SnakeGame instance. Creates a new window,
 * and sets up the controller input.
 */

private int highscore = 0;

private String saveDataPath;
private String FileName = "SaveData";

protected static JFrame myFrame = null;

public SnakeGamePanel(JFrame mf){
    myFrame = mf;

    //this is for the highscore system 
    try {
        saveDataPath = SnakeGamePanel.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();

    }
    catch (Exception e){

    }

    setLayout(new BorderLayout());
    //adding the panels to snakegamepanel


    BoardPanel board = new BoardPanel(this);//this is the left panel
    SidePanel side = new SidePanel(this);//this is the right panel


    add(board, BorderLayout.CENTER);
    add(side, BorderLayout.EAST);

    addKeyListener(new KeyAdapter() {

        @Override
        public void keyPressed(KeyEvent e) {
            switch(e.getKeyCode()) {

            /*
             * If the game is not paused, and the game is not over...
             * 
             * Ensure that the direction list is not full, and that the most
             * recent direction is adjacent to North before adding the
             * direction to the list.
             */
            case KeyEvent.VK_W:
            case KeyEvent.VK_UP:
                if(!isPaused && !isGameOver) {
                    if(directions.size() < MAX_DIRECTIONS) {
                        Direction last = directions.peekLast();
                        if(last != Direction.South && last != Direction.North) {
                            directions.addLast(Direction.North);
                        }
                    }
                }
                break;

                /*
                 * If the game is not paused, and the game is not over...
                 * 
                 * Ensure that the direction list is not full, and that the most
                 * recent direction is adjacent to South before adding the
                 * direction to the list.
                 */ 
            case KeyEvent.VK_S:
            case KeyEvent.VK_DOWN:
                if(!isPaused && !isGameOver) {
                    if(directions.size() < MAX_DIRECTIONS) {
                        Direction last = directions.peekLast();
                        if(last != Direction.North && last != Direction.South) {
                            directions.addLast(Direction.South);
                        }
                    }
                }
                break;

                /*
                 * If the game is not paused, and the game is not over...
                 * 
                 * Ensure that the direction list is not full, and that the most
                 * recent direction is adjacent to West before adding the
                 * direction to the list.
                 */                     
            case KeyEvent.VK_A:
            case KeyEvent.VK_LEFT:
                if(!isPaused && !isGameOver) {
                    if(directions.size() < MAX_DIRECTIONS) {
                        Direction last = directions.peekLast();
                        if(last != Direction.East && last != Direction.West) {
                            directions.addLast(Direction.West);
                        }
                    }
                }
                break;

                /*
                 * If the game is not paused, and the game is not over...
                 * 
                 * Ensure that the direction list is not full, and that the most
                 * recent direction is adjacent to East before adding the
                 * direction to the list.
                 */     
            case KeyEvent.VK_D:
            case KeyEvent.VK_RIGHT:
                if(!isPaused && !isGameOver) {
                    if(directions.size() < MAX_DIRECTIONS) {
                        Direction last = directions.peekLast();
                        if(last != Direction.West && last != Direction.East) {
                            directions.addLast(Direction.East);
                        }
                    }
                }
                break;

                /*
                 * If the game is not over, toggle the paused flag and update
                 * the logicTimer's pause flag accordingly.
                 */
            case KeyEvent.VK_SPACE:
                if(!isGameOver) {
                    isPaused = !isPaused;
                    logicTimer.setPaused(isPaused);
                }
                break;

                /*
                 * Reset the game if one is not currently in progress.
                 */
            case KeyEvent.VK_ENTER:
                if(isNewGame || isGameOver) {
                    resetGame();
                }
                break;
            }
        }

    });

    board.setSize(500, 500);
    side.setSize(300, 500);
    setVisible(true);

}
//this is to save the highscore in a file

private void createSaveData(){
    try{
        File file = new File (saveDataPath, FileName);

        FileWriter output = new FileWriter(file);
        BufferedWriter writer = new BufferedWriter(output);
        writer.write("" + 0);
        writer.close();
    }
    catch (Exception e){
        e.printStackTrace();
    }
}

//this is to load the highscore when the game starts
private void loadHighScore(){
    try{
        File f = new File (saveDataPath, FileName);
        if (!f.isFile()){
            createSaveData();
        }

        BufferedReader reader = new BufferedReader (new InputStreamReader(new FileInputStream(f)));
        highscore = Integer.parseInt(reader.readLine());
        reader.close();
    }
    catch (Exception e){
        e.printStackTrace();
    }
}

//this is to set the new highscore in the file if theres any 
private void setHighScore(){
    FileWriter output = null;

    try{
        File f = new File(saveDataPath, FileName);
        output = new FileWriter(f);
        BufferedWriter writer = new BufferedWriter (output);
        writer.write("" + highscore);
        writer.close();
    }
    catch (Exception e){
        e.printStackTrace();
    }

}

/**
 * Starts the game running.
 */
public void startGame() {
    /*
     * Initialize everything we're going to be using.
     */
    loadHighScore();
    this.random = new Random();
    this.snake = new LinkedList<>();
    this.directions = new LinkedList<>();
    this.logicTimer = new Clock(9.0f);
    this.isNewGame = true;

    //Set the timer to paused initially.
    logicTimer.setPaused(true);

    /*
     * This is the game loop. It will update and render the game and will
     * continue to run until the game window is closed.
     */
    while(true) {
        //Get the current frame's start time.
        long start = System.nanoTime();

        //Update the logic timer.
        logicTimer.update();

        /*
         * If a cycle has elapsed on the logic timer, then update the game.
         */
        if(logicTimer.hasElapsedCycle()) {
            updateGame();
        }

        //Repaint the board and side panel with the new content.
        board.repaint();
        side.repaint();

        /*
         * Calculate the delta time between since the start of the frame
         * and sleep for the excess time to cap the frame rate. While not
         * incredibly accurate, it is sufficient for our purposes.
         */
        long delta = (System.nanoTime() - start) / 1000000L;
        if(delta < FRAME_TIME) {
            try {
                Thread.sleep(FRAME_TIME - delta);
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * Updates the game's logic.
 */
private void updateGame() {
    /*
     * Gets the type of tile that the head of the snake collided with. If 
     * the snake hit a wall, SnakeBody will be returned, as both conditions
     * are handled identically.
     */
    TileType collision = updateSnake();

    /*
     * Here we handle the different possible collisions.
     * 
     * Fruit: If we collided with a fruit, we increment the number of
     * fruits that we've eaten, update the score, and spawn a new fruit.
     * 
     * SnakeBody: If we collided with our tail (or a wall), we flag that
     * the game is over and pause the game.
     * 
     * If no collision occurred, we simply decrement the number of points
     * that the next fruit will give us if it's high enough. This adds a
     * bit of skill to the game as collecting fruits more quickly will
     * yield a higher score.
     */
    if(collision == TileType.Fruit) {
        fruitsEaten++;
        score += nextFruitScore;
        spawnFruit();
    } else if(collision == TileType.SnakeBody) {
        isGameOver = true;
        logicTimer.setPaused(true);
    } else if(nextFruitScore > 10) {
        nextFruitScore--;
    }
    if ( score >= highscore){
        highscore = score;
        setHighScore();
    }   
}

/**
 * Updates the snake's position and size.
 * @return Tile tile that the head moved into.
 */
private TileType updateSnake() {

    /*
     * Here we peek at the next direction rather than polling it. While
     * not game breaking, polling the direction here causes a small bug
     * where the snake's direction will change after a game over (though
     * it will not move).
     */
    Direction direction = directions.peekFirst();

    /*
     * Here we calculate the new point that the snake's head will be at
     * after the update.
     */     
    Point head = new Point(snake.peekFirst());
    switch(direction) {
    case North:
        head.y--;
        break;

    case South:
        head.y++;
        break;

    case West:
        head.x--;
        break;

    case East:
        head.x++;
        break;
    }

    /*
     * If the snake has moved out of bounds ('hit' a wall), we can just
     * return that it's collided with itself, as both cases are handled
     * identically.
     */
    if(head.x < 0 || head.x >= BoardPanel.COL_COUNT || head.y < 0 || head.y >= BoardPanel.ROW_COUNT) {
        return TileType.SnakeBody; //Pretend we collided with our body.
    }

    /*
     * Here we get the tile that was located at the new head position and
     * remove the tail from of the snake and the board if the snake is
     * long enough, and the tile it moved onto is not a fruit.
     * 
     * If the tail was removed, we need to retrieve the old tile again
     * incase the tile we hit was the tail piece that was just removed
     * to prevent a false game over.
     */
    TileType old = board.getTile(head.x, head.y);
    if(old != TileType.Fruit && snake.size() > MIN_SNAKE_LENGTH) {
        Point tail = snake.removeLast();
        board.setTile(tail, null);
        old = board.getTile(head.x, head.y);
    }

    /*
     * Update the snake's position on the board if we didn't collide with
     * our tail:
     * 
     * 1. Set the old head position to a body tile.
     * 2. Add the new head to the snake.
     * 3. Set the new head position to a head tile.
     * 
     * If more than one direction is in the queue, poll it to read new
     * input.
     */
    if(old != TileType.SnakeBody) {
        board.setTile(snake.peekFirst(), TileType.SnakeBody);
        snake.push(head);
        board.setTile(head, TileType.SnakeHead);
        if(directions.size() > 1) {
            directions.poll();
        }
    }

    return old;
}

/**
 * Resets the game's variables to their default states and starts a new game.
 */
private void resetGame() {
    /*
     * Reset the score statistics. (Note that nextFruitPoints is reset in
     * the spawnFruit function later on).
     */
    this.score = 0;
    this.fruitsEaten = 0;

    /*
     * Reset both the new game and game over flags.
     */
    this.isNewGame = false;
    this.isGameOver = false;

    /*
     * Create the head at the center of the board.
     */
    Point head = new Point(BoardPanel.COL_COUNT / 2, BoardPanel.ROW_COUNT / 2);

    /*
     * Clear the snake list and add the head.
     */
    snake.clear();
    snake.add(head);

    /*
     * Clear the board and add the head.
     */
    board.clearBoard();
    board.setTile(head, TileType.SnakeHead);

    /*
     * Clear the directions and add north as the
     * default direction.
     */
    directions.clear();
    directions.add(Direction.North);

    /*
     * Reset the logic timer.
     */
    logicTimer.reset();

    /*
     * Spawn a new fruit.
     */
    spawnFruit();

}

/**
 * Gets the flag that indicates whether or not we're playing a new game.
 * @return The new game flag.
 */
public boolean isNewGame() {
    return isNewGame;
}

/**
 * Gets the flag that indicates whether or not the game is over.
 * @return The game over flag.
 */
public boolean isGameOver() {
    return isGameOver;
}

/**
 * Gets the flag that indicates whether or not the game is paused.
 * @return The paused flag.
 */
public boolean isPaused() {
    return isPaused;
}

/**
 * Spawns a new fruit onto the board.
 */
private void spawnFruit() {
    //Reset the score for this fruit to 100.
    this.nextFruitScore = 100;

    /*
     * Get a random index based on the number of free spaces left on the board.
     */
    int index = random.nextInt(BoardPanel.COL_COUNT * BoardPanel.ROW_COUNT - snake.size());

    /*
     * While we could just as easily choose a random index on the board
     * and check it if it's free until we find an empty one, that method
     * tends to hang if the snake becomes very large.
     * 
     * This method simply loops through until it finds the nth free index
     * and selects uses that. This means that the game will be able to
     * locate an index at a relatively constant rate regardless of the
     * size of the snake.
     */
    int freeFound = -1;
    for(int x = 0; x < BoardPanel.COL_COUNT; x++) {
        for(int y = 0; y < BoardPanel.ROW_COUNT; y++) {
            TileType type = board.getTile(x, y);
            if(type == null || type == TileType.Fruit) {
                if(++freeFound == index) {
                    board.setTile(x, y, TileType.Fruit);
                    break;
                }
            }
        }
    }
}

/**
 * Gets the current score.
 * @return The score.
 */
public int getScore() {
    return score;
}

/**
 * Gets the number of fruits eaten.
 * @return The fruits eaten.
 */
public int getFruitsEaten() {
    return fruitsEaten;
}

/**
 * Gets the next fruit score.
 * @return The next fruit score.
 */
public int getNextFruitScore() {
    return nextFruitScore;
}

/**
 * Gets the current direction of the snake.
 * @return The current direction.
 */
public Direction getDirection() {
    return directions.peek();
}

public int getHighScore(){
    return highscore;
}
/**
 * Entry point of the program.
 * @param args Unused.
 */

public static void main(String[] args) {
    SnakeGamePanel Snake = new SnakeGamePanel(myFrame);
    Snake.startGame();
}
}

BoardPanel:

package gamesUI;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Point;

import javax.swing.JPanel;

/**
 * The {@code BoardPanel} class is responsible for managing and displaying the
 * contents of the game board.
 * @author Brendan Jones
 *
 */
public class BoardPanel extends JPanel {

/**
 * Serial Version UID.
 */
private static final long serialVersionUID = -1102632585936750607L;

/**
 * The number of columns on the board. (Should be odd so we can start in
 * the center).
 */
public static final int COL_COUNT = 25;

/**
 * The number of rows on the board. (Should be odd so we can start in
 * the center).
 */
public static final int ROW_COUNT = 25;

/**
 * The size of each tile in pixels.
 */
public static final int TILE_SIZE = 20;

/**
 * The number of pixels to offset the eyes from the sides.
 */
private static final int EYE_LARGE_INSET = TILE_SIZE / 3;

/**
 * The number of pixels to offset the eyes from the front.
 */
private static final int EYE_SMALL_INSET = TILE_SIZE / 6;

/**
 * The length of the eyes from the base (small inset).
 */
private static final int EYE_LENGTH = TILE_SIZE / 5;

/**
 * The font to draw the text with.
 */
private static final Font FONT = new Font("Tahoma", Font.BOLD, 25);

/**
 * The SnakeGame instance.
 */
private SnakeGamePanel game;

/**
 * The array of tiles that make up this board.
 */
private TileType[] tiles;       
/**
 * Creates a new BoardPanel instance.
 * @param game The SnakeGame instance.
 */
public BoardPanel(SnakeGamePanel game) {
    this.game = game;
    this.tiles = new TileType[ROW_COUNT * COL_COUNT];

    setPreferredSize(new Dimension(COL_COUNT * TILE_SIZE, ROW_COUNT * TILE_SIZE));
    setBackground(Color.BLACK);
    setLayout(null);
}

/**
 * Clears all of the tiles on the board and sets their values to null.
 */
public void clearBoard() {
    for(int i = 0; i < tiles.length; i++) {
        tiles[i] = null;
    }
}

/**
 * Sets the tile at the desired coordinate.
 * @param point The coordinate of the tile.
 * @param type The type to set the tile to.
 */
public void setTile(Point point, TileType type) {
    setTile(point.x, point.y, type);
}

/**
 * Sets the tile at the desired coordinate.
 * @param x The x coordinate of the tile.
 * @param y The y coordinate of the tile.
 * @param type The type to set the tile to.
 */
public void setTile(int x, int y, TileType type) {
    tiles[y * ROW_COUNT + x] = type;
}

/**
 * Gets the tile at the desired coordinate.
 * @param x The x coordinate of the tile.
 * @param y The y coordinate of the tile.
 * @return
 */
public TileType getTile(int x, int y) {
    return tiles[y * ROW_COUNT + x];
}

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);

    /*
     * Loop through each tile on the board and draw it if it
     * is not null.
     */
    for(int x = 0; x < COL_COUNT; x++) {
        for(int y = 0; y < ROW_COUNT; y++) {
            TileType type = getTile(x, y);
            if(type != null) {
                drawTile(x * TILE_SIZE, y * TILE_SIZE, type, g);
            }
        }
    }

    /*
     * Draw the grid on the board. This makes it easier to see exactly
     * where we in relation to the fruit.
     * 
     * The panel is one pixel too small to draw the bottom and right
     * outlines, so we outline the board with a rectangle separately.
     */
    g.setColor(Color.DARK_GRAY);
    g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
    for(int x = 0; x < COL_COUNT; x++) {
        for(int y = 0; y < ROW_COUNT; y++) {
            g.drawLine(x * TILE_SIZE, 0, x * TILE_SIZE, getHeight());
            g.drawLine(0, y * TILE_SIZE, getWidth(), y * TILE_SIZE);
        }
    }       

    /*
     * Show a message on the screen based on the current game state.
     */
    if(game.isGameOver() || game.isNewGame() || game.isPaused()) {
        g.setColor(Color.WHITE);

        /*
         * Get the center coordinates of the board.
         */
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;

        /*
         * Allocate the messages for and set their values based on the game
         * state.
         */
        String largeMessage = null;
        String smallMessage = null;
        if(game.isNewGame()) {
            largeMessage = "Snake Game!";
            smallMessage = "Press Enter to Start";
        } else if(game.isGameOver()) {
            largeMessage = "Game Over! BOOHOOO";
            smallMessage = "Press Enter to Restart";
        } else if(game.isPaused()) {
            largeMessage = "Paused";
            smallMessage = "Press P to Resume";
        }

        /*
         * Set the message font and draw the messages in the center of the board.
         */
        g.setFont(FONT);
        g.drawString(largeMessage, centerX - g.getFontMetrics().stringWidth(largeMessage) / 2, centerY - 50);
        g.drawString(smallMessage, centerX - g.getFontMetrics().stringWidth(smallMessage) / 2, centerY + 50);
    }
}

/**
 * Draws a tile onto the board.
 * @param x The x coordinate of the tile (in pixels).
 * @param y The y coordinate of the tile (in pixels).
 * @param type The type of tile to draw.
 * @param g The graphics object to draw to.
 */
private void drawTile(int x, int y, TileType type, Graphics g) {
    /*
     * Because each type of tile is drawn differently, it's easiest
     * to just run through a switch statement rather than come up with some
     * overly complex code to handle everything.
     */
    switch(type) {

    /*
     * A fruit is depicted as a small red circle that with a bit of padding
     * on each side.
     */
    case Fruit:
        g.setColor(Color.RED);
        g.fillOval(x + 2, y + 2, TILE_SIZE - 4, TILE_SIZE - 4);
        break;

    /*
     * The snake body is depicted as a green square that takes up the
     * entire tile.
     */
    case SnakeBody:
        g.setColor(Color.GREEN);
        g.fillRect(x, y, TILE_SIZE, TILE_SIZE);
        break;

    /*
     * The snake head is depicted similarly to the body, but with two
     * lines (representing eyes) that indicate it's direction.
     */
    case SnakeHead:
        //Fill the tile in with green.
        g.setColor(Color.GREEN);
        g.fillRect(x, y, TILE_SIZE, TILE_SIZE);

        //Set the color to black so that we can start drawing the eyes.
        g.setColor(Color.BLACK);

        /*
         * The eyes will always 'face' the direction that the snake is
         * moving.
         * 
         * Vertical lines indicate that it's facing North or South, and
         * Horizontal lines indicate that it's facing East or West.
         * 
         * Additionally, the eyes will be closer to whichever edge it's
         * facing.
         * 
         * Drawing the eyes is fairly simple, but is a bit difficult to
         * explain. The basic process is this:
         * 
         * First, we add (or subtract) EYE_SMALL_INSET to or from the
         * side of the tile representing the direction we're facing. This
         * will be constant for both eyes, and is represented by the
         * variable 'baseX' or 'baseY' (depending on orientation).
         * 
         * Next, we add (or subtract) EYE_LARGE_INSET to and from the two
         * neighboring directions (Example; East and West if we're facing
         * north).
         * 
         * Finally, we draw a line from the base offset that is EYE_LENGTH
         * pixels in length at whatever the offset is from the neighboring
         * directions.
         * 
         */
        switch(game.getDirection()) {
        case North: {
            int baseY = y + EYE_SMALL_INSET;
            g.drawLine(x + EYE_LARGE_INSET, baseY, x + EYE_LARGE_INSET, baseY + EYE_LENGTH);
            g.drawLine(x + TILE_SIZE - EYE_LARGE_INSET, baseY, x + TILE_SIZE - EYE_LARGE_INSET, baseY + EYE_LENGTH);
            break;
        }

        case South: {
            int baseY = y + TILE_SIZE - EYE_SMALL_INSET;
            g.drawLine(x + EYE_LARGE_INSET, baseY, x + EYE_LARGE_INSET, baseY - EYE_LENGTH);
            g.drawLine(x + TILE_SIZE - EYE_LARGE_INSET, baseY, x + TILE_SIZE - EYE_LARGE_INSET, baseY - EYE_LENGTH);
            break;
        }

        case West: {
            int baseX = x + EYE_SMALL_INSET;
            g.drawLine(baseX, y + EYE_LARGE_INSET, baseX + EYE_LENGTH, y + EYE_LARGE_INSET);
            g.drawLine(baseX, y + TILE_SIZE - EYE_LARGE_INSET, baseX + EYE_LENGTH, y + TILE_SIZE - EYE_LARGE_INSET);
            break;
        }

        case East: {
            int baseX = x + TILE_SIZE - EYE_SMALL_INSET;
            g.drawLine(baseX, y + EYE_LARGE_INSET, baseX - EYE_LENGTH, y + EYE_LARGE_INSET);
            g.drawLine(baseX, y + TILE_SIZE - EYE_LARGE_INSET, baseX - EYE_LENGTH, y + TILE_SIZE - EYE_LARGE_INSET);
            break;
        }

        }
        break;
    }
}

}

SidePanel:

package Original;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.JPanel;

/**
 * The {@code SidePanel} class is responsible for displaying statistics and
 * controls to the player.
 * @author Brendan Jones
 *
 */

public class SidePanel extends JPanel {

/**
 * Serial Version UID.
 */
private static final long serialVersionUID = -40557434900946408L;

/**
 * The large font to draw with.
 */
private static final Font LARGE_FONT = new Font("Tahoma", Font.BOLD, 20);

/**
 * The medium font to draw with.
 */
private static final Font MEDIUM_FONT = new Font("Tahoma", Font.BOLD, 16);

/**
 * The small font to draw with.
 */
private static final Font SMALL_FONT = new Font("Tahoma", Font.BOLD, 12);

/**
 * The SnakeGame instance.
 */
private SnakeGame game;

/**
 * Creates a new SidePanel instance.
 * @param game The SnakeGame instance.
 */
public SidePanel(SnakeGame game) {
    this.game = game;

    setPreferredSize(new Dimension(300, BoardPanel.ROW_COUNT * BoardPanel.TILE_SIZE));
    setBackground(Color.BLACK);
}

private static final int STATISTICS_OFFSET = 150;

private static final int CONTROLS_OFFSET = 320;

private static final int MESSAGE_STRIDE = 30;

private static final int SMALL_OFFSET = 30;

private static final int LARGE_OFFSET = 50;

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);

    /*
     * Set the color to draw the font in to white.
     */
    g.setColor(Color.WHITE);

    /*
     * Draw the game name onto the window.
     */
    g.setFont(LARGE_FONT);
    g.drawString("Snake Game", getWidth() / 2 - g.getFontMetrics().stringWidth("Snake Game") / 2, 50);

    /*
     * Draw the categories onto the window.
     */
    g.setFont(MEDIUM_FONT);
    g.drawString("Statistics", SMALL_OFFSET, STATISTICS_OFFSET);
    g.drawString("Controls", SMALL_OFFSET, CONTROLS_OFFSET);

    /*
     * Draw the category content onto the window.
     */
    g.setFont(SMALL_FONT);

    //Draw the content for the statistics category.
    int drawY = STATISTICS_OFFSET;
    g.drawString("Total Score: " + game.getScore(), LARGE_OFFSET, drawY += MESSAGE_STRIDE);
    g.drawString("Fruit Eaten: " + game.getFruitsEaten(), LARGE_OFFSET, drawY += MESSAGE_STRIDE);
    g.drawString("Fruit Score: " + game.getNextFruitScore(), LARGE_OFFSET, drawY += MESSAGE_STRIDE);
    //Draw the content for the controls category.
    drawY = CONTROLS_OFFSET;
    g.drawString("Move Up: W / Up Arrowkey", LARGE_OFFSET, drawY += MESSAGE_STRIDE);
    g.drawString("Move Down: S / Down Arrowkey", LARGE_OFFSET, drawY += MESSAGE_STRIDE);
    g.drawString("Move Left: A / Left Arrowkey", LARGE_OFFSET, drawY += MESSAGE_STRIDE);
    g.drawString("Move Right: D / Right Arrowkey", LARGE_OFFSET, drawY += MESSAGE_STRIDE);
    g.drawString("Pause Game: P", LARGE_OFFSET, drawY += MESSAGE_STRIDE);
}

}
  • Side recommendation: `catch (Exception e){}` -- you don't want to do this, to leave catch blocks empty, trust me. – Hovercraft Full Of Eels Jan 08 '16 at 05:09
  • 1
    Main recommendation: look up "variable shadowing" because that's what you're doing -- you're shadowing at least the `board` variable by declaring it twice, once in the class and again in the constructor, and maybe others. By re-declaring the variable in the constructor and by initializing that local constructor variable, you leave the class field null -- don't do this. For more on this, please read: http://stackoverflow.com/questions/1092099/what-is-variable-shadowing-used-for-in-a-java-class – Hovercraft Full Of Eels Jan 08 '16 at 05:11
  • 1
    So in the constructor, change `BoardPanel board = new BoardPanel(this);` to `board = new BoardPanel(this);` (note the difference) and do the same for your side variable, so that now you initialize the class field, and not some shadow variable that is only visible from within the constructor. Make sense? – Hovercraft Full Of Eels Jan 08 '16 at 05:14
  • I have made the changes already, now when i run the thing nothing pops up, and i dont really understand what u mean by variable shadowing – Roy Johnson Jan 08 '16 at 05:18
  • 1
    Shadowing here means that you're declaring your variable more than once, and note that you in fact do so -- you declare both board and side in the class and in the constructor. When you do this, you create two variables, the constructor variable which is initialized and so is not null, but is only visible within the constructor, and the class field which is never initialized and so is null, and is visible throughout the class. – Hovercraft Full Of Eels Jan 08 '16 at 05:21
  • Please look at my answer to a similar question [here](http://stackoverflow.com/questions/25910918/java-swing-radio-buttons-java-lang-nullpointerexception/25910943#25910943). – Hovercraft Full Of Eels Jan 08 '16 at 05:26

0 Answers0