Snake Game

In this classic arcade game project, we’ll create a fully functional Snake game where you control a growing snake that eats apples and tries to avoid hitting itself. This complete game features multiple screens, scoring system, speed increases, and smooth gameplay - just like the original!

Sketch

// Snake Game – English version
import java.util.*;

ArrayList<PVector> snake;
PVector food;
PVector direction;
int gridSize = 20;
int cols, rows;
boolean gameOver = false;
boolean gameStarted = false;
int score = 0;
int highScore = 0;

void setup() {
    size(600, 600);
    cols = width / gridSize;
    rows = height / gridSize;
    initGame();
}

void draw() {
    background(20, 50, 20); // deep-green background

    if (!gameStarted) {
        drawStartScreen();
    } else if (gameOver) {
        drawGameOverScreen();
    } else {
        updateGame();
        drawGrid();
        drawFood();
        drawSnake();
        drawUI();
    }
}

// ------------------------------------------------------------------
//  INITIALIZATION
// ------------------------------------------------------------------
void initGame() {
    snake = new ArrayList<PVector>();
    snake.add(new PVector(5, 5)); // head
    snake.add(new PVector(4, 5)); // body
    snake.add(new PVector(3, 5)); // tail

    direction = new PVector(1, 0); // move right

    generateFood();
    gameOver = false;
    score = 0;
}

// ------------------------------------------------------------------
//  GAME LOOP
// ------------------------------------------------------------------
void updateGame() {
    // speed increases every 50 points
    int speed = 10 - min(score / 5, 7);
    if (frameCount % speed == 0) {
        moveSnake();
        checkCollisions();
        checkFood();
    }
}

void moveSnake() {
    PVector head = snake.get(0).copy().add(direction);

    // wrap around edges
    head.x = (head.x + cols) % cols;
    head.y = (head.y + rows) % rows;

    snake.add(0, head); // new head
    if (!head.equals(food)) snake.remove(snake.size() - 1); // pop tail
}

void checkCollisions() {
    PVector head = snake.get(0);
    for (int i = 1; i < snake.size(); i++) {
        if (head.equals(snake.get(i))) {
            gameOver = true;
            if (score > highScore) highScore = score;
            return;
        }
    }
}

void checkFood() {
    if (snake.get(0).equals(food)) {
        score += 10;
        generateFood();
        // snake auto-grows because tail is not removed
    }
}

// ------------------------------------------------------------------
//  DRAWING HELPERS
// ------------------------------------------------------------------
void drawGrid() {
    stroke(40, 80, 40);
    strokeWeight(1);
    for (int x = 0; x < width; x += gridSize)  line(x, 0, x, height);
    for (int y = 0; y < height; y += gridSize) line(0, y, width, y);
}

void drawSnake() {
    for (int i = 0; i < snake.size(); i++) {
        PVector p = snake.get(i);

        // head vs body color
        if (i == 0) {
            fill(100, 255, 100);
            stroke(50, 200, 50);
        } else {
            float alpha = map(i, 1, snake.size(), 255, 100);
            fill(50, 200, 50, alpha);
            stroke(30, 150, 30);
        }
        strokeWeight(2);
        rect(p.x * gridSize + 1, p.y * gridSize + 1,
            gridSize - 2, gridSize - 2, 3);

        // eyes on head
        if (i == 0) {
            fill(0);
            noStroke();
            float eye = gridSize * 0.2f;
            float off = gridSize * 0.25f;

            if (direction.x == 1) { // right
                ellipse(p.x * gridSize + gridSize - off, p.y * gridSize + off, eye, eye);
                ellipse(p.x * gridSize + gridSize - off, p.y * gridSize + gridSize - off, eye, eye);
            } else if (direction.x == -1) { // left
                ellipse(p.x * gridSize + off, p.y * gridSize + off, eye, eye);
                ellipse(p.x * gridSize + off, p.y * gridSize + gridSize - off, eye, eye);
            } else if (direction.y == -1) { // up
                ellipse(p.x * gridSize + off, p.y * gridSize + off, eye, eye);
                ellipse(p.x * gridSize + gridSize - off, p.y * gridSize + off, eye, eye);
            } else { // down
                ellipse(p.x * gridSize + off, p.y * gridSize + gridSize - off, eye, eye);
                ellipse(p.x * gridSize + gridSize - off, p.y * gridSize + gridSize - off, eye, eye);
            }
        }
    }
}

void drawFood() {
    // apple body
    fill(255, 100, 100);
    stroke(200, 50, 50);
    strokeWeight(2);
    ellipse(food.x * gridSize + gridSize / 2, food.y * gridSize + gridSize / 2,
            gridSize - 4, gridSize - 4);

    // stem
    fill(100, 200, 100);
    noStroke();
    rect(food.x * gridSize + gridSize / 2 - 1, food.y * gridSize + 2, 2, 4);

    // blinking highlight
    if (frameCount % 30 < 15) {
        fill(255, 255, 255, 100);
        noStroke();
        ellipse(food.x * gridSize + gridSize / 2, food.y * gridSize + gridSize / 2,
                gridSize / 2, gridSize / 2);
    }
}

void drawUI() {
    // score panel
    fill(0, 0, 0, 150);
    noStroke();
    rect(10, 10, 200, 80, 5);

    fill(255, 255, 100);
    textAlign(LEFT);
    textSize(20);
    text("Score: " + score, 20, 35);
    text("Best:  " + highScore, 20, 60);
    text("Length: " + snake.size(), 20, 85);

    textSize(14);
    fill(255, 200);
    text("Speed: " + (1 + score / 50), 120, 35);
}

// ------------------------------------------------------------------
//  SCREENS
// ------------------------------------------------------------------
void drawStartScreen() {
    fill(255, 255, 100);
    textAlign(CENTER);
    textSize(48);
    text("🐍 Snake", width / 2, height / 2 - 100);

    textSize(24);
    fill(255);
    text("Use arrow keys or WASD to move.", width / 2, height / 2 - 40);
    text("Eat red apples to grow.",          width / 2, height / 2 - 10);
    text("Don't hit yourself!",              width / 2, height / 2 + 20);

    textSize(32);
    fill(100, 255, 100);
    text("Press any key to start", width / 2, height / 2 + 80);

    if (highScore > 0) {
        textSize(18);
        fill(255, 255, 0);
        text("High Score: " + highScore, width / 2, height / 2 + 120);
    }
}

void drawGameOverScreen() {
    fill(0, 0, 0, 150);
    noStroke();
    rect(0, 0, width, height);

    fill(255, 100, 100);
    textAlign(CENTER);
    textSize(48);
    text("Game Over!", width / 2, height / 2 - 60);

    fill(255, 255, 100);
    textSize(32);
    text("Final Score: " + score, width / 2, height / 2 - 10);
    text("Snake Length: " + snake.size(), width / 2, height / 2 + 30);

    if (score == highScore && score > 0) {
        fill(255, 255, 0);
        textSize(24);
        text("🎉 New Record! 🎉", width / 2, height / 2 + 70);
    }

    fill(100, 255, 100);
    textSize(20);
    text("Press R to restart", width / 2, height / 2 + 120);
    text("Press Q for menu", width / 2, height / 2 + 150);
}

// ------------------------------------------------------------------
//  INPUT
// ------------------------------------------------------------------
void keyPressed() {
    if (!gameStarted) {
        gameStarted = true;
        initGame();
    } else if (gameOver) {
        if (key == 'r' || key == 'R') {
            initGame();
            gameStarted = true;
        } else if (key == 'q' || key == 'Q') {
            gameStarted = false;
        }
    } else {
        // direction controls
        if ((key == 'w' || key == 'W' || keyCode == UP)    && direction.y != 1)  direction = new PVector(0, -1);
        else if ((key == 's' || key == 'S' || keyCode == DOWN)  && direction.y != -1) direction = new PVector(0,  1);
        else if ((key == 'a' || key == 'A' || keyCode == LEFT)  && direction.x != 1)  direction = new PVector(-1, 0);
        else if ((key == 'd' || key == 'D' || keyCode == RIGHT) && direction.x != -1) direction = new PVector( 1, 0);
        else if (key == 'p' || key == 'P') noLoop();      // pause
        else if (key == ' ')                loop();       // resume
    }
}

// ------------------------------------------------------------------
//  FOOD GENERATION
// ------------------------------------------------------------------
void generateFood() {
    boolean ok = false;
    while (!ok) {
        food = new PVector(int(random(cols)), int(random(rows)));
        ok = true;
        for (PVector s : snake) if (s.equals(food)) ok = false;
    }
}

How it works?

This Snake game demonstrates comprehensive game development principles and advanced programming concepts:

Game Architecture: - State Management: Uses game states (start screen, playing, game over) to control different phases - Game Loop: Classic update-draw cycle with updateGame() and drawing functions - Modular Design: Well-organized code with separate functions for each game aspect

Snake Mechanics: - Dynamic Data Structure: Uses ArrayList<PVector> to represent growing snake body - Movement System: Snake head moves based on direction vector, body follows - Growth Logic: When food is eaten, tail is not removed, causing natural growth - Self-Collision: Checks if head touches any body segment to trigger game over

Grid-Based Movement: - Discrete Positioning: Snake moves on a grid using gridSize = 20 pixels - Wrap-Around Edges: Snake teleports to opposite side when hitting screen boundary - Coordinate System: Uses grid coordinates (cols/rows) instead of pixel coordinates

Food System: - Smart Generation: generateFood() ensures food never spawns on snake body - Visual Appeal: Apple design with stem and blinking highlight effect - Collision Detection: Precise grid-based food consumption detection

Progressive Difficulty: - Speed Increase: Game speeds up as score increases using 10 - min(score/5, 7) - Score Tracking: Points awarded for each apple (10 points) - High Score Memory: Persistent best score tracking across game sessions

Professional User Interface: - Multiple Screens: Welcome screen, gameplay, and game over with different layouts - Real-time HUD: Score, high score, snake length, and speed display - Visual Feedback: Snake head has directional eyes showing movement direction - Professional Polish: Semi-transparent panels, color coding, celebration effects

Advanced Graphics: - Gradient Effects: Snake body fades from bright head to dim tail - Direction-Aware Eyes: Snake head eyes point in movement direction - Apple Animation: Blinking highlight effect using frame-based timing - Grid Visualization: Subtle grid lines for clear game area definition

Input System: - Multiple Control Schemes: Arrow keys and WASD for accessibility - Direction Validation: Prevents 180-degree turns (instant death prevention) - Game Controls: Pause (P), resume (Space), restart (R), menu (Q) - State-Aware Input: Different controls for different game states

Collision Systems: - Self-Collision: Precise head-to-body collision detection - Food Collection: Exact grid position matching - Boundary Handling: Teleportation instead of death (wrap-around)

Game Design Features: - Immediate Feedback: Visual and numerical feedback for all actions - Difficulty Curve: Gradual speed increase maintains engagement - Achievement System: New record celebration - Accessibility: Clear instructions and multiple control options

Programming Concepts Demonstrated: - Object-Oriented Thinking: Game entities as data structures - Algorithm Design: Pathfinding, collision detection, random generation - State Machines: Game flow control - Data Management: Dynamic arrays, coordinate systems - User Experience: Interface design, feedback systems

This project showcases how to build a complete, polished game with professional features and smooth gameplay!

For more please refer to Processing Reference.