蛇游戏(JAVA)

时间:2016-01-08 05:06:00

标签: java swing

我在网上找到了这些代码,但它们最初是在JFrame中。现在我想做的是我想把这个JFrame变成一个面板。我想我已经为添加面板做了正确的更改。但是当我运行它时会出现"java.lang.NullPointerException"错误,而我的eclipse会说SnakeGamePanel.java:339处的错误是repaint()方法中的Startgame()错误。以下代码是我的3个面板,SnakeGamePanel,BoardPanel,SidePanel。如果我错误地添加了面板,请指导我如何将BoardPanel和SidePanel添加到SnakeGamePanel中。

游戏来源: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;
    }
}

}

侧面板:

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);
}

}

0 个答案:

没有答案