Menu
CoddyTech

가중치 기반 선택

Coddy Pygame 여정의 게임 규칙 섹션에 포함된 레슨. 29개 중 18번째.

떨어지는 것이 모두 똑같지 않으면 게임이 더 재미있습니다. 여기서는 떨어지는 것의 절반이 작은 바위이고, 일부는 큰 바위이며, 몇 개는 5점의 가치가 있는 보석입니다. random.choice는 모든 항목을 같은 빈도로 선택합니다. random.choices는 각 항목이 선택되는 빈도를 나타내는 weights를 받습니다.

def pick_kind():
    return random.choices(["small", "big", "gem"], weights=[5, 3, 2])[0]

가중치 5, 3, 2를 모두 더하면 10이므로, 10개가 떨어질 때마다 작은 바위는 약 5개, 큰 바위는 3개, 보석은 2개입니다. 가중치는 백분율이 아니라 비율입니다. [50, 30, 20]도 같은 뜻입니다.

random.choices는 한 번에 여러 개를 선택할 수 있으므로, 하나만 선택하더라도 항상 리스트를 반환합니다. [0]은 그 리스트에서 항목을 꺼냅니다.

그런 다음 각 종류는 서로 다른 Rect가 됩니다. 보석은 별도의 리스트인 gems에 넣습니다. 보석에 닿으면 좋은 일이기 때문입니다. 시작 코드에는 이미 보석을 움직이고, 그리고, 잡는 기능이 있지만, 아직 아무것도 떨어뜨리지는 않습니다.

challenge icon

챌린지

중급
  1. pick_kind()를 작성하세요. 가중치 5, 3, 2로 각각 "small", "big" 또는 "gem"을 반환합니다.
  2. SPAWN 이벤트가 발생하면 선택된 항목을 떨어뜨리세요. 작은 바위는 24×24, 큰 바위는 40×40(x는 0~440, y는 -40), 보석은 20×20(x는 0~460, y는 -20)이며, 보석은 gems에 추가합니다.

직접 해보기

import random
import pygame

pygame.init()
screen = pygame.display.set_mode((480, 360))
pygame.display.set_caption("Meteor Dodge")
clock = pygame.time.Clock()
heart = pygame.image.load("assets/images/heart.png").convert_alpha()
heart_empty = pygame.image.load("assets/images/heart_empty.png").convert_alpha()
font = pygame.font.Font("assets/fonts/pixel.ttf", 24)
big_font = pygame.font.Font("assets/fonts/pixel.ttf", 48)
WHITE = (240, 240, 240)
PLATE = (60, 60, 90)
POINT = pygame.USEREVENT + 1
SPAWN = pygame.USEREVENT + 2


def reset():
    global player, rocks, gems, score, lives
    player = pygame.Rect(220, 300, 40, 40)
    rocks = []
    gems = []
    score = 0
    lives = 3
    pygame.time.set_timer(POINT, 1000)
    pygame.time.set_timer(SPAWN, 500)


def draw_menu():
    screen.fill((30, 30, 46))
    title = big_font.render("Meteor Dodge", True, WHITE)
    title_rect = title.get_rect(center=(240, 140))
    pygame.draw.rect(screen, PLATE, title_rect.inflate(24, 12))
    screen.blit(title, title_rect)
    hint = font.render("Press Enter to play", True, WHITE)
    screen.blit(hint, hint.get_rect(center=(240, 200)))


def draw_game():
    screen.fill((30, 30, 46))
    pygame.draw.rect(screen, (255, 196, 0), player)
    for rock in rocks:
        pygame.draw.rect(screen, (235, 80, 80), rock)
    for gem in gems:
        pygame.draw.rect(screen, (90, 220, 120), gem)
    score_text = font.render(f"{score:05}", True, WHITE)
    score_rect = score_text.get_rect(topright=(470, 10))
    pygame.draw.rect(screen, PLATE, score_rect.inflate(12, 8))
    screen.blit(score_text, score_rect)
    for i in range(3):
        if i < lives:
            screen.blit(heart, (10 + i * 40, 10))
        else:
            screen.blit(heart_empty, (10 + i * 40, 10))


def play_frame():
    global rocks, gems, score, lives, state
    keys = pygame.key.get_pressed()
    if keys[pygame.K_LEFT]:
        player.x -= 6
    if keys[pygame.K_RIGHT]:
        player.x += 6
    player.clamp_ip(screen.get_rect())

    kept = []
    for rock in rocks:
        rock.y += 3
        if rock.colliderect(player):
            lives -= 1
        elif rock.top <= 360:
            kept.append(rock)
    rocks = kept
    kept = []
    for gem in gems:
        gem.y += 3
        if gem.colliderect(player):
            score += 5
        elif gem.top <= 360:
            kept.append(gem)
    gems = kept
    if lives <= 0:
        state = "over"
    draw_game()


def draw_over():
    draw_game()
    message = big_font.render("Game over", True, WHITE)
    screen.blit(message, message.get_rect(center=(240, 160)))
    hint = font.render("Press R to play again", True, WHITE)
    screen.blit(hint, hint.get_rect(center=(240, 200)))


reset()
state = "menu"

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == POINT and state == "play":
            score += 1
        if event.type == SPAWN and state == "play":
            rocks.append(pygame.Rect(random.randint(0, 456), -24, 24, 24))
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN and state == "menu":
                reset()
                state = "play"
            elif event.key == pygame.K_r and state == "over":
                reset()
                state = "play"

    if state == "menu":
        draw_menu()
    elif state == "play":
        play_frame()
    else:
        draw_over()
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

게임 규칙의 모든 레슨

직접 연습해 보세요: Pygame 플레이그라운드