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

Поділитися
Вставка
  • Опубліковано 29 жов 2024

КОМЕНТАРІ • 31

  • @willtollefson
    @willtollefson  Рік тому +10

    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  6 місяців тому

      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 6 місяців тому

      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  6 місяців тому

      @@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 6 місяців тому

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

  • @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

  • @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 :)

  • @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

  • @عبدالرازقنادري
    @عبدالرازقنادري 4 дні тому +1

    Thank you for the lesson

  • @EliasDirksen-vw2nq
    @EliasDirksen-vw2nq 5 місяців тому +1

    Thank you very much Will

  • @jaylordjl6337
    @jaylordjl6337 Рік тому +5

    Nice ❤

  • @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 5 місяців тому

      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?