34. Menggerakkan benda

Bab 32 dan 33 membahas menggambar dan input. Bab ini menghubungkan keduanya: objek yang bergerak sebagai respons terhadap input, memantul dari dinding, dan bertabrakan satu sama lain.

Posisi sebagai variable

Cara paling sederhana untuk menggerakkan apa pun adalah menyimpan posisinya dalam variable dan memperbaruinya setiap frame. Di bagian gambar, gunakan variable tersebut untuk memposisikan bentuk:

x = 100
y = 100

# In the game loop update section:
x += 3   # move 3 pixels right every frame

# In the draw section:
pygame.draw.rect(screen, (255, 100, 0), (x, y, 40, 40))

Pada 60 fps, menambahkan 3 setiap frame menggerakkan objek 180 piksel per detik.

Kecepatan

Kecepatan adalah perubahan posisi per frame. Simpan dalam variable dx (perubahan x) dan dy (perubahan y):

x  = 100
y  = 100
dx = 3
dy = 2

# Update section each frame:
x += dx
y += dy

Untuk membalik arah, negasikan kecepatan: dx = -dx. Ini adalah pola "pantul" — digunakan untuk bola yang memantul.

pygame.Rect

pygame.Rect(x, y, width, height) adalah objek persegi panjang yang disediakan pygame untuk pemosisian dan deteksi tabrakan yang mudah.

rect = pygame.Rect(100, 200, 60, 60)

Atribut yang berguna:

Atribut Arti
rect.x Posisi x tepi kiri
rect.y Posisi y tepi atas
rect.width Lebar dalam piksel
rect.height Tinggi dalam piksel
rect.centerx Koordinat x pusat
rect.centery Koordinat y pusat
rect.right Koordinat x tepi kanan
rect.bottom Koordinat y tepi bawah

Gerakkan rect dengan menugaskan ke rect.x dan rect.y. Gunakan langsung dalam pemanggilan draw — pygame.draw.rect menerima sebuah Rect:

rect = pygame.Rect(100, 200, 60, 60)
rect.x += 3

pygame.draw.rect(screen, (255, 0, 0), rect)

Mengatur rect.centerx dan rect.centery memindahkan rect sehingga pusatnya berada di posisi tersebut:

rect.centerx = 400
rect.centery = 300
# rect is now centred in an 800x600 window

Mengontrol frame rate

clock.tick(60) membatasi loop ke 60 frame per detik. Tanpa itu, CPU yang cepat menjalankan ribuan frame per detik dan objek terbang melintasi layar terlalu cepat. Dengan itu, kecepatan gerakan bisa diprediksi: menambahkan 4 ke x setiap frame menggerakkan objek 4 * 60 = 240 piksel per detik, di mesin mana pun.

Pada mesin yang sangat lambat, clock.tick(60) tidak bisa memaksa loop berjalan lebih cepat dari kecepatan alaminya. Frame rate sebenarnya mungkin lebih rendah dari 60 pada hardware yang lambat. Untuk tujuan buku ini, 60 fps sudah cukup andal.

Pemeriksaan batas

Objek yang bergerak keluar dari layar perlu dihentikan atau dipantulkan kembali. Periksa tepi secara manual setiap frame.

Berhenti di dinding:

if rect.left < 0:
    rect.left = 0
if rect.right > 800:
    rect.right = 800
if rect.top < 0:
    rect.top = 0
if rect.bottom > 600:
    rect.bottom = 600

rect.left < 0 berarti tepi kiri telah melewati dinding kiri. Mengatur rect.left = 0 mendorongnya kembali.

Memantul dari dinding:

if rect.left < 0 or rect.right > 800:
    dx = -dx
if rect.top < 0 or rect.bottom > 600:
    dy = -dy

Membalik kecepatan mengirim objek kembali ke arah asalnya.

Deteksi tabrakan dengan Rect.colliderect

Rect.colliderect(other_rect) mengembalikan True jika dua persegi panjang saling tumpang tindih:

player_rect = pygame.Rect(100, 100, 50, 50)
coin_rect   = pygame.Rect(120, 110, 30, 30)

if player_rect.colliderect(coin_rect):
    print("collected!")

Gunakan ini untuk mendeteksi saat pemain menyentuh koin, musuh, atau objek lainnya.

Contoh lengkap: kotak tombol panah yang memantul

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Moving Square")
clock = pygame.time.Clock()

rect  = pygame.Rect(370, 270, 60, 60)
speed = 4

running = True
while running:
    # 1. Events
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 2. Update
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        rect.x -= speed
    if keys[pygame.K_RIGHT]:
        rect.x += speed
    if keys[pygame.K_UP]:
        rect.y -= speed
    if keys[pygame.K_DOWN]:
        rect.y += speed

    # Clamp to window edges
    if rect.left < 0:
        rect.left = 0
    if rect.right > 800:
        rect.right = 800
    if rect.top < 0:
        rect.top = 0
    if rect.bottom > 600:
        rect.bottom = 600

    # 3. Draw
    screen.fill((30, 30, 30))
    pygame.draw.rect(screen, (255, 100, 50), rect)

    # 4. Flip
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Buka exercises/34/01-bouncing-ball.py. Tambahkan bola (lingkaran) yang bergerak sendiri menggunakan dx dan dy. Setiap frame, tambahkan dx ke x dan dy ke y. Saat bola menyentuh tepi kiri atau kanan jendela, negasikan dx. Saat menyentuh atas atau bawah, negasikan dy. Mulai dengan dx = 3, dy = 2.

Pekerjaan Rumah

Soal 1 — Penggerak terkunci

Buka exercises/34/homework/01-clamped-mover.py. Sebuah kotak bergerak dengan tombol panah. Kotak tersebut tidak boleh keluar dari jendela — clamp ke semua empat tepi.

Soal 2 — Dua bola memantul

Buka exercises/34/homework/02-two-balls.py. Dua bola dimulai di posisi berbeda dengan kecepatan berbeda. Keduanya memantul dari semua empat dinding secara independen. Setiap bola harus berwarna berbeda.

Soal 3 — Mengikuti mouse

Buka exercises/34/homework/03-follow-mouse.py. Sebuah kotak bergerak menuju posisi mouse saat ini setiap frame. Kotak tidak langsung melompat ke kursor; ia bergerak sejumlah piksel tetap ke arahnya per frame, berhenti saat tiba. Petunjuk: hitung perbedaan x dan y, dan gerakkan sebagian dari perbedaan itu.

Tantangan — Kumpulkan target

Buka exercises/34/homework/04-collect-target.py. Kotak pemain bergerak dengan tombol panah. Lingkaran target stasioner digambar di suatu tempat di layar. Saat rect pemain tumpang tindih dengan target (gunakan colliderect atau pemeriksaan jarak untuk lingkaran), pindahkan target ke posisi acak baru dan cetak "collected!" ke terminal.

Macet atau sudah selesai? Buka halaman solusi pekerjaan rumah.