Created
May 20, 2023 05:55
-
-
Save R3DHULK/768d25bb51b97ab63a845c3a692b003e to your computer and use it in GitHub Desktop.
Snake Game In Java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.*; | |
import javax.imageio.ImageIO; | |
import java.util.Timer; | |
import java.awt.*; | |
import java.awt.event.*; | |
import java.awt.image.*; | |
import java.io.*; | |
import javax.swing.*; | |
public class Main extends JFrame { | |
public static void main(String[] args) { | |
EventQueue.invokeLater(() -> { | |
Main ex = new Main(); | |
ex.setVisible(true); | |
}); | |
} | |
public Main() { | |
initUI(); | |
} | |
private void initUI() { | |
add(new Game()); | |
setTitle("Snake"); | |
setSize(800, 610); | |
setLocationRelativeTo(null); | |
setResizable(false); | |
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
} | |
@Override | |
public String toString() { | |
return "Main []"; | |
} | |
} | |
class Game extends JPanel { | |
// game loop | |
private class KeyListener extends KeyAdapter { | |
@Override | |
public void keyPressed(KeyEvent e) { | |
int key = e.getKeyCode(); | |
if (status == GameStatus.RUNNING) { | |
switch(key) { | |
case KeyEvent.VK_LEFT: snake.turn(Direction.LEFT); break; | |
case KeyEvent.VK_RIGHT: snake.turn(Direction.RIGHT); break; | |
case KeyEvent.VK_UP: snake.turn(Direction.UP); break; | |
case KeyEvent.VK_DOWN: snake.turn(Direction.DOWN); break; | |
} | |
} | |
if (status == GameStatus.NOT_STARTED) { | |
setStatus(GameStatus.RUNNING); | |
} | |
if (status == GameStatus.GAME_OVER && key == KeyEvent.VK_ENTER) { | |
reset(); | |
} | |
if (key == KeyEvent.VK_P) { | |
togglePause(); | |
} | |
} | |
} | |
private class GameLoop extends java.util.TimerTask { | |
public void run() { | |
update(); | |
repaint(); | |
} | |
} | |
private static Font FONT_M = new Font("MV Boli", Font.PLAIN, 24); | |
private static Font FONT_M_ITALIC = new Font("MV Boli", Font.ITALIC, 24); | |
private static Font FONT_L = new Font("MV Boli", Font.PLAIN, 84); | |
private static Font FONT_XL = new Font("MV Boli", Font.PLAIN, 150); | |
private static int WIDTH = 760; | |
private static int HEIGHT = 520; | |
private static int DELAY = 50; | |
public static Font getFONT_M() { | |
return FONT_M; | |
} | |
public static void setFONT_M(Font fONT_M) { | |
FONT_M = fONT_M; | |
} | |
public static Font getFONT_M_ITALIC() { | |
return FONT_M_ITALIC; | |
} | |
public static void setFONT_M_ITALIC(Font fONT_M_ITALIC) { | |
FONT_M_ITALIC = fONT_M_ITALIC; | |
} | |
public static Font getFONT_L() { | |
return FONT_L; | |
} | |
public static void setFONT_L(Font fONT_L) { | |
FONT_L = fONT_L; | |
} | |
public static Font getFONT_XL() { | |
return FONT_XL; | |
} | |
public static void setFONT_XL(Font fONT_XL) { | |
FONT_XL = fONT_XL; | |
} | |
public static int getWIDTH() { | |
return WIDTH; | |
} | |
public static void setWIDTH(int wIDTH) { | |
WIDTH = wIDTH; | |
} | |
public static int getHEIGHT() { | |
return HEIGHT; | |
} | |
public static void setHEIGHT(int hEIGHT) { | |
HEIGHT = hEIGHT; | |
} | |
public static int getDELAY() { | |
return DELAY; | |
} | |
public static void setDELAY(int dELAY) { | |
DELAY = dELAY; | |
} | |
private Timer timer; | |
private Snake snake; | |
private Point cherry; | |
private int points = 0; | |
private int best = 0; | |
private BufferedImage image; | |
private GameStatus status; | |
private boolean didLoadCherryImage = true; | |
// Constructor | |
public Game() { | |
try { | |
image = ImageIO.read(new File("cherry.png")); | |
} catch (IOException e) { | |
didLoadCherryImage = false; | |
} | |
addKeyListener(new KeyListener()); | |
setFocusable(true); | |
setBackground(new Color(130, 205, 71)); | |
setDoubleBuffered(true); | |
snake = new Snake(WIDTH / 2, HEIGHT / 2); | |
status = GameStatus.NOT_STARTED; | |
repaint(); | |
} | |
@Override | |
public void paintComponent(Graphics g) { | |
super.paintComponent(g); | |
render(g); | |
Toolkit.getDefaultToolkit().sync(); | |
} | |
// Spawn a cherry at a random location | |
public void drawCenteredString(Graphics g, String text, Font font, int y) { | |
FontMetrics metrics = g.getFontMetrics(font); | |
int x = (WIDTH - metrics.stringWidth(text)) / 2; | |
g.setFont(font); | |
g.drawString(text, x, y); | |
} | |
// spawn cherry in random position | |
public void spawnCherry() { | |
cherry = new Point((new Random()).nextInt(WIDTH - 60) + 20, | |
(new Random()).nextInt(HEIGHT - 60) + 40); | |
} | |
public Timer getTimer() { | |
return timer; | |
} | |
public void setTimer(Timer timer) { | |
this.timer = timer; | |
} | |
public Snake getSnake() { | |
return snake; | |
} | |
public void setSnake(Snake snake) { | |
this.snake = snake; | |
} | |
public Point getCherry() { | |
return cherry; | |
} | |
public void setCherry(Point cherry) { | |
this.cherry = cherry; | |
} | |
public int getPoints() { | |
return points; | |
} | |
public void setPoints(int points) { | |
this.points = points; | |
} | |
public int getBest() { | |
return best; | |
} | |
public void setBest(int best) { | |
this.best = best; | |
} | |
public BufferedImage getImage() { | |
return image; | |
} | |
public void setImage(BufferedImage image) { | |
this.image = image; | |
} | |
public GameStatus getStatus() { | |
return status; | |
} | |
public boolean isDidLoadCherryImage() { | |
return didLoadCherryImage; | |
} | |
public void setDidLoadCherryImage(boolean didLoadCherryImage) { | |
this.didLoadCherryImage = didLoadCherryImage; | |
} | |
// Render the game | |
private void update() { | |
snake.move(); | |
if (cherry != null && snake.getHead().intersects(cherry, 20)) { | |
snake.addTail(); | |
cherry = null; | |
points++; | |
} | |
if (cherry == null) { | |
spawnCherry(); | |
} | |
checkForGameOver(); | |
} | |
private void reset() { | |
points = 0; | |
cherry = null; | |
snake = new Snake(WIDTH / 2, HEIGHT / 2); | |
setStatus(GameStatus.RUNNING); | |
} | |
private void setStatus(GameStatus newStatus) { | |
switch(newStatus) { | |
case RUNNING: | |
timer = new Timer(); | |
timer.schedule(new GameLoop(), 0, DELAY); | |
break; | |
case PAUSED: | |
timer.cancel(); | |
case GAME_OVER: | |
timer.cancel(); | |
best = points > best ? points : best; | |
break; | |
default: | |
break; | |
} | |
status = newStatus; | |
} | |
private void togglePause() { | |
setStatus(status == GameStatus.PAUSED ? GameStatus.RUNNING : GameStatus.PAUSED); | |
} | |
// Check if the snake has hit the wall or itself | |
private void checkForGameOver() { | |
Point head = snake.getHead(); | |
boolean hitBoundary = head.getX() <= 20 | |
|| head.getX() >= WIDTH + 10 | |
|| head.getY() <= 40 | |
|| head.getY() >= HEIGHT + 30; | |
boolean ateItself = false; | |
for(Point t : snake.getTail()) { | |
ateItself = ateItself || head.equals(t); | |
} | |
if (hitBoundary || ateItself) { | |
setStatus(GameStatus.GAME_OVER); | |
} | |
} | |
private void render(Graphics g) { | |
Graphics2D g2d = (Graphics2D) g; | |
g2d.setColor(Color.BLACK); | |
g2d.setFont(FONT_M); | |
if (status == GameStatus.NOT_STARTED) { | |
drawCenteredString(g2d, "SNAKE", FONT_XL, 200); | |
drawCenteredString(g2d, "GAME", FONT_XL, 300); | |
drawCenteredString(g2d, "Press any key to begin", FONT_M_ITALIC, 330); | |
return; | |
} | |
Point p = snake.getHead(); | |
g2d.drawString("SCORE: " + String.format ("%02d", points), 20, 30); | |
g2d.drawString("BEST: " + String.format ("%02d", best), 630, 30); | |
if (cherry != null) { | |
if (didLoadCherryImage) { | |
g2d.drawImage(image, cherry.getX(), cherry.getY(), 60, 60, null); | |
} else { | |
g2d.setColor(Color.BLACK); | |
g2d.fillOval(cherry.getX(), cherry.getY(), 10, 10); | |
g2d.setColor(Color.BLACK); | |
} | |
} | |
if (status == GameStatus.GAME_OVER) { | |
drawCenteredString(g2d, "Press enter to start again", FONT_M_ITALIC, 330); | |
drawCenteredString(g2d, "GAME OVER", FONT_L, 300); | |
} | |
if (status == GameStatus.PAUSED) { | |
g2d.drawString("Paused", 600, 14); | |
} | |
g2d.setColor(new Color(33, 70, 199)); | |
g2d.fillRect(p.getX(), p.getY(), 10, 10); | |
for(int i = 0, size = snake.getTail().size(); i < size; i++) { | |
Point t = snake.getTail().get(i); | |
g2d.fillRect(t.getX(), t.getY(), 10, 10); | |
} | |
g2d.setColor(Color.RED); | |
g2d.setStroke(new BasicStroke(4)); | |
g2d.drawRect(20, 40, WIDTH, HEIGHT); | |
} | |
} | |
enum GameStatus | |
{ | |
NOT_STARTED, RUNNING, PAUSED, GAME_OVER | |
} | |
// direction of snake | |
enum Direction { | |
UP, DOWN, LEFT, RIGHT; | |
public boolean isX() { | |
return this == LEFT || this == RIGHT; | |
} | |
public boolean isY() { | |
return this == UP || this == DOWN; | |
} | |
} | |
class Point { | |
private int x; | |
private int y; | |
public Point(int x, int y) { | |
this.x = x; | |
this.y = y; | |
} | |
public Point(Point p) { | |
this.x = p.getX(); | |
this.y = p.getY(); | |
} | |
public void move(Direction d, int value) { | |
switch(d) { | |
case UP: this.y -= value; break; | |
case DOWN: this.y += value; break; | |
case RIGHT: this.x += value; break; | |
case LEFT: this.x -= value; break; | |
} | |
} | |
public int getX() { | |
return x; | |
} | |
public int getY() { | |
return y; | |
} | |
public Point setX(int x) { | |
this.x = x; | |
return this; | |
} | |
public Point setY(int y) { | |
this.y = y; | |
return this; | |
} | |
public boolean equals(Point p) { | |
return this.x == p.getX() && this.y == p.getY(); | |
} | |
public String toString() { | |
return "(" + x + ", " + y + ")"; | |
} | |
public boolean intersects(Point p) { | |
return intersects(p, 10); | |
} | |
public boolean intersects(Point p, int tolerance) { | |
int diffX = Math.abs(x - p.getX()); | |
int diffY = Math.abs(y - p.getY()); | |
return this.equals(p) || (diffX <= tolerance && diffY <= tolerance); | |
} | |
} | |
class Snake { | |
private Direction direction; | |
private Point head; | |
private ArrayList<Point> tail; | |
public Snake(int x, int y) { | |
this.head = new Point(x, y); | |
this.direction = Direction.RIGHT; | |
this.tail = new ArrayList<Point>(); | |
this.tail.add(new Point(0, 0)); | |
this.tail.add(new Point(0, 0)); | |
this.tail.add(new Point(0, 0)); | |
} | |
public void move() { | |
ArrayList<Point> newTail = new ArrayList<Point>(); | |
for (int i = 0, size = tail.size(); i < size; i++) { | |
Point previous = i == 0 ? head : tail.get(i - 1); | |
newTail.add(new Point(previous.getX(), previous.getY())); | |
} | |
this.tail = newTail; | |
this.head.move(this.direction, 10); | |
} | |
public void addTail() { | |
this.tail.add(new Point(-10, -10)); | |
} | |
public void turn(Direction d) { | |
if (d.isX() && direction.isY() || d.isY() && direction.isX()) { | |
direction = d; | |
} | |
} | |
public ArrayList<Point> getTail() { | |
return this.tail; | |
} | |
public Point getHead() { | |
return this.head; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment