Game Development 22 min read

How to Build a Simple Penalty Shootout Game with Java Swing

This tutorial walks through building a simple penalty‑shootout game in Java Swing by outlining the panel architecture, rendering the audience, grass, goal and net with Graphics2D, computing a QuadCurve2D trajectory, handling mouse dragging and clicks to animate the ball, detecting collisions, and updating score and timer.

Tencent Cloud Developer
Tencent Cloud Developer
Tencent Cloud Developer
How to Build a Simple Penalty Shootout Game with Java Swing

This article demonstrates how to create a simple penalty‑shootout game using Java Swing. It explains the overall architecture, the drawing of each game component, the trajectory calculation, mouse interaction, collision detection, scoring, and timing.

1. Interface terminology – The game screen is divided into a game area and a score area . The game area further contains the audience zone, goal zone, game elements, and the shooting zone.

2. Drawing the audience zone – The audience is rendered with Swing Graphics2D primitives (rectangles, ovals, arcs). The code draws background rectangles and then composes circles and ellipses to form each spectator.

// Audience background
g2d.setColor(personBgColor);
g2d.fillRect(0, y, getWidth(), 100);
// Second row of spectators
for (int i = 0; i < getWidth(); i += 46) {
    g2d.setColor(person2);
    g2d.fillOval(i, 36, 16, 16);
    g2d.fillArc(i - 7, 50, 30, 50, 0, 180);
    g2d.fillOval(i + 24, 40, 13, 13);
    g2d.fillArc(i + 18, 51, 24, 49, 0, 180);
}

3. Drawing the grass – Two alternating colored rectangles are drawn across the screen to simulate a 3‑D lawn.

int count = 0;
int h1 = 60;
for (int i = y; i < getHeight(); i++) {
    if (count % 2 == 0) {
        g2d.setColor(bgColor1);
    } else {
        g2d.setColor(bgColor2);
    }
    g2d.fillRect(0, y, getWidth(), h1 + count * 10);
    y += h1 + count * 10;
    count++;
}

4. Drawing the goal and net – A rounded rectangle forms the goal frame, and multiple lines create the net. The net is drawn with Graphics2D drawPolyline calls.

// Goal frame
g2d.setColor(doorColor);
Stroke stroke = new BasicStroke(9);
g2d.setStroke(stroke);
g2d.drawRoundRect(getWidth()*4/20, 85, getWidth()*3/5, 95, 20, 20);
// Net vertical lines
int step = 0;
for (int i = startX; i < getWidth()*4/5; i = startX) {
    int[] x = {startX, startX + (startX < centerX ? +8 : -8), startX + (startX < centerX ? +12 : -12)};
    int[] y = {startY, startY + 10, 155};
    g2d.drawPolyline(x, y, x.length);
    startX += step;
}

5. Trajectory calculation – The start point (ball centre) and the end point (goal centre) are used to build a QuadCurve2D . Random points on the curve are extracted with a PathIterator and stored in a list.

QuadCurve2D quad = new QuadCurve2D.Double(startX, startY, startX + stepX, endY + stepY + 50, endX, endY + stepY);
PathIterator pi = quad.getPathIterator(g2d.getTransform(), 6);
points = new ArrayList<>(25);
while (!pi.isDone()) {
    double[] coords = new double[6];
    int type = pi.currentSegment(coords);
    if (type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO) {
        points.add(new Point2D.Double(coords[0], coords[1]));
    }
    pi.next();
}

6. Mouse interaction – Dragging the mouse moves the start and end points, updating the trajectory in real time. Holding Ctrl while dragging places the ball at the new location.

ball.addMouseMotionListener(new MouseAdapter() {
    @Override
    public void mouseDragged(MouseEvent e) {
        int stepX = e.getX();
        int stepY = e.getY();
        line.reDraw(ball, BackgroundPanel.this, stepX, stepY, e.isControlDown());
        repaint();
    }
});

7. Shooting the ball – Clicking the ball starts a new thread that moves the ball through the pre‑computed points, sleeping 100 ms between steps.

ball.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent e) {
        new Thread(() -> {
            for (Point2D.Double p : line.getPoints()) {
                ball.setBounds((int) p.x, (int) p.y, ball.getWidth(), ball.getHeight());
                try { Thread.sleep(100); } catch (InterruptedException ex) { ex.printStackTrace(); }
            }
        }).start();
    }
});

8. Collision detection – During the animation the ball is checked against stars, the goalkeeper and the stone obstacle. Overlap of any bounding rectangles triggers removal of the star or a “blocked” state.

Rectangle ballBounds = ball.getBounds();
for (Component c : getComponents()) {
    if (c instanceof Obstacle) {
        Rectangle r = ((Obstacle) c).getComponent().getBounds();
        if (ballBounds.intersects(r)) {
            // handle hit (remove star, play sound, etc.)
        }
    }
}

9. Scoring and timing – A separate thread increments a time counter every second and repaints the timer. When the ball reaches the goal, the score is updated with the number of stars cleared plus one point for the goal.

long time = 0;
@Override
public void run() {
    while (true) {
        try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
        time++;
        repaint(); // draw time as "mm:ss"
    }
}
// After ball stops
infoPanel.addScore(starCount + 1); // 1 for goal, starCount for cleared stars

All of the above pieces are assembled into a Swing JPanel that continuously repaints the background, handles user input, and runs the game loop, resulting in a fully functional penalty‑shootout demo.

JavaanimationGame developmentcollision detectionGraphics2DSwing
Tencent Cloud Developer
Written by

Tencent Cloud Developer

Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.