Menu
CoddyTech

اختيارات موزونة

جزء من قسم قواعد اللعبة في رحلة Pygame على Coddy. الدرس 18 من 29.

تكون اللعبة أكثر تشويقًا عندما لا يكون كل ما يسقط متماثلًا. هنا، نصف الأشياء الساقطة صخور صغيرة، وبعضها صخور كبيرة، والقليل منها جواهر تساوي 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(): فهي تُعيد "small" أو "big" أو "gem" بأوزان 5 و3 و2.
  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اختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس قواعد اللعبة

تدرّب بنفسك: Playground لـ Pygame