34. Menggerakkan benda — Solusi pekerjaan rumah

Soal 1 — Penggerak terkunci

Soal. Sebuah kotak yang bergerak dengan tombol panah dan tetap berada di dalam jendela.

Cara memikirkannya. Terapkan gerakan get_pressed, lalu clamp semua empat tepi menggunakan rect.left, rect.right, rect.top, rect.bottom.

Solusi.

import pygame

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

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

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

    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

    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

    screen.fill((20, 20, 40))
    pygame.draw.rect(screen, (100, 200, 255), rect)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Kesalahan umum.

  • Clamp rect.x < 0 tapi tidak mempertimbangkan lebar. Jika kamu memeriksa x < 0 dan clamp ke x = 0, tepi kiri sudah benar. Tapi untuk tepi kanan kamu harus memeriksa rect.right > 800 (yaitu rect.x + rect.width > 800). Menggunakan rect.right dan rect.left lebih sederhana daripada melakukan aritmatika secara manual.

Soal 2 — Dua bola memantul

Soal. Dua bola memantul dari semua empat dinding secara independen.

Cara memikirkannya. Gunakan dua set variable posisi dan kecepatan. Logika pantulan sama untuk keduanya; cukup tulis dua kali (atau buat function).

Solusi.

import pygame

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

ax, ay, adx, ady = 200, 150, 4, 3
bx, by, bdx, bdy = 600, 400, -3, 5
radius = 20
W, H = 800, 600

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

    ax += adx
    ay += ady
    if ax - radius < 0 or ax + radius > W:
        adx = -adx
    if ay - radius < 0 or ay + radius > H:
        ady = -ady

    bx += bdx
    by += bdy
    if bx - radius < 0 or bx + radius > W:
        bdx = -bdx
    if by - radius < 0 or by + radius > H:
        bdy = -bdy

    screen.fill((10, 10, 10))
    pygame.draw.circle(screen, (255, 80, 80),  (ax, ay), radius)
    pygame.draw.circle(screen, (80, 180, 255), (bx, by), radius)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Pemeriksaan batas menggunakan ax - radius < 0 karena posisi adalah pusat lingkaran, dan lingkaran memanjang sejauh radius piksel ke setiap arah.

Kesalahan umum.

  • Memeriksa ax < 0 alih-alih ax - radius < 0. Pusat bisa berada di (5, ...) sementara tepi kiri bola sudah keluar dari layar.

Soal 3 — Mengikuti mouse

Soal. Sebuah kotak bergerak menuju posisi mouse setiap frame, satu langkah tetap dalam satu waktu.

Cara memikirkannya. Hitung dx = mx - rect.centerx dan dy = my - rect.centery. Ini adalah jarak yang perlu ditempuh. Setiap frame, gerakkan sebagian dari jarak tersebut (langkah lebih kecil dari total jarak) atau berhenti saat cukup dekat.

Solusi.

import pygame

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

rect  = pygame.Rect(370, 270, 50, 50)
speed = 5

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

    mx, my = pygame.mouse.get_pos()
    dx = mx - rect.centerx
    dy = my - rect.centery

    if abs(dx) > speed:
        rect.x += speed if dx > 0 else -speed
    else:
        rect.centerx = mx

    if abs(dy) > speed:
        rect.y += speed if dy > 0 else -speed
    else:
        rect.centery = my

    screen.fill((20, 20, 20))
    pygame.draw.rect(screen, (255, 200, 50), rect)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

speed if dx > 0 else -speed bergerak satu langkah ke arah target. Cabang else meng-snap ke target saat sisa jarak lebih kecil dari satu langkah, mencegah getaran.

Tantangan — Kumpulkan target

Soal. Pemain bergerak dengan tombol panah. Menyentuh target memindahkannya ke posisi acak.

Cara memikirkannya. Gunakan pygame.Rect.colliderect untuk tabrakan. import random untuk memilih posisi baru. Rect target harus tetap sepenuhnya di dalam jendela — kurangi target.width dan target.height dari batas atas.

Solusi.

import pygame
import random

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Collect Target")
clock = pygame.time.Clock()
W, H = 800, 600

player = pygame.Rect(370, 270, 50, 50)
target = pygame.Rect(random.randint(0, W - 40), random.randint(0, H - 40), 40, 40)
speed  = 5

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

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

    # clamp
    player.clamp_ip(pygame.Rect(0, 0, W, H))

    if player.colliderect(target):
        print("collected!")
        target.x = random.randint(0, W - target.width)
        target.y = random.randint(0, H - target.height)

    screen.fill((20, 20, 30))
    pygame.draw.circle(screen, (255, 220, 0),
                       (target.centerx, target.centery), target.width // 2)
    pygame.draw.rect(screen, (100, 200, 255), player)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

pygame.Rect.clamp_ip(boundary_rect) memindahkan rect sehingga pas di dalam batas — alternatif bawaan untuk empat pemeriksaan tepi terpisah.

Kesalahan umum.

  • Menempatkan target secara acak hingga x W tanpa mengurangi target.width, memungkinkan sebagian target muncul di luar layar.

Selesai?

Bab 35 menambahkan rendering teks dan pelacakan skor untuk melengkapi game loop dasar.