GamePanel.java - Game Engine
The heart of the Pong game - contains the game loop, rendering, collision detection, and input handling. This is where all the game logic comes together.
Purpose: Main game engine that coordinates all game objects, handles the game loop, processes input, and manages collision detection.
Source Code
  1import java.awt.*;
  2import java.awt.event.*;
  3import java.util.*;
  4import javax.swing.*;
  5
  6public class GamePanel extends JPanel implements Runnable{
  7
  8	static final int GAME_WIDTH = 1000;
  9	static final int GAME_HEIGHT = (int)(GAME_WIDTH * (0.5555));
 10	static final Dimension SCREEN_SIZE = new Dimension(GAME_WIDTH,GAME_HEIGHT);
 11	static final int BALL_DIAMETER = 20;
 12	static final int PADDLE_WIDTH = 25;
 13	static final int PADDLE_HEIGHT = 100;
 14	Thread gameThread;
 15	Image image;
 16	Graphics graphics;
 17	Random random;
 18	Paddle paddle1;
 19	Paddle paddle2;
 20	Ball ball;
 21	Score score;
 22	
 23	GamePanel(){
 24		newPaddles();
 25		newBall();
 26		score = new Score(GAME_WIDTH,GAME_HEIGHT);
 27		this.setFocusable(true);
 28		this.addKeyListener(new AL());
 29		this.setPreferredSize(SCREEN_SIZE);
 30		
 31		gameThread = new Thread(this);
 32		gameThread.start();
 33	}
 34	
 35	public void newBall() {
 36		random = new Random();
 37		ball = new Ball((GAME_WIDTH/2)-(BALL_DIAMETER/2),random.nextInt(GAME_HEIGHT-BALL_DIAMETER),BALL_DIAMETER,BALL_DIAMETER);
 38	}
 39	public void newPaddles() {
 40		paddle1 = new Paddle(0,(GAME_HEIGHT/2)-(PADDLE_HEIGHT/2),PADDLE_WIDTH,PADDLE_HEIGHT,1);
 41		paddle2 = new Paddle(GAME_WIDTH-PADDLE_WIDTH,(GAME_HEIGHT/2)-(PADDLE_HEIGHT/2),PADDLE_WIDTH,PADDLE_HEIGHT,2);
 42	}
 43	public void paint(Graphics g) {
 44		image = createImage(getWidth(),getHeight());
 45		graphics = image.getGraphics();
 46		draw(graphics);
 47		g.drawImage(image,0,0,this);
 48	}
 49	public void draw(Graphics g) {
 50		paddle1.draw(g);
 51		paddle2.draw(g);
 52		ball.draw(g);
 53		score.draw(g);
 54		Toolkit.getDefaultToolkit().sync(); // Helps with animation smoothness
 55	}
 56	public void move() {
 57		paddle1.move();
 58		paddle2.move();
 59		ball.move();
 60	}
 61	public void checkCollision() {
 62		
 63		//bounce ball off top & bottom window edges
 64		if(ball.y <=0) {
 65			ball.setYDirection(-ball.yVelocity);
 66		}
 67		if(ball.y >= GAME_HEIGHT-BALL_DIAMETER) {
 68			ball.setYDirection(-ball.yVelocity);
 69		}
 70		//bounce ball off paddles
 71		if(ball.intersects(paddle1)) {
 72			ball.xVelocity = Math.abs(ball.xVelocity);
 73			ball.xVelocity++; //optional for more difficulty
 74			if(ball.yVelocity>0)
 75				ball.yVelocity++; //optional for more difficulty
 76			else
 77				ball.yVelocity--;
 78			ball.setXDirection(ball.xVelocity);
 79			ball.setYDirection(ball.yVelocity);
 80		}
 81		if(ball.intersects(paddle2)) {
 82			ball.xVelocity = Math.abs(ball.xVelocity);
 83			ball.xVelocity++; //optional for more difficulty
 84			if(ball.yVelocity>0)
 85				ball.yVelocity++; //optional for more difficulty
 86			else
 87				ball.yVelocity--;
 88			ball.setXDirection(-ball.xVelocity);
 89			ball.setYDirection(ball.yVelocity);
 90		}
 91		//stops paddles at window edges
 92		if(paddle1.y<=0)
 93			paddle1.y=0;
 94		if(paddle1.y >= (GAME_HEIGHT-PADDLE_HEIGHT))
 95			paddle1.y = GAME_HEIGHT-PADDLE_HEIGHT;
 96		if(paddle2.y<=0)
 97			paddle2.y=0;
 98		if(paddle2.y >= (GAME_HEIGHT-PADDLE_HEIGHT))
 99			paddle2.y = GAME_HEIGHT-PADDLE_HEIGHT;
100		//give a player 1 point and creates new paddles & ball
101		if(ball.x <=0) {
102			score.player2++;
103			newPaddles();
104			newBall();
105			System.out.println("Player 2: "+score.player2);
106		}
107		if(ball.x >= GAME_WIDTH-BALL_DIAMETER) {
108			score.player1++;
109			newPaddles();
110			newBall();
111			System.out.println("Player 1: "+score.player1);
112		}
113	}
114	public void run() {
115		//game loop
116		long lastTime = System.nanoTime();
117		double amountOfTicks =60.0;
118		double ns = 1000000000 / amountOfTicks;
119		double delta = 0;
120		while(true) {
121			long now = System.nanoTime();
122			delta += (now -lastTime)/ns;
123			lastTime = now;
124			if(delta >=1) {
125				move();
126				checkCollision();
127				repaint();
128				delta--;
129			}
130		}
131	}
132	public class AL extends KeyAdapter{
133		public void keyPressed(KeyEvent e) {
134			paddle1.keyPressed(e);
135			paddle2.keyPressed(e);
136		}
137		public void keyReleased(KeyEvent e) {
138			paddle1.keyReleased(e);
139			paddle2.keyReleased(e);
140		}
141	}
142}Architectural Overview
This class demonstrates several important programming patterns:
Game Loop Architecture
Threading with Runnable
1public class GamePanel extends JPanel implements Runnable- Extends JPanel: Provides drawing surface and component functionality
- Implements Runnable: Enables the game loop to run in a separate thread
- Thread Safety: Keeps the game running without blocking the UI thread
Game Constants
1static final int GAME_WIDTH = 1000;
2static final int GAME_HEIGHT = (int)(GAME_WIDTH * (0.5555));
3static final Dimension SCREEN_SIZE = new Dimension(GAME_WIDTH,GAME_HEIGHT);- Aspect Ratio: Height calculated as 55.55% of width (≈ 16:9 ratio)
- Static Final: Constants shared across all instances
- Dimension Object: Used for setting preferred panel size
Core Game Systems
1. Initialization System
Constructor Setup
 1GamePanel(){
 2    newPaddles();           // Create paddle objects
 3    newBall();              // Create ball object
 4    score = new Score(...); // Create score tracker
 5    this.setFocusable(true); // Enable keyboard input
 6    this.addKeyListener(new AL()); // Handle key events
 7    this.setPreferredSize(SCREEN_SIZE); // Set panel size
 8    
 9    gameThread = new Thread(this); // Create game thread
10    gameThread.start();     // Start the game loop
11}Key Setup Steps:
- Object Creation: Initialize all game objects
- Input Setup: Enable keyboard focus and add listener
- Display Setup: Set panel dimensions
- Threading: Start the game loop in separate thread
2. Rendering System
Double Buffering
1public void paint(Graphics g) {
2    image = createImage(getWidth(),getHeight());
3    graphics = image.getGraphics();
4    draw(graphics);
5    g.drawImage(image,0,0,this);
6}Double Buffering Process:
- Create Off-Screen Image: createImage()creates a buffer
- Draw to Buffer: All drawing operations happen off-screen
- Display Buffer: Single operation copies buffer to screen
- Prevents Flickering: No partial draws visible to user
Drawing Coordination
1public void draw(Graphics g) {
2    paddle1.draw(g);
3    paddle2.draw(g);
4    ball.draw(g);
5    score.draw(g);
6    Toolkit.getDefaultToolkit().sync();
7}Rendering Order:
- Paddles drawn first (background objects)
- Ball drawn on top
- Score overlay drawn last
- sync()ensures smooth animation timing
3. Game Loop System
Delta Time Game Loop
 1public void run() {
 2    long lastTime = System.nanoTime();
 3    double amountOfTicks = 60.0;
 4    double ns = 1000000000 / amountOfTicks;
 5    double delta = 0;
 6    while(true) {
 7        long now = System.nanoTime();
 8        delta += (now - lastTime) / ns;
 9        lastTime = now;
10        if(delta >= 1) {
11            move();
12            checkCollision();
13            repaint();
14            delta--;
15        }
16    }
17}Game Loop Breakdown:
- Target 60 FPS: amountOfTicks = 60.0
- Delta Calculation: Tracks time since last update
- Frame-Rate Independence: Updates only when enough time has passed
- Update Sequence: Move → Check Collisions → Repaint
4. Collision Detection System
Boundary Collisions
1// Ball bouncing off top and bottom
2if(ball.y <= 0) {
3    ball.setYDirection(-ball.yVelocity);
4}
5if(ball.y >= GAME_HEIGHT - BALL_DIAMETER) {
6    ball.setYDirection(-ball.yVelocity);
7}Boundary Physics:
- Top/Bottom: Reverse Y velocity (bounce vertically)
- Left/Right: Trigger scoring and reset
- Simple but Effective: Basic physics simulation
Paddle Collisions
1if(ball.intersects(paddle1)) {
2    ball.xVelocity = Math.abs(ball.xVelocity);
3    ball.xVelocity++; // Increase difficulty
4    // ... velocity adjustments
5    ball.setXDirection(ball.xVelocity);
6    ball.setYDirection(ball.yVelocity);
7}Collision Response:
- Detection: Uses Rectangle.intersects()method
- Direction Change: Reverse X direction, maintain/modify Y
- Speed Increase: Game gets progressively harder
- Physics Update: Apply new velocities to ball
Paddle Boundaries
1// Prevent paddles from moving off-screen
2if(paddle1.y <= 0)
3    paddle1.y = 0;
4if(paddle1.y >= (GAME_HEIGHT - PADDLE_HEIGHT))
5    paddle1.y = GAME_HEIGHT - PADDLE_HEIGHT;Constraint System:
- Clamp Position: Keep paddles within screen bounds
- Direct Position Setting: Override position if out of bounds
- Applied to Both Paddles: Consistent behavior
5. Input Handling System
Event Delegation
 1public class AL extends KeyAdapter {
 2    public void keyPressed(KeyEvent e) {
 3        paddle1.keyPressed(e);
 4        paddle2.keyPressed(e);
 5    }
 6    public void keyReleased(KeyEvent e) {
 7        paddle1.keyReleased(e);
 8        paddle2.keyReleased(e);
 9    }
10}Input Architecture:
- KeyAdapter: Convenient base class (empty implementations)
- Event Delegation: Forward events to appropriate objects
- Decoupled Design: Paddles handle their own key mappings
- Both Events: Handles press and release for smooth movement
Advanced Concepts
Performance Optimizations
Graphics Optimization:
- Double buffering prevents screen flickering
- Toolkit.sync()ensures smooth animation
- Fixed frame rate prevents excessive CPU usage
Design Patterns Used
- Component Pattern: Each game object handles its own behavior
- Game Loop Pattern: Continuous update-render cycle
- Observer Pattern: KeyListener responds to input events
- Template Method: Consistent object interface (move, draw methods)
Threading Considerations
Thread Safety: The game loop runs in a separate thread from the UI thread. This prevents the game from blocking user interface operations but requires careful consideration of shared state.
Study Questions
- Performance: What would happen if the game loop ran on the main UI thread?
- Physics: How could you make the ball bouncing more realistic?
- Architecture: What if you wanted to add power-ups? Where would you handle them?
- Debugging: How could you add a pause feature to this game loop?
Next: Explore the individual game objects: Ball.java, Paddle.java, Score.java