โ˜• My First Java Game ยท Episode 4 of 7 ยท See All Episodes
๐Ÿ“ Episode 4 ยท Improver ยท Two-Player + AI

The Original!
Pong Classic

Recreate the 1972 legend: two paddles, one ball, first to 7. Then teach the right paddle to play by itself, your first game AI, plus proper game states (menu, playing, game over).

๐Ÿ‘ถ Ages 11+ โฑ๏ธ ~1.5 Hours โ˜• Java โœ“ Free
๐Ÿ“ Two paddles ๐Ÿค– Simple AI ๐ŸŽฏ Serve & rally ๐Ÿ”ข Score to win ๐ŸŽฌ Game states โš–๏ธ Balancing
โญ
0 XP
Level 1
๐Ÿ”ฅ0
Your Progress 0 / 6 steps
1
๐Ÿ“
Court, Paddles & Controls
Two paddles: W/S for player 1, arrows for player 2
Active
๐ŸŽฏ
Goal for this step

Draw the court and get both paddles moving with their own keys.

  • 1New project, 800ร—500 window, black background, plus a dashed centre line drawn with a loop of small rectangles.
  • 2Two paddles: p1Y on the left (x = 20), p2Y on the right (x = 766). Both 14ร—90.
  • 3Player 1 holds W/S, player 2 holds Up/Down, four held-key booleans.
  • 4Clamp both paddles between 0 and 500 - 90.
  • 5Draw everything white, like 1972.
GamePanel.java
    int p1Y = 205, p2Y = 205;
    boolean wHeld, sHeld, upHeld, downHeld;

    void update() {
        if (wHeld)    p1Y -= 6;
        if (sHeld)    p1Y += 6;
        if (upHeld)   p2Y -= 6;
        if (downHeld) p2Y += 6;
        p1Y = Math.max(0, Math.min(410, p1Y));
        p2Y = Math.max(0, Math.min(410, p2Y));
    }

    // centre line in paintComponent:
    for (int y = 0; y < 500; y += 24)
        g.fillRect(397, y, 6, 14);
โœ๏ธ
Fill in the Blanks
+15 XP
Player 1 uses the and S keys; player 2 uses the arrow keys. Both paddles are clamped between 0 and (the height minus the paddle length).
๐Ÿง 
Knowledge Check
+15 XP
Why 410 as the clamp maximum?
AIt is a random nice number
BWindow height 500 minus paddle height 90, so the paddle bottom never leaves the court
CPaddles must stay in the top half
2
โšช
Ball, Serve & Rally
Centre serve, wall bounces and paddle rebounds with spin
Locked
๐ŸŽฏ
Goal for this step

A ball that serves from the centre, bounces off walls and paddles, and speeds up each hit.

  • 1Ball at centre with velocity bvx = 5 in a random left/right direction: Math.random() < 0.5 ? 5 : -5.
  • 2Top and bottom walls flip bvy.
  • 3Paddle rebounds flip bvx AND add spin from where it struck the paddle (the Breakout trick, vertical this time).
  • 4Rally speed-up: every paddle hit, grow bvx slightly, cap it at 12 or it becomes silly.
  • 5Write a serve() method that recentres the ball, we will call it after every point.
GamePanel.java
    int bx = 393, by = 243, bs = 14;
    int bvx = 5, bvy = 3;

    void serve(int direction) {
        bx = 393; by = 243;
        bvx = 5 * direction;
        bvy = Math.random() < 0.5 ? 3 : -3;
    }

    void update() {
        bx += bvx;  by += bvy;
        if (by <= 0 || by + bs >= 500) bvy = -bvy;

        Rectangle ball = new Rectangle(bx, by, bs, bs);
        if (ball.intersects(new Rectangle(20, p1Y, 14, 90)) && bvx < 0) {
            bvx = -bvx + 1;                       // rally speeds up
            bvy = ((by + bs/2) - (p1Y + 45)) / 7; // spin
        }
        if (ball.intersects(new Rectangle(766, p2Y, 14, 90)) && bvx > 0) {
            bvx = -(bvx + 1);
            bvy = ((by + bs/2) - (p2Y + 45)) / 7;
        }
        bvx = Math.max(-12, Math.min(12, bvx));
    }
โŒจ๏ธ
Code Challenge
+20 XP
Complete the serve that restarts each rally:
GamePanel.java
void serve(int direction) {
    bx = ; by = 243;              // back to centre
    bvx = 5 * ;             // towards the scorer's opponent
    bvy = Math.random() < 0.5 ? 3 : ;
}
๐Ÿ’ก Hint: The centre x was set when we declared the ball. The direction parameter is +1 or -1. The vertical start is up or down at random.
๐Ÿง 
Knowledge Check
+15 XP
The rebound only fires when bvx < 0 on the left paddle. Why?
ALeft paddles are weaker
BOnly rebound when the ball travels toward that paddle, stops it getting stuck inside
CJava booleans need a condition
3
๐Ÿ”ข
Scoring & First to 7
Points when the ball exits, big scoreboard, win at 7
Locked
๐ŸŽฏ
Goal for this step

Score points when the ball leaves the court, and crown a winner at 7.

  • 1If bx < -20: player 2 scores, serve(-1) (towards the loser). If bx > 820: player 1 scores, serve(1).
  • 2Draw both scores huge at the top with a big font, Pong scoreboards are half the charm.
  • 3Add boolean gameOver: when either score reaches 7, freeze play and announce PLAYER 1 WINS (or 2).
  • 4Serve toward the player who lost the point, that is real Pong etiquette.
GamePanel.java
    int score1 = 0, score2 = 0;
    boolean gameOver = false;

    void update() {
        if (gameOver) return;
        // ... movement ...
        if (bx < -20)  { score2++; serve(-1); }
        if (bx > 820)  { score1++; serve(1);  }
        if (score1 == 7 || score2 == 7) gameOver = true;
    }

    // paintComponent:
    g.setFont(new Font("Monospaced", Font.BOLD, 56));
    g.drawString("" + score1, 320, 70);
    g.drawString("" + score2, 450, 70);
โœ๏ธ
Fill in the Blanks
+15 XP
When the ball exits the LEFT side, player scores a point. The match ends when a score reaches .
๐Ÿง 
Knowledge Check
+15 XP
Why serve toward the player who just lost the point?
AIt punishes the loser
BIt is fair, the scorer should not immediately receive an easy attack
CThe ball must alternate sides by law
4
๐Ÿค–
Computer Opponent
An AI paddle that tracks the ball, imperfectly
Locked
๐ŸŽฏ
Goal for this step

Replace player 2 with a computer that chases the ball but can be beaten.

  • 1Add boolean vsComputer = true;, when on, ignore Up/Down and let the AI drive p2Y.
  • 2Dead-simple AI: if the paddle centre is above the ball centre, move down; if below, move up.
  • 3A perfect AI is unbeatable and boring. Give it a reaction dead-zone: only react when the gap is bigger than 12 pixels.
  • 4And a speed limit: the AI paddle moves 5 while you move 6, you can outpace it with clever angles.
  • 5Playtest: can you beat it 7-3? Tune deadZone and aiSpeed until it is a fair fight.
GamePanel.java
    boolean vsComputer = true;
    int aiSpeed = 5, deadZone = 12;

    void update() {
        // ...
        if (vsComputer) {
            int padCentre  = p2Y + 45;
            int ballCentre = by + bs/2;
            if (padCentre < ballCentre - deadZone) p2Y += aiSpeed;
            if (padCentre > ballCentre + deadZone) p2Y -= aiSpeed;
        } else {
            if (upHeld)   p2Y -= 6;
            if (downHeld) p2Y += 6;
        }
    }
๐Ÿ’ก
This tiny "chase with flaws" pattern is genuinely how a LOT of game AI works: perfect logic, deliberately weakened until it is fun.
โŒจ๏ธ
Code Challenge
+20 XP
Complete the AI chase logic:
GamePanel.java
if (padCentre < ballCentre - deadZone) p2Y  aiSpeed;
if (padCentre > ballCentre + deadZone) p2Y  aiSpeed;
๐Ÿ’ก Hint: If the paddle centre is ABOVE the ball (smaller y), the paddle must move DOWN (y grows).
๐Ÿง 
Knowledge Check
+15 XP
Why give the AI a dead-zone and a speed limit?
ATo save CPU power
BA flawless AI cannot be beaten, deliberate weakness is what makes it fun
CSwing timers require it
5
๐ŸŽฌ
Game States & Menu
MENU โ†’ PLAYING โ†’ GAME_OVER with clean switching
Locked
๐ŸŽฏ
Goal for this step

Add a title screen and restart flow using a proper state variable.

  • 1One variable runs the show: String state = "MENU"; (or an enum if you are feeling fancy).
  • 2update(): only move things when state equals "PLAYING".
  • 3paintComponent(): draw the court always; draw the menu text over it in MENU; the winner banner in GAME_OVER.
  • 4Keys: in MENU press 1 for vs-computer or 2 for two-player; in GAME_OVER press R to reset scores and return to MENU.
  • 5This MENU/PLAYING/GAME_OVER pattern is in literally every game you will ever write. Learn it here, reuse it forever.
GamePanel.java
    String state = "MENU";

    void update() {
        if (!state.equals("PLAYING")) return;
        // ... all game code ...
        if (score1 == 7 || score2 == 7) state = "GAME_OVER";
    }

    public void keyPressed(KeyEvent e) {
        if (state.equals("MENU")) {
            if (e.getKeyChar() == '1') { vsComputer = true;  state = "PLAYING"; }
            if (e.getKeyChar() == '2') { vsComputer = false; state = "PLAYING"; }
        } else if (state.equals("GAME_OVER") && e.getKeyChar() == 'r') {
            score1 = 0; score2 = 0; serve(1);
            state = "MENU";
        }
        // ... held-key booleans ...
    }
โœ๏ธ
Fill in the Blanks
+15 XP
Our three states are MENU, and GAME_OVER. Gameplay code only runs when state equals , and pressing after a match returns to the menu.
๐Ÿง 
Knowledge Check
+15 XP
What is the advantage of one state variable over lots of booleans like inMenu, isPlaying, hasEnded?
AStrings are faster than booleans
BThe game can only ever be in ONE state, impossible to be in the menu and playing at once
CIt uses less disk space
6
โœจ
Juice & Final Polish
Screen flash on score, sound beeps and tuning
Locked
๐ŸŽฏ
Goal for this step

Add the little touches that make Pong feel alive, then ship it.

  • 1Score flash: set int flash = 12; when someone scores; while flash > 0, decrement it and paint the background slightly brighter.
  • 2Beep! Java can make the classic blip with Toolkit.getDefaultToolkit().beep(); on every paddle hit.
  • 3Show FIRST TO 7 and control hints on the menu screen so nobody has to guess.
  • 4Final balance pass: rally speed-up (+1 per hit) vs paddle speed (6) vs AI (5). Adjust until matches last 2-3 minutes.
  • 5Show a friend. Best-of-three. Defend your title. ๐Ÿ†
GamePanel.java
    int flash = 0;

    // when a point is scored:
    //   score2++; serve(-1); flash = 12;

    void update() {
        // ...
        if (flash > 0) flash--;
    }

    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (flash > 0) {
            g.setColor(new Color(255, 255, 255, 40));
            g.fillRect(0, 0, 800, 500);
        }
        // ... everything else ...
    }
๐Ÿ’ก
Game developers call this juice, flashes, shakes, beeps. It costs a few lines and doubles how good a game feels.
โŒจ๏ธ
Code Challenge
+20 XP
Make the score flash fade out over time:
GamePanel.java
// when scoring:
flash = ;
// each frame:
if (flash > 0) flash;
๐Ÿ’ก Hint: Set the counter to its full value, then count down by one per frame.
๐Ÿง 
Knowledge Check
+15 XP
You built Pong with AI and game states. Which pattern from this episode appears in almost every commercial game?
AThe dashed centre line
BThe MENU โ†’ PLAYING โ†’ GAME_OVER state machine
CThe beep sound
๐ŸŽ‰๐Ÿ†๐ŸŽฎโœจ๐ŸŽ‰
Workshop Complete!
Two paddles, an AI opponent and real game states, Pong looks simple but you just used every core game-dev pattern. Next: the three-part RPG Quest finale!
0
Total XP
1
Level
0
Best Streak
0%
Accuracy
โ–ถ Episode 5: RPG Quest Part 1 โ†’
โญ View My Progress & Certificates

This workshop was free and took many hours to build. If it helped you learn something new, consider supporting the project.

☕ Support on Ko-fi