Pertemuan 14: Implementasi Pong Game

Tugas Pertemuan 14

Nama: Farrel Aqilla Novianto

NRP: 5025241015

Kelas: Pemrograman Berorientasi Objek (A)

Source Code

Pong Game

Dalam pembuatan Pong Game berbasis OOP, beberapa konsep utama akan diterapkan untuk membangun struktur game yang rapi. Setiap komponen seperti Ball dan Paddle dibuat sebagai class dengan atribut dan perilaku masing-masing, sehingga game mudah dimodelkan sebagai kumpulan objek. Konsep encapsulation digunakan untuk melindungi data internal seperti posisi dan kecepatan, sementara abstraction menyederhanakan logika kompleks seperti pergerakan dan pantulan ke dalam method yang jelas. Jika game dikembangkan lebih jauh, misalnya menambah AI, maka inheritance dan polymorphism dapat digunakan untuk membuat objek turunan dengan perilaku yang berbeda namun tetap mengikuti struktur dasar yang sama. Seluruh proses permainan diatur melalui game loop dalam class Game, termasuk update posisi dan deteksi tabrakan.

Kode Ball.java

/**
 * @author Farrel Aqilla Novianto
 */
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;

public class Ball{
  public int x, y, width = 25, height = 25;
  public int motionX, motionY;
  public Random random;
  private Pong pong;
  public int amountOfHits;
  public Ball(Pong pong){
    this.pong = pong;
    this.random = new Random();
    spawn();
  }

  public void update(Paddle paddle1, Paddle paddle2){
    int speed = 5;
    this.x += motionX * speed;
    this.y += motionY * speed;
    if(this.y + height - motionY > pong.height || this.y + motionY < 0){
      if(this.motionY < 0){
        this.y = 0;
        this.motionY = random.nextInt(4);
        if(motionY == 0){
          motionY = 1;
        }
      }
      else{
        this.motionY = -random.nextInt(4);
        this.y = pong.height - height;
        if(motionY == 0){
          motionY = -1;
        }
      }
    }

    if(checkCollision(paddle1) == 1){
      this.motionX = 1 + (amountOfHits / 5);
      this.motionY = -2 + random.nextInt(4);
      if(motionY == 0){
        motionY = 1;
      }
      amountOfHits++;
    }
    else if(checkCollision(paddle2) == 1){
      this.motionX = -1 - (amountOfHits / 5);
      this.motionY = -2 + random.nextInt(4);
      if(motionY == 0){
        motionY = 1;
      }
      amountOfHits++;
    }

    if(checkCollision(paddle1) == 2){
      paddle2.score++;
      spawn();
    }
    else if(checkCollision(paddle2) == 2){
      paddle1.score++;
      spawn();
    }
  }

  public void spawn(){
    this.amountOfHits = 0;
    this.x = pong.width / 2 - this.width / 2;
    this.y = pong.height / 2 - this.height / 2;
    this.motionY = -2 + random.nextInt(4);
    if(motionY == 0){
      motionY = 1;
    }
    if(random.nextBoolean()){
      motionX = 1;
    }
    else{
      motionX = -1;
    }
  }

  public int checkCollision(Paddle paddle){
    if(this.x < paddle.x + paddle.width && this.x + width > paddle.x && this.y < paddle.y + paddle.height && this.y + height > paddle.y){
      return 1;
    }
    else if((paddle.x > x && paddle.paddleNumber == 1) || (paddle.x < x - width && paddle.paddleNumber == 2)){
      return 2;
    }
    return 0;
  }

  public void render(Graphics g){
    g.setColor(Color.WHITE);
    g.fillOval(x, y, width, height);
  }
}

Kode Paddle.java

/**
 * @author Farrel Aqilla Novianto
 */
import java.awt.Color;
import java.awt.Graphics;

public class Paddle{
  public int paddleNumber;
  public int x, y, width = 50, height = 250;
  public int score;
  public Paddle(Pong pong, int paddleNumber){
    this.paddleNumber = paddleNumber;
    if(paddleNumber == 1){
      this.x = 0;
    }
    if(paddleNumber == 2){
      this.x = pong.width - width;
    }
    this.y = pong.height / 2 - this.height / 2;
  }

  public void render(Graphics g){
    g.setColor(Color.WHITE);
    g.fillRect(x, y, width, height);
  }

  public void move(boolean up){
    int speed = 15;
    if(up){
      if(y - speed > 0){
        y -= speed;
      }
      else{
        y = 0;
      }
    }
    else{
      if(y + height + speed < Pong.pong.height){
        y += speed;
      }
      else{
        y = Pong.pong.height - height;
      }
    }
  }
}

Kode Pong.java

/**
 * @author Farrel Aqilla Novianto
 */
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.Timer;

public class Pong implements ActionListener, KeyListener{
  public static Pong pong;
  public int width = 700, height = 700;
  public Renderer renderer;
  public Paddle player1;
  public Paddle player2;
  public Ball ball;
  public boolean bot = false, selectingDifficulty;
  public boolean w, s, up, down;
  public int gameStatus = 0, scoreLimit = 7, playerWon;
  public int botDifficulty, botMoves, botCooldown = 0;
  public Random random;
  public JFrame jframe;
  public Pong(){
    Timer timer = new Timer(20, this);
    random = new Random();
    jframe = new JFrame("Pong");
    renderer = new Renderer();

    jframe.setSize(width + 15, height + 35);
    jframe.setVisible(true);
    jframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    jframe.add(renderer);
    jframe.addKeyListener(this);

    timer.start();
  }

  public void start(){
    gameStatus = 2;
    player1 = new Paddle(this, 1);
    player2 = new Paddle(this, 2);
    ball = new Ball(this);
  }

  public void update(){
    if(player1.score >= scoreLimit){
      playerWon = 1;
      gameStatus = 3;
    }

    if(player2.score >= scoreLimit){
      gameStatus = 3;
      playerWon = 2;
    }

    if(w){
      player1.move(true);
    }
    if(s){
      player1.move(false);
    }

    if(!bot){
      if(up){
        player2.move(true);
      }
      if(down){
        player2.move(false);
      }
    }
    else{
      if(botCooldown > 0){
        botCooldown--;

        if(botCooldown == 0){
          botMoves = 0;
        }
      }

      if(botMoves < 10){
        if(player2.y + player2.height / 2 < ball.y){
          player2.move(false);
          botMoves++;
        }

        if(player2.y + player2.height / 2 > ball.y){
          player2.move(true);
          botMoves++;
        }

        if(botDifficulty == 0){
          botCooldown = 20;
        }
        if(botDifficulty == 1){
          botCooldown = 15;
        }
        if(botDifficulty == 2){
          botCooldown = 10;
        }
      }
    }

    ball.update(player1, player2);
  }

  public void render(Graphics2D g){
    g.setColor(Color.BLACK);
    g.fillRect(0, 0, width, height);
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    if(gameStatus == 0){
      g.setColor(Color.WHITE);
      g.setFont(new Font("Arial", 1, 50));

      g.drawString("PONG", width / 2 - 75, 50);

      if(!selectingDifficulty){
        g.setFont(new Font("Arial", 1, 30));

        g.drawString("Press Space to Play", width / 2 - 150, height / 2 - 25);
        g.drawString("Press Shift to Play with Bot", width / 2 - 200, height / 2 + 25);
        g.drawString("<< Score Limit: " + scoreLimit + " >>", width / 2 - 150, height / 2 + 75);
      }
    }

    if(selectingDifficulty){
      String string = botDifficulty == 0 ? "Easy" : (botDifficulty == 1 ? "Medium" : "Hard");

      g.setFont(new Font("Arial", 1, 30));

      g.drawString("<< Bot Difficulty: " + string + " >>", width / 2 - 180, height / 2 - 25);
      g.drawString("Press Space to Play", width / 2 - 150, height / 2 + 25);
    }

    if(gameStatus == 1){
      g.setColor(Color.WHITE);
      g.setFont(new Font("Arial", 1, 50));
      g.drawString("PAUSED", width / 2 - 103, height / 2 - 25);
    }

    if(gameStatus == 1 || gameStatus == 2){
      g.setColor(Color.WHITE);
      g.setStroke(new BasicStroke(5f));
      g.drawLine(width / 2, 0, width / 2, height);
      g.setStroke(new BasicStroke(2f));
      g.drawOval(width / 2 - 150, height / 2 - 150, 300, 300);
      g.setFont(new Font("Arial", 1, 50));
      g.drawString(String.valueOf(player1.score), width / 2 - 90, 50);
      g.drawString(String.valueOf(player2.score), width / 2 + 65, 50);
      player1.render(g);
      player2.render(g);
      ball.render(g);
    }

    if(gameStatus == 3){
      g.setColor(Color.WHITE);
      g.setFont(new Font("Arial", 1, 50));
      g.drawString("PONG", width / 2 - 75, 50);
      if(bot && playerWon == 2){
        g.drawString("The Bot Wins!", width / 2 - 170, 200);
      }
      else{
        g.drawString("Player " + playerWon + " Wins!", width / 2 - 165, 200);
      }
      g.setFont(new Font("Arial", 1, 30));
      g.drawString("Press Space to Play Again", width / 2 - 185, height / 2 - 25);
      g.drawString("Press ESC for Menu", width / 2 - 140, height / 2 + 25);
    }
  }

  @Override
  public void actionPerformed(ActionEvent e){
    if(gameStatus == 2){
      update();
    }
    renderer.repaint();
  }

  public static void main(String[] args){
    pong = new Pong();
  }

  @Override
  public void keyPressed(KeyEvent e){
    int id = e.getKeyCode();

    if(id == KeyEvent.VK_W){
      w = true;
    }
    else if(id == KeyEvent.VK_S){
      s = true;
    }
    else if(id == KeyEvent.VK_UP){
      up = true;
    }
    else if(id == KeyEvent.VK_DOWN){
      down = true;
    }
    else if(id == KeyEvent.VK_RIGHT){
      if(selectingDifficulty){
        if(botDifficulty < 2){
          botDifficulty++;
        }
        else{
          botDifficulty = 0;
        }
      }
      else if(gameStatus == 0){
        scoreLimit++;
      }
    }
    else if(id == KeyEvent.VK_LEFT){
      if(selectingDifficulty){
        if(botDifficulty > 0){
          botDifficulty--;
        }
        else{
          botDifficulty = 2;
        }
      }
      else if(gameStatus == 0 && scoreLimit > 1){
        scoreLimit--;
      }
    }
    else if(id == KeyEvent.VK_ESCAPE && (gameStatus == 2 || gameStatus == 3)){
      gameStatus = 0;
    }
    else if(id == KeyEvent.VK_SHIFT && gameStatus == 0){
      bot = true;
      selectingDifficulty = true;
    }
    else if(id == KeyEvent.VK_SPACE){
      if(gameStatus == 0 || gameStatus == 3){
        if(!selectingDifficulty){
          bot = false;
        }
        else{
          selectingDifficulty = false;
        }

        start();
      }
      else if(gameStatus == 1){
        gameStatus = 2;
      }
      else if(gameStatus == 2){
        gameStatus = 1;
      }
    }
  }

  @Override
  public void keyReleased(KeyEvent e){
    int id = e.getKeyCode();

    if(id == KeyEvent.VK_W){
      w = false;
    }
    else if(id == KeyEvent.VK_S){
      s = false;
    }
    else if(id == KeyEvent.VK_UP){
      up = false;
    }
    else if(id == KeyEvent.VK_DOWN){
      down = false;
    }
  }
  
  @Override
  public void keyTyped(KeyEvent e){
  }
}

Kode Renderer.java

/**
 * @author Farrel Aqilla Novianto
 */
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;

public class Renderer extends JPanel{
  private static final long serialVersionUID = 1L;
  @Override
  protected void paintComponent(Graphics g){
    super.paintComponent(g);
    Pong.pong.render((Graphics2D) g);
  }
}

Dokumentasi

Kesimpulan

Implementasi Pong Game dengan OOP bukan hanya latihan teknis membuat permainan sederhana, tetapi juga cara efektif untuk memahami bagaimana objek, enkapsulasi, abstraksi, pewarisan, dan polimorfisme bekerja dalam sebuah sistem yang nyata. Dengan menerapkan konsep-konsep tersebut, program menjadi lebih fleksibel, aman, serta mudah diperluas di masa depan.

Komentar

Postingan populer dari blog ini

Final Project: Tetris

Pertemuan 4: Membuat Clock Display