35. Skor dan teks — Solusi pekerjaan rumah

Soal 1 — Tampilan skor

Soal. Angka di layar yang berubah dengan tombol panah Atas/Bawah.

Cara memikirkannya. Satu variable integer, dua pemeriksaan KEYDOWN. Render ulang Surface teks setiap frame.

Solusi.

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Score Display")
clock  = pygame.time.Clock()
font   = pygame.font.SysFont("arial", 48)

value = 0

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                value += 1
            if event.key == pygame.K_DOWN:
                value = max(0, value - 1)

    screen.fill((20, 20, 40))
    surf = font.render(str(value), True, (255, 255, 255))
    screen.blit(surf, (10, 10))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

max(0, value - 1) mencegah nilai turun di bawah 0 tanpa memerlukan pemeriksaan if terpisah.

Kesalahan umum.

  • Menggunakan get_pressed untuk tombol panah di sini. get_pressed terpicu setiap frame saat ditahan, sehingga angka akan naik cepat. KEYDOWN terpicu sekali per penekanan, memberikan kontrol per-penekanan.

Soal 2 — Timer hitung mundur

Soal. Hitung mundur dari 10 ke 0 dan tampilkan "Time's up!" di akhir.

Cara memikirkannya. Tambahkan 1/60 ke akumulator setiap frame. Integer yang ditampilkan adalah 10 - int(accumulator), dibulatkan ke bawah ke 0. Saat akumulator mencapai 10, hitungan mundur selesai.

Solusi.

import pygame

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Countdown")
clock  = pygame.time.Clock()
font   = pygame.font.SysFont("arial", 64)
small  = pygame.font.SysFont("arial", 36)

elapsed = 0.0
DURATION = 10.0
done = False

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

    if not done:
        elapsed += 1 / 60
        if elapsed >= DURATION:
            elapsed = DURATION
            done = True

    remaining = int(DURATION - elapsed)

    screen.fill((20, 20, 40))

    if done:
        surf = small.render("Time's up!", True, (255, 100, 100))
        screen.blit(surf, ((800 - surf.get_width()) // 2, 260))
    else:
        surf = font.render(str(remaining), True, (255, 255, 255))
        screen.blit(surf, ((800 - surf.get_width()) // 2, 230))

    pygame.display.flip()
    clock.tick(60)

pygame.quit()

clock.tick(60) mengembalikan jumlah milidetik sejak panggilan terakhir — alternatif yang lebih akurat untuk timing adalah clock.tick(60) / 1000.0 sebagai delta time. Untuk latihan ini, asumsi tepat 60 fps dengan 1/60 sudah cukup.

Kesalahan umum.

  • Menghitung remaining sebagai DURATION - elapsed sebagai float dan menampilkannya. Timer menampilkan detik pecahan, yang terlihat tidak rapi. int(...) memotong ke detik penuh.

Soal 3 — Kumpulkan dan skor

Soal. Pemain tombol panah, target diposisikan ulang saat disentuh, skor ditampilkan di atas.

Cara memikirkannya. Gabungkan pola kumpul dari bab 34 dengan font.render / screen.blit.

Solusi.

import pygame
import random

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Collect")
clock  = pygame.time.Clock()
font   = pygame.font.SysFont("arial", 28)
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)
score  = 0
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

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

    if player.colliderect(target):
        score += 1
        target.x = random.randint(0, W - target.width)
        target.y = random.randint(0, H - target.height)

    screen.fill((20, 20, 40))
    pygame.draw.circle(screen, (255, 220, 0), target.center, target.width // 2)
    pygame.draw.rect(screen, (100, 200, 255), player)

    score_surf = font.render(f"Score: {score}", True, (255, 255, 255))
    screen.blit(score_surf, (10, 10))

    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Tantangan — Tiga nyawa

Soal. Kumpulkan koin (skor), hindari kotak merah (nyawa). Tampilkan keduanya. Game over saat nyawa 0.

Cara memikirkannya. Simpan list kotak merah. Setiap frame, gerakkan setiap kotak sejumlah tetap ke arah pemain (atau dalam arah tetap). Saat bertabrakan dengan pemain, kurangi nyawa dan posisikan ulang kotak. Saat bertabrakan dengan koin, tambah skor dan posisikan ulang koin.

Solusi.

import pygame
import random

pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("Three Lives")
clock  = pygame.time.Clock()
font   = pygame.font.SysFont("arial", 28)
large  = pygame.font.SysFont("arial", 64)
W, H   = 800, 600

player    = pygame.Rect(370, 270, 50, 50)
coin      = pygame.Rect(random.randint(0, W-30), random.randint(0, H-30), 30, 30)
enemies   = [pygame.Rect(random.randint(0, W-40), random.randint(0, H-40), 40, 40)
             for _ in range(3)]
score     = 0
lives     = 3
speed     = 4
enemy_speed = 2
game_over = False

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

    if not game_over:
        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
        player.clamp_ip(pygame.Rect(0, 0, W, H))

        for enemy in enemies:
            if enemy.centerx < player.centerx:
                enemy.x += enemy_speed
            elif enemy.centerx > player.centerx:
                enemy.x -= enemy_speed
            if enemy.centery < player.centery:
                enemy.y += enemy_speed
            elif enemy.centery > player.centery:
                enemy.y -= enemy_speed

            if player.colliderect(enemy):
                lives -= 1
                enemy.x = random.randint(0, W - enemy.width)
                enemy.y = random.randint(0, H - enemy.height)
                if lives <= 0:
                    game_over = True
                    break

        if not game_over and player.colliderect(coin):
            score += 1
            coin.x = random.randint(0, W - coin.width)
            coin.y = random.randint(0, H - coin.height)

    screen.fill((20, 20, 40))
    pygame.draw.circle(screen, (255, 220, 0), coin.center, coin.width // 2)
    for enemy in enemies:
        pygame.draw.rect(screen, (220, 50, 50), enemy)
    pygame.draw.rect(screen, (100, 200, 255), player)

    screen.blit(font.render(f"Score: {score}", True, (255, 255, 255)), (10, 10))
    screen.blit(font.render(f"Lives: {lives}", True, (255, 200, 200)), (10, 44))

    if game_over:
        surf = large.render("Game Over", True, (255, 80, 80))
        screen.blit(surf, ((W - surf.get_width()) // 2, (H - surf.get_height()) // 2))

    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Musuh bergerak satu langkah menuju pemain setiap frame. Saat musuh bertabrakan dengan pemain, ia muncul kembali di posisi acak. break setelah mendeteksi tabrakan mematikan keluar dari loop musuh lebih awal sehingga hanya satu nyawa yang hilang per frame meskipun beberapa musuh tumpang tindih dengan pemain.

Kesalahan umum.

  • Memodifikasi list saat mengiterasi di atasnya. Solusi di sini hanya memodifikasi posisi item list, bukan struktur list itu sendiri, jadi mengiterasi dengan for enemy in enemies aman.

Selesai?

Bagian 7 diakhiri dengan dua mini-proyek — Coin Collector dan Collect-All-Coins — yang menggabungkan game loop, menggambar, gerakan, dan skor menjadi program yang bisa dimainkan.