Menu
CoddyTech

صخرة كل 800 ms

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

لعبة إسقاط صخرة واحدة في كل مرة لعبة هادئة. باستخدام مؤقّت، يمكن أن تسقط صخرة جديدة كل 800 ms بينما لا تزال الصخور الأخرى تسقط. احتفظ بالصخور في قائمة:

SPAWN = pygame.USEREVENT + 2
...
if event.type == SPAWN and state == "play":
    rocks.append(pygame.Rect(random.randint(0, 456), -24, 24, 24))

لكل حدث رقمه الخاص، لذا فإن SPAWN هو USEREVENT + 2، أي رقم واحد بعد POINT. ابدأ مؤقّته بجوار المؤقّت الآخر، واجعل rocks = [] في reset().

في كل إطار، حرّك جميع الصخور، ثم احتفظ فقط بتلك التي لا تزال داخل النافذة:

for rock in rocks:
    rock.y += 4
rocks = [rock for rock in rocks if rock.top <= 360]

من دون هذا السطر الثاني، ستبقى كل صخرة سقطت في القائمة. ستحرّك اللعبة آلاف الصخور التي لا يستطيع أحد رؤيتها وتختبرها، وستتباطأ كلما طالت مدة لعبك.

يصبح الرسم واختبار الاصطدام أيضًا حلقتين على rocks.

challenge icon

التحدي

متوسط
  1. استبدل rock بقائمة اسمها rocks، واجعلها فارغة في reset().
  2. أضف SPAWN = pygame.USEREVENT + 2 ومؤقّتًا له كل 800 ms، وابدأه في reset().
  3. عند حدوث SPAWN أثناء اللعب، أضف صخرة أبعادها 24 في 24 عند y -24، مع قيمة x عشوائية من 0 إلى 456.
  4. في كل إطار، حرّك كل صخرة إلى الأسفل بمقدار 4، واحذف الصخور التي تجاوز أعلاها 360، وارسمها جميعًا واختبر كل واحدة منها للتحقق من حدوث اصطدام.

جرّب بنفسك

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


def reset():
    global player, rock, score, lives, hit_time
    player = pygame.Rect(220, 300, 40, 40)
    rock = pygame.Rect(random.randint(0, 456), -24, 24, 24)
    score = 0
    lives = 3
    hit_time = -2000
    pygame.time.set_timer(POINT, 1000)


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))
    safe = pygame.time.get_ticks() - hit_time < 1000
    if not safe or pygame.time.get_ticks() // 100 % 2 == 0:
        pygame.draw.rect(screen, (255, 196, 0), player)
    pygame.draw.rect(screen, (235, 80, 80), rock)
    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 score, lives, state, hit_time
    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())

    rock.y += 4
    if rock.top > 360:
        rock.y = -24
        rock.x = random.randint(0, 456)
    now = pygame.time.get_ticks()
    if rock.colliderect(player) and now - hit_time > 1000:
        hit_time = now
        lives -= 1
        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 == pygame.KEYDOWN:
            if event.key == pygame.K_RETURN and state == "menu":
                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()

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

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