构建Snake Game的ListNode实现

时间:2019-02-26 03:30:05

标签: java

我一般是ListNodes和节点的新手。我已经为我的游戏成功构建了起始蛇,并且按计划运行。我一直在努力弄清楚如何向蛇添加其他链接。我还认为代码中有一些地方可以极大地简化事情。在简化或更好地创建此应用程序的地方的任何建议,将不胜感激。

`

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.animation.AnimationTimer;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;

/**
 *  A Snake game.  A snake consisting of a sequence of circles moves around on the board.
 *  The user controls the direction of motion with the arrow keys.  The snake sometimes
 *  grows a new segment.  If the snake tries to move outside the board, or if the head
 *  of snake collides with one of its body segments, then the game is over.
 */
public class Snake extends Application {

    private final int SQUARE_SIZE = 15;  // The size of each snake segment; the size of one snake segment.
    private final int ROWS = 40, COLUMNS  = 50; /* The board is made up of squares that can each hold
                                                  one snaked segment.  These constants give the number of
                                                  rows and the number of columns of squares.  The snake head
                                                  moves one full square up, down, left, or right in each frame
                                                  of the animation. */

    private Canvas canvas;      // The canvas that fills the entire window, where all drawing is done.
    private GraphicsContext g;  // A graphics context for drawing on the canvas.
    private boolean running;    // Set to true when the animation is running (and the snake is moving.)
    private boolean gameover;   // Set to true when the game ends because of a crash.
    private AnimationTimer animator; // Timer that drives the animation.
    private int direction = 1;  // Current direction of motion.  1, 2, 3, 4 for RIGHT, LEFT, UP, DOWN.
    private int frameNumber;    // Frame number of the current frame; starts at 0 when a game is started.

    private static class ListNode {
        int atX;
        int atY;
        ListNode next;
        ListNode prev;



        public ListNode(int x, int y) {
            atX = x;
            atY = y;
            next = null;

        }
        public ListNode(int x, int y, ListNode n) {
            atX = x;
            atY = y;
            next = n;

        }
    }

    private static ListNode head;




    /**
     * Set up for a new game of Snake.  A new snake is created, in its initial position.  Animation is turned off.
     * The gameover variable is false.  A temporary message, "Press space bar to start", is displayed on the canvas.
     */
    private void startGame() {
        int x = ROWS/2;
        int y = (COLUMNS/2);
        head = new ListNode(x,y-4);
        ListNode b = new ListNode(x,y-3,head);
        head = b;
        ListNode c = new ListNode(x,y-2,head);
        head = c;
        ListNode d = new ListNode(x,y-1,head);
        head = d;
        ListNode e = new ListNode(x,y,head);
        head = e;
        draw();
        g.setFill(Color.RED.darker());
        g.setFont(Font.font(20));
        g.fillText("Press space bar to start.", canvas.getWidth()/2, canvas.getHeight()/2+60);
        gameover = false;
        direction = 1;
        frameNumber = 0;
    }


    /**
     * This method is called when a crash occurs and the game has to end.  The animation is stopped,
     * The gameover variable becomes true.  A temporary message is added to the canvas saying "GAME OVER".
     */
    private void gameOver() {
        animator.stop();
        running = false;
        gameover = true;
        g.setFill(Color.YELLOW);
        g.setFont(Font.font(80));
        g.fillText("GAME OVER", 40, canvas.getHeight() - 40);
        g.strokeText("GAME OVER", 40, canvas.getHeight() - 40);
    }


    /**
     * This method is called in each frame to test whether moving the snake head to 
     * the given row and column will cause a crash.  A crash happens if the given position
     * is outside the limits of the game board, or if the the given position is already 
     * occupied by one of the segments of the snake.
     */
    private boolean crash(int row, int col) {
        if (row < 0 || row >= ROWS || col < 0 || col >= COLUMNS) { // position outside the board
            return true;
        }
        return false;
    }


    /**
     * Move the snake forward one square.  The snake head should be moved to the given newRow
     * and newCol.  (These values are one square horizontally or vertically away from the
     * current position of the snake head.)  All the other segments of the snake move up
     * one position.  Sometimes a new snake segment is added at the end of the snake; this
     * allows the snake to grow with time.
     */
    private void moveSnake(int newRow, int newCol) {

        int aX = head.next.next.next.next.atX;
        int aY = head.next.next.next.next.atY;
        int bX = head.next.next.next.atX;
        int bY = head.next.next.next.atY;
        int cX = head.next.next.atX;
        int cY = head.next.next.atY;
        int dX = head.next.atX;
        int dY = head.next.atY;
        int eX = head.atX;
        int eY = head.atY;




        head.atX = newRow;
        head.atY = newCol;

        head.next.atX = eX;
        head.next.atY = eY;

        head.next.next.atX = dX;
        head.next.next.atY = dY;

        head.next.next.next.atX = cX;
        head.next.next.next.atY = cY;

        head.next.next.next.next.atX = bX;
        head.next.next.next.next.atY = bY;


        draw();
    }


    /**
     * Fills the canvas with white, then draws the snake in its current position.
     */
    private void draw() {
        GraphicsContext g = canvas.getGraphicsContext2D();
        g.setFill(Color.WHITE);
        g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
        g.setFill(Color.BLUE);
        g.fillOval(head.atY*SQUARE_SIZE, head.atX*SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE);
        for (ListNode runner = head.next; runner != null; runner = runner.next) {
            g.setFill(Color.PINK);
            g.fillOval(runner.atY*SQUARE_SIZE, runner.atX*SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE);
            }

    }


    /**
     * This method is called for each frame of the animation.  It moves the snake
     * by one square in the current direction of motion, unless doing so would
     * cause a crash.  It a crash would occur, the game is terminated instead.
     */
    private void doFrame() {
        if (!running)
            return;
        frameNumber++;  // Frame number goes up by 1 for each frame.
        int newRow = head.atX; // Next row for the position of the snake head.
        int newCol = head.atY; // Next column for the position of the snake head.
        switch (direction) {
        case 1:  // right
            newCol++;
            break;
        case 2:  // left
            newCol--;
            break;
        case 3:  // up
            newRow--;
            break;
        case 4:  // down
            newRow++;
            break;
        }
        if (crash(newRow,newCol)) {
               // Moving the snake would cause a crash.  End the game.
            gameOver();
        }
        else {
            moveSnake(newRow,newCol);
        }
    }


    /**
     * This method is called when the user presses any keyboard key.
     * Arrow keys change the direction of motion of the snake.
     * The space key starts and stops the game action (but can only
     * be started if the game is not over).  The escape key can
     * be used to close the window and end the program.  Note: Arrow
     * keys are ignored if the game is not running.  Also, an arrow key
     * will not reverse the direction of motion, since that would lead
     * to an immediate crash when the head hits the 2nd snake segment.
     */
    private void doKeyEvent(KeyEvent evt) {
        KeyCode code = evt.getCode();
        if (code == KeyCode.RIGHT) {
            if (running && direction != 2)
                direction = 1;
        }
        else if (code == KeyCode.LEFT) {
            if (running && direction != 1)
                direction = 2;
        }
        else if (code == KeyCode.UP) {
            if (running && direction != 4)
                direction = 3;
        }
        else if (code == KeyCode.DOWN) {
            if (running && direction != 3)
                direction = 4;
        }
        else if (code == KeyCode.SPACE) {
            if (running) { // Pause the action.
                animator.stop();
                running = false;
            }
            else if ( !gameover ) { // Start animation, but only if gameover is false.
                animator.start();
                running = true;
            }
        }
        else if (code == KeyCode.ESCAPE) {
            Platform.exit();  // End the program.
        }
    }


    /**
     * Start method creates the window content and configures event listening.
     */
    public void start(Stage stage) {
        canvas = new Canvas(COLUMNS*SQUARE_SIZE, ROWS*SQUARE_SIZE);
        g = canvas.getGraphicsContext2D();
        Pane root = new Pane(canvas);
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Snake Games");
        stage.setResizable(false);
        scene.setOnKeyPressed( evt -> doKeyEvent(evt) );
        animator = new AnimationTimer( ) {
            int tickCount = 0;
            public void handle(long time) {
                tickCount++;
                if (tickCount == 5) {
                       // There will only be 12 frames per second.
                    doFrame();
                    tickCount = 0;
                }
            }
        };
        startGame();
        stage.show();
    } // end start()

    public static void main(String[] args) {
        launch(args);
    }

} // end class Snake`

1 个答案:

答案 0 :(得分:0)

看起来您不必使用自己的链表实现。因此,您可以使用LinkedList或其他JDK集合。

public class DemoApplication {

    public static class Point {
        private int x;
        private int y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public Point(Point other) {
            this.x = other.x;
            this.y = other.y;
        }

        // Getters and setters
    }

    public static void main(String[] args) {
        LinkedList<Point> list = new LinkedList<>();

        // Add points to the list
        list.addFirst(new Point(4,5));
        list.addLast(new Point(1,2));

        // Get head
        list.getFirst();

        // Get point by index
        list.get(0).getX();
        list.get(1).getY();
    }
}

另一个建议。您不必只在一个类中编写完整的程序。这是糟糕的设计解决方案。您必须分解您的代码。至少,将所有游戏逻辑代码移至一个(或多个)类,并将UI代码移至另一个。查看MVC设计模式,可能会有所帮助。