Nama : Erlangga Rizqi Dwi Raswanto
NRP : 5025241179
Kelas : PBO A
1. Pendahuluan
Pada pertemuan 15, kita mempraktikkan pembuatan game arcade sederhana “Pong” menggunakan Java GUI (AWT & Swing).
Secara singkat, Pong adalah salah satu game video generasi awal yang terinspirasi dari permainan tenis meja (ping-pong). Secara visual, game ini sangat sederhana: hanya terdiri dari dua paddle (kayu pemukul) dan satu bola yang memantul ke kanan dan kiri.
Di pertemuan ini, saya mengimplementasikan game Pong dengan Java Swing yang terdiri dari beberapa kelas: PongGame, GameFrame, GamePanel, Paddle, dan Ball.
2. Tujuan Praktikum
Tujuan dari tugas pertemuan 15 adalah:
-
Menerapkan konsep GUI pada Java menggunakan Swing dan AWT.
-
Memahami cara kerja game loop menggunakan
Threaddan interfaceRunnable. -
Menggunakan event listener (
KeyListener) untuk menangani input keyboard. -
Menerapkan pemodelan objek (OOP) ke dalam game (memecah game jadi beberapa class).
-
Menerapkan logika sederhana collision detection dan scoring.
3. Perancangan Program
3.1 Skema Kelas
Secara garis besar, struktur program:
-
PongGame
Kelas denganmain(). Tugasnya hanya membuat jendela game (GameFrame). -
GameFrame
Turunan dariJFrame. Ini adalah window utama yang berisiGamePanel. -
GamePanel
Turunan dariJPanel, sekaligusRunnabledanKeyListener.
Bertanggung jawab untuk:-
Menjalankan game loop (update & repaint)
-
Menggambar semua objek (paddle, ball, score, garis tengah)
-
Mengatur collision dan scoring
-
-
Paddle
Mewakili objek paddle (kayu pemukul) milik Player 1 dan Player 2. Masing-masing bisa digerakkan dengan keyboard:-
Player 1:
W(naik),S(turun) -
Player 2:
UPdanDOWN
-
-
Ball
Mewakili bola game. Bola memiliki posisi(x, y)dan kecepatan(xVelocity, yVelocity)yang di-update di setiap frame.
3.2 Alur Program
-
Program dimulai dari
PongGame.main(), yang membuat objekGameFrame. -
GameFramemembuatGamePaneldan menampilkannya. -
Di dalam
GamePanel, constructor:-
Membuat dua paddle dan satu bola.
-
Mengatur ukuran panel dan background.
-
Menambahkan
KeyListener. -
Memulai
Threadgame.
-
-
Threadmenjalankan methodrun(), yang berisi game loop:-
Panggil
move()→ update posisi paddle & bola. -
Panggil
checkCollision()→ cek pantulan & scoring. -
Panggil
repaint()→ memicupaint()dan menggambar ulang layar.
-
-
Saat user menekan/melepas tombol, event akan diteruskan ke
PaddlemelaluikeyPressed()dankeyReleased().
4. Implementasi Program
4.1 PongGame.java
/**
* Kelas utama untuk menjalankan game Pong.
* Memanggil GameFrame sebagai window utama.
*/
public class PongGame {
public static void main(String[] args) {
new GameFrame();
}
}
4.2 GameFrame.java
import javax.swing.JFrame;
/**
* GameFrame
* Window utama untuk game Pong.
* Berisi satu GamePanel di dalamnya.
*/
public class GameFrame extends JFrame {
public GameFrame() {
this.add(new GamePanel());
this.setTitle("Pong - Erlangga");
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack(); // ukuran frame mengikuti GamePanel
this.setLocationRelativeTo(null); // tampil di tengah layar
this.setVisible(true);
}
}
4.3 GamePanel.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* GamePanel
* Panel utama yang menangani:
* - Gambar objek (paddle, bola, score)
* - Game loop (Runnable)
* - Input keyboard (KeyListener)
*/
public class GamePanel extends JPanel implements Runnable, KeyListener {
static final int GAME_WIDTH = 800;
static final int GAME_HEIGHT = 600;
static final Dimension SCREEN_SIZE = new Dimension(GAME_WIDTH, GAME_HEIGHT);
Thread gameThread;
Image image;
Graphics graphics;
Paddle paddle1;
Paddle paddle2;
Ball ball;
int player1Score = 0;
int player2Score = 0;
boolean running = true;
public GamePanel() {
newPaddles();
newBall();
this.setPreferredSize(SCREEN_SIZE);
this.setBackground(Color.BLACK);
this.setFocusable(true);
this.addKeyListener(this);
gameThread = new Thread(this);
gameThread.start();
}
/**
* Membuat bola baru di tengah.
*/
public void newBall() {
ball = new Ball(GAME_WIDTH / 2 - Ball.DIAMETER / 2,
GAME_HEIGHT / 2 - Ball.DIAMETER / 2);
}
/**
* Membuat paddle baru (reset posisi).
*/
public void newPaddles() {
paddle1 = new Paddle(
20,
GAME_HEIGHT / 2 - Paddle.PADDLE_HEIGHT / 2,
1
);
paddle2 = new Paddle(
GAME_WIDTH - 20 - Paddle.PADDLE_WIDTH,
GAME_HEIGHT / 2 - Paddle.PADDLE_HEIGHT / 2,
2
);
}
@Override
public void paint(Graphics g) {
// Double buffering: gambar ke image dulu
image = createImage(getWidth(), getHeight());
graphics = image.getGraphics();
draw(graphics);
g.drawImage(image, 0, 0, this);
}
/**
* Menggambar semua objek game.
*/
public void draw(Graphics g) {
// Background
g.setColor(Color.BLACK);
g.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
// Garis tengah putus-putus
g.setColor(Color.GRAY);
for (int i = 0; i < GAME_HEIGHT; i += 30) {
g.fillRect(GAME_WIDTH / 2 - 2, i, 4, 20);
}
// Score
g.setColor(Color.WHITE);
g.setFont(new Font("Consolas", Font.BOLD, 32));
g.drawString(String.valueOf(player1Score), GAME_WIDTH / 2 - 60, 40);
g.drawString(String.valueOf(player2Score), GAME_WIDTH / 2 + 40, 40);
// Paddle & bola
paddle1.draw(g);
paddle2.draw(g);
ball.draw(g);
}
/**
* Update posisi paddle dan bola.
*/
public void move() {
paddle1.move();
paddle2.move();
ball.move();
}
/**
* Cek benturan (collision) dan scoring.
*/
public void checkCollision() {
// Pantulan bola di tepi atas & bawah
if (ball.y <= 0) {
ball.setYDirection(-ball.yVelocity);
}
if (ball.y >= GAME_HEIGHT - Ball.DIAMETER) {
ball.setYDirection(-ball.yVelocity);
}
// Batas gerak paddle (tidak boleh keluar layar)
if (paddle1.y < 0) {
paddle1.y = 0;
}
if (paddle1.y > GAME_HEIGHT - Paddle.PADDLE_HEIGHT) {
paddle1.y = GAME_HEIGHT - Paddle.PADDLE_HEIGHT;
}
if (paddle2.y < 0) {
paddle2.y = 0;
}
if (paddle2.y > GAME_HEIGHT - Paddle.PADDLE_HEIGHT) {
paddle2.y = GAME_HEIGHT - Paddle.PADDLE_HEIGHT;
}
// Bola menabrak paddle kiri
if (ball.getBounds().intersects(paddle1.getBounds())) {
ball.x = paddle1.x + Paddle.PADDLE_WIDTH;
ball.setXDirection(Math.abs(ball.xVelocity)); // pastikan ke kanan
}
// Bola menabrak paddle kanan
if (ball.getBounds().intersects(paddle2.getBounds())) {
ball.x = paddle2.x - Ball.DIAMETER;
ball.setXDirection(-Math.abs(ball.xVelocity)); // pastikan ke kiri
}
// Bola keluar di sisi kiri (poin untuk Player 2)
if (ball.x <= 0) {
player2Score++;
newBall();
newPaddles();
}
// Bola keluar di sisi kanan (poin untuk Player 1)
if (ball.x >= GAME_WIDTH - Ball.DIAMETER) {
player1Score++;
newBall();
newPaddles();
}
}
@Override
public void run() {
// Game loop ~60 FPS
long lastTime = System.nanoTime();
double ticksPerSecond = 60.0;
double ns = 1000000000 / ticksPerSecond;
double delta = 0;
while (running) {
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
while (delta >= 1) {
move();
checkCollision();
repaint();
delta--;
}
}
}
// ==== KeyListener ====
@Override
public void keyPressed(KeyEvent e) {
paddle1.keyPressed(e);
paddle2.keyPressed(e);
}
@Override
public void keyReleased(KeyEvent e) {
paddle1.keyReleased(e);
paddle2.keyReleased(e);
}
@Override
public void keyTyped(KeyEvent e) {
// tidak digunakan
}
}
4.4 Paddle.java
import java.awt.*;
import java.awt.event.KeyEvent;
/**
* Paddle
* Mewakili paddle kiri dan kanan.
* Kontrol:
* - Paddle 1 (kiri) : W / S
* - Paddle 2 (kanan): UP / DOWN
*/
public class Paddle {
public static final int PADDLE_WIDTH = 20;
public static final int PADDLE_HEIGHT = 100;
int x;
int y;
int yVelocity;
int speed = 10;
int paddleNumber; // 1 = kiri, 2 = kanan
public Paddle(int x, int y, int paddleNumber) {
this.x = x;
this.y = y;
this.paddleNumber = paddleNumber;
}
public void draw(Graphics g) {
g.setColor(Color.WHITE);
g.fillRect(x, y, PADDLE_WIDTH, PADDLE_HEIGHT);
}
public void move() {
y += yVelocity;
}
public Rectangle getBounds() {
return new Rectangle(x, y, PADDLE_WIDTH, PADDLE_HEIGHT);
}
/**
* Mengatur kecepatan paddle saat tombol ditekan.
*/
public void keyPressed(KeyEvent e) {
switch (e.getKeyCode()) {
// Paddle 1 (kiri): W/S
case KeyEvent.VK_W:
if (paddleNumber == 1) {
yVelocity = -speed;
}
break;
case KeyEvent.VK_S:
if (paddleNumber == 1) {
yVelocity = speed;
}
break;
// Paddle 2 (kanan): UP/DOWN
case KeyEvent.VK_UP:
if (paddleNumber == 2) {
yVelocity = -speed;
}
break;
case KeyEvent.VK_DOWN:
if (paddleNumber == 2) {
yVelocity = speed;
}
break;
}
}
/**
* Menghentikan paddle saat tombol dilepas.
*/
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()) {
case KeyEvent.VK_W:
case KeyEvent.VK_S:
if (paddleNumber == 1) {
yVelocity = 0;
}
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_DOWN:
if (paddleNumber == 2) {
yVelocity = 0;
}
break;
}
}
}
4.5 Ball.java
import java.awt.*;
import java.util.Random;
/**
* Ball
* Mewakili bola pada game Pong.
* Memiliki posisi, kecepatan, dan bisa dipantulkan.
*/
public class Ball {
public static final int DIAMETER = 30;
int x;
int y;
int xVelocity;
int yVelocity;
int initialSpeed = 5;
Random random;
public Ball(int x, int y) {
this.x = x;
this.y = y;
this.random = new Random();
// Arah awal bola random (kanan/kiri & atas/bawah)
int randomXDirection = random.nextBoolean() ? 1 : -1;
int randomYDirection = random.nextBoolean() ? 1 : -1;
xVelocity = initialSpeed * randomXDirection;
yVelocity = initialSpeed * randomYDirection;
}
public void setXDirection(int xDirection) {
xVelocity = xDirection;
}
public void setYDirection(int yDirection) {
yVelocity = yDirection;
}
/**
* Update posisi bola berdasarkan velocity.
*/
public void move() {
x += xVelocity;
y += yVelocity;
}
public void draw(Graphics g) {
g.setColor(Color.GREEN);
g.fillOval(x, y, DIAMETER, DIAMETER);
}
public Rectangle getBounds() {
return new Rectangle(x, y, DIAMETER, DIAMETER);
}
}
5. Cara Menjalankan Program
-
Buat project Java baru
-
Buat 5 file dengan nama:
-
PongGame.java -
GameFrame.java -
GamePanel.java -
Paddle.java -
Ball.java
lalu copy–paste kode di atas sesuai nama kelasnya.
-
-
Compile semua class.
-
Jalankan program dari
PongGame.main().
Kontrol Permainan
-
Paddle kiri (Player 1)
-
W→ naik -
S→ turun
-
-
Paddle kanan (Player 2)
-
↑ (UP)→ naik -
↓ (DOWN)→ turun
-
Setiap kali bola keluar dari sisi kiri/kanan layar, pemain lawan akan mendapatkan skor dan permainan otomatis mereset posisi bola dan paddle ke tengah.
Tidak ada komentar:
Posting Komentar