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.