Building a Snake Game in Java: A Complete Walkthrough - Java Programming

แชร์
ฝัง
  • เผยแพร่เมื่อ 21 ต.ค. 2024

ความคิดเห็น • 29

  • @willtollefson
    @willtollefson  ปีที่แล้ว +9

    Hi everyone! Here's the game's full source code.
    Main.java
    import javax.swing.JFrame;
    public class Main {
    private static final int WIDTH = 800;
    private static final int HEIGHT = 600;
    public static void main(String[] args) {
    final JFrame frame = new JFrame("Snake Game");
    frame.setSize(WIDTH, HEIGHT);
    final SnakeGame game = new SnakeGame(WIDTH, HEIGHT);
    frame.add(game);
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setResizable(false);
    frame.setVisible(true);
    frame.pack();
    game.startGame();
    }
    }
    SnakeGame.java
    import javax.swing.JPanel;
    import javax.swing.Timer;
    import java.awt.Color;
    import java.awt.Dimension;
    import java.awt.Graphics;
    import java.awt.Graphics2D;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    import java.awt.font.TextLayout;
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Random;
    public class SnakeGame extends JPanel implements ActionListener {
    private final int width;
    private final int height;
    private final int cellSize;
    private final Random random = new Random();
    private static final int FRAME_RATE = 20;
    private boolean gameStarted = false;
    private boolean gameOver = false;
    private int highScore;
    private GamePoint food;
    private Direction direction = Direction.RIGHT;
    private Direction newDirection = Direction.RIGHT;
    private final List snake = new ArrayList();
    public SnakeGame(final int width, final int height) {
    this.width = width;
    this.height = height;
    this.cellSize = width / (FRAME_RATE * 2);
    setPreferredSize(new Dimension(width, height));
    setBackground(Color.BLACK);
    }
    public void startGame() {
    resetGameData();
    setFocusable(true);
    setFocusTraversalKeysEnabled(false);
    requestFocusInWindow();
    addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(final KeyEvent e) {
    handleKeyEvent(e.getKeyCode());
    }
    });
    new Timer(1000 / FRAME_RATE, this).start();
    }
    private void handleKeyEvent(final int keyCode) {
    if (!gameStarted) {
    if (keyCode == KeyEvent.VK_SPACE) {
    gameStarted = true;
    }
    } else if (!gameOver) {
    switch (keyCode) {
    case KeyEvent.VK_UP:
    if (direction != Direction.DOWN) {
    newDirection = Direction.UP;
    }
    break;
    case KeyEvent.VK_DOWN:
    if (direction != Direction.UP) {
    newDirection = Direction.DOWN;
    }
    break;
    case KeyEvent.VK_RIGHT:
    if (direction != Direction.LEFT) {
    newDirection = Direction.RIGHT;
    }
    break;
    case KeyEvent.VK_LEFT:
    if (direction != Direction.RIGHT) {
    newDirection = Direction.LEFT;
    }
    break;
    }
    } else if (keyCode == KeyEvent.VK_SPACE) {
    gameStarted = false;
    gameOver = false;
    resetGameData();
    }
    }
    private void resetGameData() {
    snake.clear();
    snake.add(new GamePoint(width / 2, height / 2));
    generateFood();
    }
    private void generateFood() {
    do {
    food = new GamePoint(random.nextInt(width / cellSize) * cellSize,
    random.nextInt(height / cellSize) * cellSize);
    } while (snake.contains(food));
    }
    @Override
    protected void paintComponent(final Graphics graphics) {
    super.paintComponent(graphics);
    if (!gameStarted) {
    printMessage(graphics, "Press Space Bar to Begin Game");
    } else {
    graphics.setColor(Color.cyan);
    graphics.fillRect(food.x, food.y, cellSize, cellSize);
    Color snakeColor = Color.GREEN;
    for (final var point : snake) {
    graphics.setColor(snakeColor);
    graphics.fillRect(point.x, point.y, cellSize, cellSize);
    final int newGreen = (int) Math.round(snakeColor.getGreen() * (0.95));
    snakeColor = new Color(0, newGreen, 0);
    }
    if (gameOver) {
    final int currentScore = snake.size();
    if (currentScore > highScore) {
    highScore = currentScore;
    }
    printMessage(graphics, "Your Score: " + currentScore
    + "
    High Score: " + highScore
    + "
    Press Space Bar to Reset");
    }
    }
    }
    private void printMessage(final Graphics graphics, final String message) {
    graphics.setColor(Color.WHITE);
    graphics.setFont(graphics.getFont().deriveFont(30F));
    int currentHeight = height / 3;
    final var graphics2D = (Graphics2D) graphics;
    final var frc = graphics2D.getFontRenderContext();
    for (final var line : message.split("
    ")) {
    final var layout = new TextLayout(line, graphics.getFont(), frc);
    final var bounds = layout.getBounds();
    final var targetWidth = (float) (width - bounds.getWidth()) / 2;
    layout.draw(graphics2D, targetWidth, currentHeight);
    currentHeight += graphics.getFontMetrics().getHeight();
    }
    }
    private void move() {
    direction = newDirection;
    final GamePoint head = snake.getFirst();
    final GamePoint newHead = switch (direction) {
    case UP -> new GamePoint(head.x, head.y - cellSize);
    case DOWN -> new GamePoint(head.x, head.y + cellSize);
    case LEFT -> new GamePoint(head.x - cellSize, head.y);
    case RIGHT -> new GamePoint(head.x + cellSize, head.y);
    };
    snake.addFirst(newHead);
    if (newHead.equals(food)) {
    generateFood();
    } else if (isCollision()) {
    gameOver = true;
    snake.removeFirst();
    } else {
    snake.removeLast();
    }
    }
    private boolean isCollision() {
    final GamePoint head = snake.getFirst();
    final var invalidWidth = (head.x < 0) || (head.x >= width);
    final var invalidHeight = (head.y < 0) || (head.y >= height);
    if (invalidWidth || invalidHeight) {
    return true;
    }
    return snake.size() != new HashSet(snake).size();
    }
    @Override
    public void actionPerformed(final ActionEvent e) {
    if (gameStarted && !gameOver) {
    move();
    }
    repaint();
    }
    private record GamePoint(int x, int y) {
    }
    private enum Direction {
    UP, DOWN, RIGHT, LEFT
    }
    }

    • @shubhamkanozia5886
      @shubhamkanozia5886 6 หลายเดือนก่อน

      Hey, thanks for the explanation, but I believe there is an issue in your code that you commented here because my IDE is giving an error at compile time, i.e., 'GamePoint cannot be resolved to a type.' It is telling me to create the class.

    • @willtollefson
      @willtollefson  5 หลายเดือนก่อน

      I'm wondering if maybe you're using a JDK that doesn't have records in it yet? GamePoint is a record type and record was a preview feature in JDK 14 with full support added in JDK 16. Hopefully this helps!

    • @sonkhyle1357
      @sonkhyle1357 5 หลายเดือนก่อน

      hi im a 1st yr senior hs student on prog and can I ask if this is possible to compile in notepad? that's the only thing that we got taught to use and I'm in a hurry to find games rn bc we were only given a week to make a GUI project alongside a manual. Thank you!!

    • @willtollefson
      @willtollefson  5 หลายเดือนก่อน

      @@sonkhyle1357 I personally haven’t used notepad or notepad++ as a Java development tool for code compilation. I’m used to those being text editors. There might be some plugin support for this, but I’d trend toward either using the command line to compile the code or using an IDE like IntelliJ or Eclipse. It will make your life easier down the road. Good luck!

    • @sonkhyle1357
      @sonkhyle1357 5 หลายเดือนก่อน

      @@willtollefson I understand, I will learn how to use those this school break. Thank you for your suggestions, I appreciate it :33

  • @sinjungmin
    @sinjungmin ปีที่แล้ว +5

    Hi. I'm a foreign subscriber.
    Thank you for making the video by attaching the subtitles.
    I hope this channel will be more popular.

    • @willtollefson
      @willtollefson  ปีที่แล้ว +2

      Thanks for the comment! I'm hopeful the channel will grow in popularity over time as well :)

  • @miriamwanjohi2388
    @miriamwanjohi2388 8 หลายเดือนก่อน +4

    from this tut i have got to understand some basics in java like eventlisteners and adpters. Truly your experience superceeds you, # Never stop learning

    • @willtollefson
      @willtollefson  8 หลายเดือนก่อน

      Thanks so much! Glad to hear you thought this was helpful

  • @DenaTollefson
    @DenaTollefson ปีที่แล้ว +7

    Fantastic tutorial, Will! I love how you explain everything in detail 😊

  • @pablopronsky7364
    @pablopronsky7364 ปีที่แล้ว +5

    Hey Will, I hope you are doing well!
    I'll try this out later today and update you.
    Have the best of weekends, Professor.
    Greetings from Argentina.

    • @willtollefson
      @willtollefson  ปีที่แล้ว +2

      Hey Pablo! I'm doing well. This was a fun video to make :)

  • @malloryranae8069
    @malloryranae8069 ปีที่แล้ว +4

    Awesome tutorial! I love that make learning fun here by making a classic game!

    • @willtollefson
      @willtollefson  ปีที่แล้ว +1

      Thanks! This was definitely a fun video to make

  • @EliasDirksen-vw2nq
    @EliasDirksen-vw2nq 5 หลายเดือนก่อน +1

    Thank you very much Will

  • @legitmorphman22
    @legitmorphman22 5 หลายเดือนก่อน +3

    I am a bit confused how you are using snake.addFirst() or snake.removeFirst(). These are not methods of ArrayList so where are they coming from?

    • @willtollefson
      @willtollefson  5 หลายเดือนก่อน

      A lot of those are default methods added to the List interface in JDK 21, so ArrayList will have them in recent JDK versions. I believe some of these were available in some List implementations in prior JDKs, like LinkedList, as well.

    • @mangalamshukla825
      @mangalamshukla825 4 หลายเดือนก่อน

      private final LinkedList snake = new LinkedList(); use this instead of private final List snake = new ArrayList();

    • @RupaRani-mu8qw
      @RupaRani-mu8qw 2 หลายเดือนก่อน

      Bro finally I found someone who has the same doubt as mine😂

    • @RupaRani-mu8qw
      @RupaRani-mu8qw 2 หลายเดือนก่อน

      ​@@willtollefsonmine is latest version but it still shows error

    • @willtollefson
      @willtollefson  2 หลายเดือนก่อน

      @@RupaRani-mu8qw here's a JDK 21 javadoc reference for addFirst. Its part of the List interface in JDK 21:
      docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/List.html#addFirst(E)
      Are you trying to run this against JDK 21 specifically?

  • @jaylordjl6337
    @jaylordjl6337 ปีที่แล้ว +5

    Nice ❤