Score.java - Score Display
The Score
class manages the game scoreboard, tracking points for both players and rendering the score display with a center divider line.
Purpose: Tracks player scores and renders the scoreboard display with proper formatting and visual design.
Source Code
1import java.awt.*;
2
3public class Score extends Rectangle{
4 static int GAME_WIDTH;
5 static int GAME_HEIGHT;
6 int player1;
7 int player2;
8
9 Score(int GAME_WIDTH, int GAME_HEIGHT){
10 Score.GAME_WIDTH = GAME_WIDTH;
11 Score.GAME_HEIGHT = GAME_HEIGHT;
12 }
13
14 public void draw(Graphics g){
15 g.setColor(Color.white);
16 g.setFont(new Font("Consolas", Font.PLAIN, 60));
17 g.drawLine(GAME_WIDTH/2, 0, GAME_WIDTH/2, GAME_HEIGHT);
18 g.drawString(String.valueOf(player1/10)+String.valueOf(player1%10), (GAME_WIDTH/2)-85, 50);
19 g.drawString(String.valueOf(player2/10)+String.valueOf(player2%10), (GAME_WIDTH/2)+20, 50);
20 }
21}
Code Analysis
Class Design
Static Field Usage
1static int GAME_WIDTH;
2static int GAME_HEIGHT;
Static Fields for Dimensions:
- Shared State: All Score instances use same game dimensions
- Memory Efficiency: Only one copy of dimensions needed
- Global Access: Can be accessed without instance reference
- Initialization: Set once in constructor, used in drawing
Constructor Assignment:
1Score(int GAME_WIDTH, int GAME_HEIGHT){
2 Score.GAME_WIDTH = GAME_WIDTH; // Assign to static field
3 Score.GAME_HEIGHT = GAME_HEIGHT; // Using class name prefix
4}
Score Storage
1int player1; // Player 1's current score
2int player2; // Player 2's current score
Score Management:
- Integer Counters: Simple increment system
- Default Initialization: Both start at 0
- Public Access: GamePanel directly modifies these fields
- No Upper Limit: Scores can increment indefinitely
Rendering System
Visual Layout Design
1public void draw(Graphics g){
2 g.setColor(Color.white);
3 g.setFont(new Font("Consolas", Font.PLAIN, 60));
4
5 // Center divider line
6 g.drawLine(GAME_WIDTH/2, 0, GAME_WIDTH/2, GAME_HEIGHT);
7
8 // Player scores positioned relative to center
9 g.drawString(player1Score, (GAME_WIDTH/2)-85, 50); // Left of center
10 g.drawString(player2Score, (GAME_WIDTH/2)+20, 50); // Right of center
11}
Layout Elements:
- Center Line: Vertical divider separating player sides
- Font Choice: “Consolas” monospace font for consistent character width
- Font Size: Large 60pt for clear visibility during gameplay
- Positioning: Scores positioned symmetrically around center line
Score Formatting Logic
1String.valueOf(player1/10) + String.valueOf(player1%10)
Two-Digit Display System:
- Tens Digit:
player1/10
- Integer division gives tens place - Ones Digit:
player1%10
- Modulo gives remainder (ones place) - Always 2 Digits: Ensures “05” instead of “5” for single digits
- String Concatenation: Combines digits into display string
Examples:
1Score = 7: 7/10=0, 7%10=7 → "0" + "7" = "07"
2Score = 15: 15/10=1, 15%10=5 → "1" + "5" = "15"
3Score = 23: 23/10=2, 23%10=3 → "2" + "3" = "23"
Graphics Programming Concepts
Font and Typography
1g.setFont(new Font("Consolas", Font.PLAIN, 60));
Font Configuration:
- Font Family: “Consolas” - monospace font for digital aesthetic
- Font Style:
Font.PLAIN
- no bold or italic styling - Font Size: 60 points - large enough for game visibility
- Monospace Advantage: All digits have same width for consistent alignment
Alternative Font Options:
1new Font("Arial", Font.BOLD, 60); // Bold sans-serif
2new Font("Courier New", Font.PLAIN, 60); // Alternative monospace
3new Font(Font.MONOSPACED, Font.PLAIN, 60); // System default monospace
Coordinate System
1// Center line coordinates
2g.drawLine(GAME_WIDTH/2, 0, GAME_WIDTH/2, GAME_HEIGHT);
3
4// Score positioning
5g.drawString(score, (GAME_WIDTH/2)-85, 50); // Player 1
6g.drawString(score, (GAME_WIDTH/2)+20, 50); // Player 2
Positioning Strategy:
- Center Reference: All positioning relative to
GAME_WIDTH/2
- Vertical Line: From top (y=0) to bottom (y=GAME_HEIGHT)
- Text Baseline: Y=50 positions text near top of screen
- Horizontal Offset: -85 and +20 create symmetric spacing around center
Integration with Game System
Score Updates
The GamePanel updates scores directly when goals occur:
1// In GamePanel.checkCollision():
2if(ball.x <= 0) {
3 score.player2++; // Player 2 scores
4 newPaddles();
5 newBall();
6 System.out.println("Player 2: " + score.player2);
7}
8if(ball.x >= GAME_WIDTH - BALL_DIAMETER) {
9 score.player1++; // Player 1 scores
10 newPaddles();
11 newBall();
12 System.out.println("Player 1: " + score.player1);
13}
Scoring Logic:
- Left Goal: Ball exits left side → Player 2 scores
- Right Goal: Ball exits right side → Player 1 scores
- Immediate Update: Score incremented directly
- Game Reset: New ball and paddles created after each goal
Design Patterns and Architecture
Observer Pattern (Implicit)
- Score Display: Automatically reflects current game state
- No Notifications: Direct field access instead of events
- Simple Coupling: GamePanel modifies, Score displays
Utility Class Pattern
- Single Responsibility: Only handles score display
- Stateless Rendering: Draw method has no side effects
- Configuration Storage: Holds display parameters
Enhancement Ideas
Advanced Score Features
1public class EnhancedScore extends Rectangle {
2 private int player1, player2;
3 private int winningScore = 11;
4 private long lastScoreTime;
5 private boolean gameWon = false;
6 private String winnerName = "";
7
8 public void incrementPlayer1() {
9 player1++;
10 lastScoreTime = System.currentTimeMillis();
11 checkForWin();
12 }
13
14 public void incrementPlayer2() {
15 player2++;
16 lastScoreTime = System.currentTimeMillis();
17 checkForWin();
18 }
19
20 private void checkForWin() {
21 if(player1 >= winningScore) {
22 gameWon = true;
23 winnerName = "Player 1";
24 } else if(player2 >= winningScore) {
25 gameWon = true;
26 winnerName = "Player 2";
27 }
28 }
29
30 public void draw(Graphics g) {
31 // Draw normal score
32 drawScore(g);
33
34 // Flash recent scores
35 if(System.currentTimeMillis() - lastScoreTime < 1000) {
36 g.setColor(Color.YELLOW);
37 // Highlight recent score
38 }
39
40 // Display winner
41 if(gameWon) {
42 g.setColor(Color.GREEN);
43 g.setFont(new Font("Arial", Font.BOLD, 40));
44 g.drawString(winnerName + " Wins!", GAME_WIDTH/2 - 100, 150);
45 }
46 }
47}
Visual Improvements
1public void draw(Graphics g) {
2 // Gradient background for score area
3 Graphics2D g2d = (Graphics2D) g;
4 GradientPaint gradient = new GradientPaint(
5 0, 0, Color.BLACK,
6 0, 80, new Color(30, 30, 30)
7 );
8 g2d.setPaint(gradient);
9 g2d.fillRect(0, 0, GAME_WIDTH, 80);
10
11 // Animated center line
12 g.setColor(Color.WHITE);
13 for(int i = 0; i < GAME_HEIGHT; i += 20) {
14 if((System.currentTimeMillis() / 100 + i) % 40 < 20) {
15 g.fillRect(GAME_WIDTH/2 - 2, i, 4, 10);
16 }
17 }
18
19 // Shadow effect for text
20 g.setFont(new Font("Consolas", Font.BOLD, 60));
21 g.setColor(Color.DARK_GRAY);
22 g.drawString(player1String, (GAME_WIDTH/2)-83, 52); // Shadow
23 g.setColor(Color.WHITE);
24 g.drawString(player1String, (GAME_WIDTH/2)-85, 50); // Main text
25}
Study Questions
- Memory Management: Why use static fields for game dimensions?
- String Formatting: What’s the purpose of the complex score formatting logic?
- Architecture: How could you implement score limits or win conditions?
- Performance: Is creating new Font objects every frame efficient?
Common Issues and Solutions
Potential Issues:
- Large Numbers: Score formatting breaks for scores > 99
- Font Loading: Font may not be available on all systems
- Performance: String concatenation in drawing loop
- Positioning: Hard-coded offsets may not work with different fonts
Solutions:
- Use
String.format("%02d", score)
for better formatting - Check font availability and provide fallbacks
- Pre-format score strings outside draw method
- Calculate text bounds for dynamic positioning
Summary: This completes the Pong game architecture! The Score class demonstrates simple state management, string formatting, and graphics programming concepts essential for game UI development.