├── App.java ├── README.md └── SnakeGame.java /App.java: -------------------------------------------------------------------------------- 1 | import javax.swing.*; 2 | 3 | public class App { 4 | public static void main(String[] args) throws Exception { 5 | int boardWidth = 600; 6 | int boardHeight = boardWidth; 7 | 8 | JFrame frame = new JFrame("Snake"); 9 | frame.setVisible(true); 10 | frame.setSize(boardWidth, boardHeight); 11 | frame.setLocationRelativeTo(null); 12 | frame.setResizable(false); 13 | frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 14 | 15 | SnakeGame snakeGame = new SnakeGame(boardWidth, boardHeight); 16 | frame.add(snakeGame); 17 | frame.pack(); 18 | snakeGame.requestFocus(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Snake (Java)](https://youtu.be/Y62MJny9LHg) 2 | 3 | How to code a snake game in Java for beginners! Learn how to create a game of snake in Java using the awt and swing graphics library. Throughout the tutorial, you will learn how to create a graphical user interface (GUI) to display the Snake game, create the game loop, listen for Key presses to move the snake, use the ArrayList to store the snake's segments, and check collision and out of bounds for game over. 4 | 5 | [How to setup Java with Visual Studio Code](https://youtu.be/BB0gZFpukJU) 6 | 7 | ![snake-java-demo](https://github.com/ImKennyYip/snake-java/assets/78777681/047a8e7c-1f8e-4059-af08-5dd0197f7543) 8 | 9 | ## Homework: 10 | You can continue working on this project if you like. One feature you can add is a keyListener in KeyPressed to restart the game every time there is a game over. ex) if (e.getKeyCode() == KeyEvent.VK_SPACE && gameOver). Inside this conditional statement, you should reset the game to default (clear snakeBody ArrayList, call placeFood(), etc). Another feature you can add after this is keep track of the high score and paint it below the current score. For this you will need a variable to keep track of high score. 11 | -------------------------------------------------------------------------------- /SnakeGame.java: -------------------------------------------------------------------------------- 1 | import java.awt.*; 2 | import java.awt.event.*; 3 | import java.util.ArrayList; 4 | import java.util.Random; 5 | import javax.swing.*; 6 | 7 | public class SnakeGame extends JPanel implements ActionListener, KeyListener { 8 | private class Tile { 9 | int x; 10 | int y; 11 | 12 | Tile(int x, int y) { 13 | this.x = x; 14 | this.y = y; 15 | } 16 | } 17 | 18 | int boardWidth; 19 | int boardHeight; 20 | int tileSize = 25; 21 | 22 | //snake 23 | Tile snakeHead; 24 | ArrayList snakeBody; 25 | 26 | //food 27 | Tile food; 28 | Random random; 29 | 30 | //game logic 31 | int velocityX; 32 | int velocityY; 33 | Timer gameLoop; 34 | 35 | boolean gameOver = false; 36 | 37 | SnakeGame(int boardWidth, int boardHeight) { 38 | this.boardWidth = boardWidth; 39 | this.boardHeight = boardHeight; 40 | setPreferredSize(new Dimension(this.boardWidth, this.boardHeight)); 41 | setBackground(Color.black); 42 | addKeyListener(this); 43 | setFocusable(true); 44 | 45 | snakeHead = new Tile(5, 5); 46 | snakeBody = new ArrayList(); 47 | 48 | food = new Tile(10, 10); 49 | random = new Random(); 50 | placeFood(); 51 | 52 | velocityX = 1; 53 | velocityY = 0; 54 | 55 | //game timer 56 | gameLoop = new Timer(100, this); //how long it takes to start timer, milliseconds gone between frames 57 | gameLoop.start(); 58 | } 59 | 60 | public void paintComponent(Graphics g) { 61 | super.paintComponent(g); 62 | draw(g); 63 | } 64 | 65 | public void draw(Graphics g) { 66 | //Grid Lines 67 | for(int i = 0; i < boardWidth/tileSize; i++) { 68 | //(x1, y1, x2, y2) 69 | g.drawLine(i*tileSize, 0, i*tileSize, boardHeight); 70 | g.drawLine(0, i*tileSize, boardWidth, i*tileSize); 71 | } 72 | 73 | //Food 74 | g.setColor(Color.red); 75 | // g.fillRect(food.x*tileSize, food.y*tileSize, tileSize, tileSize); 76 | g.fill3DRect(food.x*tileSize, food.y*tileSize, tileSize, tileSize, true); 77 | 78 | //Snake Head 79 | g.setColor(Color.green); 80 | // g.fillRect(snakeHead.x, snakeHead.y, tileSize, tileSize); 81 | // g.fillRect(snakeHead.x*tileSize, snakeHead.y*tileSize, tileSize, tileSize); 82 | g.fill3DRect(snakeHead.x*tileSize, snakeHead.y*tileSize, tileSize, tileSize, true); 83 | 84 | //Snake Body 85 | for (int i = 0; i < snakeBody.size(); i++) { 86 | Tile snakePart = snakeBody.get(i); 87 | // g.fillRect(snakePart.x*tileSize, snakePart.y*tileSize, tileSize, tileSize); 88 | g.fill3DRect(snakePart.x*tileSize, snakePart.y*tileSize, tileSize, tileSize, true); 89 | } 90 | 91 | //Score 92 | g.setFont(new Font("Arial", Font.PLAIN, 16)); 93 | if (gameOver) { 94 | g.setColor(Color.red); 95 | g.drawString("Game Over: " + String.valueOf(snakeBody.size()), tileSize - 16, tileSize); 96 | } 97 | else { 98 | g.drawString("Score: " + String.valueOf(snakeBody.size()), tileSize - 16, tileSize); 99 | } 100 | } 101 | 102 | public void placeFood(){ 103 | food.x = random.nextInt(boardWidth/tileSize); 104 | food.y = random.nextInt(boardHeight/tileSize); 105 | } 106 | 107 | public void move() { 108 | //eat food 109 | if (collision(snakeHead, food)) { 110 | snakeBody.add(new Tile(food.x, food.y)); 111 | placeFood(); 112 | } 113 | 114 | //move snake body 115 | for (int i = snakeBody.size()-1; i >= 0; i--) { 116 | Tile snakePart = snakeBody.get(i); 117 | if (i == 0) { //right before the head 118 | snakePart.x = snakeHead.x; 119 | snakePart.y = snakeHead.y; 120 | } 121 | else { 122 | Tile prevSnakePart = snakeBody.get(i-1); 123 | snakePart.x = prevSnakePart.x; 124 | snakePart.y = prevSnakePart.y; 125 | } 126 | } 127 | //move snake head 128 | snakeHead.x += velocityX; 129 | snakeHead.y += velocityY; 130 | 131 | //game over conditions 132 | for (int i = 0; i < snakeBody.size(); i++) { 133 | Tile snakePart = snakeBody.get(i); 134 | 135 | //collide with snake head 136 | if (collision(snakeHead, snakePart)) { 137 | gameOver = true; 138 | } 139 | } 140 | 141 | if (snakeHead.x*tileSize < 0 || snakeHead.x*tileSize > boardWidth || //passed left border or right border 142 | snakeHead.y*tileSize < 0 || snakeHead.y*tileSize > boardHeight ) { //passed top border or bottom border 143 | gameOver = true; 144 | } 145 | } 146 | 147 | public boolean collision(Tile tile1, Tile tile2) { 148 | return tile1.x == tile2.x && tile1.y == tile2.y; 149 | } 150 | 151 | @Override 152 | public void actionPerformed(ActionEvent e) { //called every x milliseconds by gameLoop timer 153 | move(); 154 | repaint(); 155 | if (gameOver) { 156 | gameLoop.stop(); 157 | } 158 | } 159 | 160 | @Override 161 | public void keyPressed(KeyEvent e) { 162 | // System.out.println("KeyEvent: " + e.getKeyCode()); 163 | if (e.getKeyCode() == KeyEvent.VK_UP && velocityY != 1) { 164 | velocityX = 0; 165 | velocityY = -1; 166 | } 167 | else if (e.getKeyCode() == KeyEvent.VK_DOWN && velocityY != -1) { 168 | velocityX = 0; 169 | velocityY = 1; 170 | } 171 | else if (e.getKeyCode() == KeyEvent.VK_LEFT && velocityX != 1) { 172 | velocityX = -1; 173 | velocityY = 0; 174 | } 175 | else if (e.getKeyCode() == KeyEvent.VK_RIGHT && velocityX != -1) { 176 | velocityX = 1; 177 | velocityY = 0; 178 | } 179 | } 180 | 181 | //not needed 182 | @Override 183 | public void keyTyped(KeyEvent e) {} 184 | 185 | @Override 186 | public void keyReleased(KeyEvent e) {} 187 | } 188 | --------------------------------------------------------------------------------