Membuat Game Ular Seru dengan Python & PyQt5

🐍 Belajar Membuat Game Ular Seru dengan Python & PyQt5 – PyCoding ID

Pendahuluan

PyCoding-ID - Siapa yang tidak kenal dengan game ular? Game klasik ini sudah hadir sejak zaman ponsel Nokia lawas, dan hingga sekarang masih menjadi permainan yang sederhana namun bikin ketagihan. Seiring perkembangan teknologi, kita bisa membuat versi modernnya menggunakan bahasa pemrograman Python.

Nah, pada artikel kali ini, PyCoding ID akan mengajak kamu untuk membuat game ular dengan tampilan modern, background gradasi biru, serta fitur tambahan seperti rintangan dan skor. Seru, bukan? Selain belajar coding, kamu juga bisa merasakan nostalgia memainkan game yang legendaris.

Artikel ini cukup panjang, jadi pastikan kamu membacanya sampai selesai. Kita akan bahas mulai dari konsep, kode lengkap, cara menjalankan, hingga tips optimasi.

Game Ular Seru dengan Python - PyCodingID

Kenapa Python Cocok untuk Membuat Game?

Sebelum masuk ke kode, mari kita pahami dulu alasan mengapa Python cocok untuk membuat game sederhana:

  1. Mudah dipelajari – Sintaks Python sederhana, cocok untuk pemula. 
  2. Komunitas luas – Kalau mentok, banyak sekali tutorial dan forum Python yang bisa membantu.
  3. Banyak library grafis – Salah satunya adalah PyQt5, yang bisa dipakai untuk membuat GUI (Graphical User Interface) dengan cepat.
  4. Cross-platform – Python bisa berjalan di Windows, macOS, maupun Linux.

Di sini, kita akan menggunakan PyQt5 untuk mengelola grafik, event keyboard, dan tampilan layar.

Konsep Game Ular yang Akan Dibuat

Game ular yang kita buat bersama PyCoding ID punya beberapa fitur menarik:

  • Latar belakang gradasi biru → membuat tampilan lebih modern. 
  •  Ular berwarna hijau dengan efek gradasi → kepala dan badan berbeda warna.
  • Makanan berbentuk bulatan kuning → jika dimakan, skor bertambah.
  • Rintangan merah → muncul setelah skor mencapai 5 poin.
  • Tombol restart → supaya mudah memulai ulang permainan.
 Dengan konsep ini, game terasa lebih hidup dibanding game ular klasik yang monoton.

Kode Lengkap Game Ular Python + PyQt5

Berikut adalah kode Python yang bisa langsung kamu jalankan:

PyCoding

import sys
import random
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtCore import Qt, QTimer, QPoint, QRect
from PyQt5.QtGui import QPainter, QColor, QFont, QLinearGradient

class WormZoneGame(QWidget):
    # Pengaturan awal permainan
    BOARD_WIDTH = 600
    BOARD_HEIGHT = 600
    DOT_SIZE = 20
    BASE_SPEED = 100

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        # Mengatur jendela utama
        self.setGeometry(300, 300, self.BOARD_WIDTH, self.BOARD_HEIGHT)
        self.setWindowTitle('Ular~Gode  PyCoding')

        # Timer game
        self.timer = QTimer()
        self.timer.timeout.connect(self.updateGame)

        # Tombol restart
        self.restart_button = QPushButton("Mulai Ulang", self)
        self.restart_button.setFont(QFont('Arial', 14, QFont.Bold))
        self.restart_button.setStyleSheet(
            "background-color: #4CAF50; color: white; "
            "border-radius: 10px; border: 2px solid #388E3C;"
        )
        self.restart_button.setGeometry(self.BOARD_WIDTH // 2 - 75,
                                        self.BOARD_HEIGHT // 2 + 50,
                                        150, 40)
        self.restart_button.clicked.connect(self.startGame)
        self.restart_button.hide()
        
        self.startGame()
        self.show()

    def startGame(self):
        # Reset variabel game
        self.inGame = True
        self.direction = 'right'
        self.score = 0
        self.body_length = 5
        self.speed = self.BASE_SPEED
        self.obstacle = QPoint(-1, -1)  # rintangan di luar layar
        
        # Posisi awal ular
        self.snake_body = []
        for i in range(self.body_length):
            self.snake_body.append(QPoint(self.BOARD_WIDTH // 2 - i * self.DOT_SIZE,
                                          self.BOARD_HEIGHT // 2))

        self.locateFood()
        self.locateObstacle()
        self.timer.start(self.speed)
        self.restart_button.hide()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

        # Background gradasi biru
        gradient = QLinearGradient(0, 0, 0, self.BOARD_HEIGHT)
        gradient.setColorAt(0.0, QColor(0, 100, 255))   # biru muda atas
        gradient.setColorAt(1.0, QColor(0, 30, 100))    # biru gelap bawah
        painter.setBrush(gradient)
        painter.setPen(Qt.NoPen)
        painter.drawRect(0, 0, self.BOARD_WIDTH, self.BOARD_HEIGHT)

        if self.inGame:
            # Makanan
            food_color = QColor(255, 200, 50)
            painter.setBrush(food_color)
            painter.setPen(Qt.NoPen)
            painter.drawEllipse(self.food_x, self.food_y,
                                self.DOT_SIZE, self.DOT_SIZE)

            # Rintangan
            if self.score >= 5:
                obstacle_color = QColor(200, 50, 50)
                painter.setBrush(obstacle_color)
                painter.setPen(Qt.NoPen)
                painter.drawRect(self.obstacle.x(), self.obstacle.y(),
                                 self.DOT_SIZE, self.DOT_SIZE)

            # Ular
            for i, segment in enumerate(self.snake_body):
                if i == 0:
                    head_gradient = QLinearGradient(segment.x(), segment.y(),
                                                    segment.x() + self.DOT_SIZE,
                                                    segment.y() + self.DOT_SIZE)
                    head_gradient.setColorAt(0.0, QColor(0, 255, 100))
                    head_gradient.setColorAt(1.0, QColor(0, 150, 50))
                    painter.setBrush(head_gradient)
                else:
                    body_gradient = QLinearGradient(segment.x(), segment.y(),
                                                    segment.x() + self.DOT_SIZE,
                                                    segment.y() + self.DOT_SIZE)
                    body_gradient.setColorAt(0.0, QColor(0, 150, 50))
                    body_gradient.setColorAt(1.0, QColor(0, 100, 25))
                    painter.setBrush(body_gradient)
                
                painter.setPen(Qt.NoPen)
                painter.drawEllipse(segment.x(), segment.y(),
                                    self.DOT_SIZE, self.DOT_SIZE)

            # Skor
            painter.setPen(QColor(255, 255, 255))
            painter.setFont(QFont('Arial', 12))
            painter.drawText(10, 20, f"Skor: {self.score}")

            # Footer
            footer_msg = "© 2023 Noval MR. Daud"
            footer_font = QFont('Arial', 10)
            painter.setFont(footer_font)
            footer_metrics = painter.fontMetrics()
            footer_width = footer_metrics.horizontalAdvance(footer_msg)
            painter.setPen(QColor(230, 230, 230))
            painter.drawText((self.BOARD_WIDTH - footer_width) // 2,
                             self.BOARD_HEIGHT - 10,
                             footer_msg)
        else:
            self.gameOver(painter)

    def gameOver(self, painter):
        msg = "Game Over"
        font = QFont('Arial', 24, QFont.Bold)
        painter.setFont(font)
        
        metrics = painter.fontMetrics()
        text_width = metrics.horizontalAdvance(msg)
        
        painter.setPen(QColor(255, 255, 255))
        painter.drawText((self.BOARD_WIDTH - text_width) // 2,
                         self.BOARD_HEIGHT // 2,
                         msg)
        
        # Footer juga di layar game over
        copyright_msg = "© 2023 Noval MR. Daud"
        copyright_font = QFont('Arial', 10)
        painter.setFont(copyright_font)
        copyright_metrics = painter.fontMetrics()
        copyright_width = copyright_metrics.horizontalAdvance(copyright_msg)
        
        painter.setPen(QColor(200, 200, 200))
        painter.drawText((self.BOARD_WIDTH - copyright_width) // 2,
                         self.BOARD_HEIGHT - 20,
                         copyright_msg)
        
        self.restart_button.show()

    def keyPressEvent(self, e):
        key = e.key()
        
        if key == Qt.Key_Left and self.direction != 'right':
            self.direction = 'left'
        elif key == Qt.Key_Right and self.direction != 'left':
            self.direction = 'right'
        elif key == Qt.Key_Up and self.direction != 'down':
            self.direction = 'up'
        elif key == Qt.Key_Down and self.direction != 'up':
            self.direction = 'down'

    def updateGame(self):
        if self.inGame:
            self.moveSnake()
            self.checkFood()
            self.checkCollision()
        self.update()

    def moveSnake(self):
        head_pos = self.snake_body[0]
        new_head_pos = QPoint(head_pos.x(), head_pos.y())
        
        if self.direction == 'left':
            new_head_pos.setX(head_pos.x() - self.DOT_SIZE)
        elif self.direction == 'right':
            new_head_pos.setX(head_pos.x() + self.DOT_SIZE)
        elif self.direction == 'up':
            new_head_pos.setY(head_pos.y() - self.DOT_SIZE)
        elif self.direction == 'down':
            new_head_pos.setY(head_pos.y() + self.DOT_SIZE)

        self.snake_body.insert(0, new_head_pos)

        if len(self.snake_body) > self.body_length:
            self.snake_body.pop()

    def checkCollision(self):
        # Dinding
        if (self.snake_body[0].x() < 0 or
            self.snake_body[0].x() >= self.BOARD_WIDTH or
            self.snake_body[0].y() < 0 or
            self.snake_body[0].y() >= self.BOARD_HEIGHT):
            self.inGame = False

        # Badan sendiri
        for segment in self.snake_body[1:]:
            if self.snake_body[0] == segment:
                self.inGame = False

        # Rintangan
        if self.score >= 5:
            obstacle_rect = QRect(self.obstacle.x(), self.obstacle.y(),
                                  self.DOT_SIZE, self.DOT_SIZE)
            head_rect = QRect(self.snake_body[0].x(), self.snake_body[0].y(),
                              self.DOT_SIZE, self.DOT_SIZE)
            if head_rect.intersects(obstacle_rect):
                self.inGame = False

        if not self.inGame:
            self.timer.stop()

    def checkFood(self):
        head_rect = QRect(self.snake_body[0].x(), self.snake_body[0].y(),
                          self.DOT_SIZE, self.DOT_SIZE)
        food_rect = QRect(self.food_x, self.food_y,
                          self.DOT_SIZE, self.DOT_SIZE)

        if head_rect.intersects(food_rect):
            self.body_length += 1
            self.score += 1
            self.locateFood()
            self.increaseSpeed()
            self.locateObstacle()

    def locateFood(self):
        max_pos_x = (self.BOARD_WIDTH // self.DOT_SIZE) - 1
        max_pos_y = (self.BOARD_HEIGHT // self.DOT_SIZE) - 1
        
        self.food_x = random.randint(0, max_pos_x) * self.DOT_SIZE
        self.food_y = random.randint(0, max_pos_y) * self.DOT_SIZE

    def locateObstacle(self):
        if self.score % 5 == 0 and self.score > 0:
            max_pos_x = (self.BOARD_WIDTH // self.DOT_SIZE) - 1
            max_pos_y = (self.BOARD_HEIGHT // self.DOT_SIZE) - 1
            self.obstacle = QPoint(random.randint(0, max_pos_x) * self.DOT_SIZE,
                                   random.randint(0, max_pos_y) * self.DOT_SIZE)
        
    def increaseSpeed(self):
        if self.score % 5 == 0 and self.score > 0:
            if self.speed > 30:
                self.speed -= 10
                self.timer.setInterval(self.speed)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = WormZoneGame()
    sys.exit(app.exec_())


Cara Menjalankan Program

  • Pastikan Python 3 sudah terinstall di laptop/PC kamu.
  • Install library PyQt5 dengan perintah: 

pip install pyqt5

  • Simpan dan jalankan aplikasi tersebut

Penjelasan Kode

Mari kita bedah bagian penting dari kode tersebut:

  • paintEvent → digunakan untuk menggambar background, ular, makanan, rintangan, dan teks skor.
  • keyPressEvent → menangani input keyboard (panah kiri, kanan, atas, bawah).
  • updateGame → dipanggil terus menerus oleh QTimer untuk memperbarui posisi ular.
  • checkCollision → memeriksa apakah ular menabrak dinding, badan sendiri, atau rintangan.
  • checkFood → jika ular memakan makanan, skor bertambah dan ular memanjang.

Dengan sistem ini, kita bisa membuat gameplay yang sederhana namun menyenangkan.


 

 

 

Fitur Tambahan yang Bisa Dikembangkan

Game ini masih bisa dikembangkan lebih jauh. Berikut beberapa ide dari PyCoding ID:

  1. Level kesulitan → semakin tinggi skor, semakin cepat ular bergerak.
  2. Tampilan makanan berbeda-beda → misalnya buah apel, jeruk, pisang.
  3. Sound effect → suara ketika makan atau game over.
  4. Leaderboard → menyimpan skor tertinggi ke file atau database.
  5. Mode multiplayer lokal → dua pemain dengan dua ular dalam satu layar.

Dengan tambahan ini, game akan lebih seru dan menantang.


Manfaat Belajar Membuat Game dengan Python

Selain sekadar bermain, membuat game juga memberikan banyak manfaat:

  • Mengasah logika pemrograman – kita belajar algoritma pergerakan, deteksi tabrakan, dll.
  • Melatih kreativitas – mengubah konsep sederhana menjadi permainan yang menarik.
  • Menambah portfolio – cocok untuk mahasiswa atau programmer pemula.
  • Meningkatkan motivasi belajar – karena hasilnya bisa langsung dimainkan.

Itulah kenapa PyCoding ID selalu mendorong pembaca untuk belajar dengan metode project-based learning.


Kesimpulan

Membuat game ular dengan Python + PyQt5 bukan hanya menyenangkan, tapi juga melatih logika dan kreativitas.
Dengan tampilan background gradasi biru, rintangan, skor, serta tombol restart, permainan terasa lebih modern dibanding versi klasiknya.

Artikel ini sudah membahas mulai dari konsep, kode lengkap, cara menjalankan, hingga ide pengembangan.
Semoga tutorial dari PyCoding ID ini bisa menjadi inspirasi untuk proyek belajar kamu berikutnya.

Jangan lupa share artikel ini agar semakin banyak teman programmer pemula yang bisa belajar membuat game dengan Python. Selamat mencoba, dan semoga artikel ini bermanfaat bagi perjalanan belajar Python Anda di PyCodingID

Posting Komentar

0 Komentar