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.
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!
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!!
@@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!
from this tut i have got to understand some basics in java like eventlisteners and adpters. Truly your experience superceeds you, # Never stop learning
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.
@@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?
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
}
}
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.
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!
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!!
@@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!
@@willtollefson I understand, I will learn how to use those this school break. Thank you for your suggestions, I appreciate it :33
from this tut i have got to understand some basics in java like eventlisteners and adpters. Truly your experience superceeds you, # Never stop learning
Thanks so much! Glad to hear you thought this was helpful
Hi. I'm a foreign subscriber.
Thank you for making the video by attaching the subtitles.
I hope this channel will be more popular.
Thanks for the comment! I'm hopeful the channel will grow in popularity over time as well :)
Fantastic tutorial, Will! I love how you explain everything in detail 😊
Glad you liked it!
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.
Hey Pablo! I'm doing well. This was a fun video to make :)
Awesome tutorial! I love that make learning fun here by making a classic game!
Thanks! This was definitely a fun video to make
Thank you for the lesson
You’re welcome!
Thank you very much Will
Nice ❤
Thanks!
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?
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.
private final LinkedList snake = new LinkedList(); use this instead of private final List snake = new ArrayList();
Bro finally I found someone who has the same doubt as mine😂
@@willtollefsonmine is latest version but it still shows error
@@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?