Menu
CoddyTech

حدّ أدنى للفجوة

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

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

interval = 1000 - seconds * 60

بعد 10 ثوانٍ، يصبح الفاصل 400 ms، وبعد 17 ثانية يصبح أقل من 0، ويتوقف المؤقّت إذا كان الفاصل 0 أو أقل. تحتاج اللعبة إلى حد أدنى: أصغر فاصل لا يقل عنه أبدًا. تعطي max القيمة الأكبر بين أعدادها، لذا فهي لا تسمح أبدًا للفاصل بأن يقل عن العدد الأول:

interval = max(400, 1000 - seconds * 60)

يحتفظ المؤقّت بالفاصل الزمني الذي بدأ به. لتغييره، ابدأه مرة أخرى: في كل مرة تسقط فيها صخرة، احسب الفاصل الجديد واستدعِ set_timer به. تسقط الصخرة التالية بعد ذلك الفاصل.

if event.type == SPAWN and state == "play":
    rocks.append(...)
    seconds = (pygame.time.get_ticks() - start_time) // 1000
    interval = max(400, 1000 - seconds * 60)
    pygame.time.set_timer(SPAWN, interval)
challenge icon

التحدي

متوسط
  1. في reset()، ابدأ مؤقّت SPAWN عند 1000 ms.
  2. في كل مرة يسقط فيها نيزك، اضبط interval = max(400, 1000 - seconds * 60) وابدأ مؤقّت SPAWN مرة أخرى باستخدامه.

جرّب بنفسك

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, score, lives, start_time
    player = pygame.Rect(220, 300, 40, 40)
    rocks = []
    score = 0
    lives = 3
    start_time = pygame.time.get_ticks()
    pygame.time.set_timer(POINT, 1000)
    pygame.time.set_timer(SPAWN, 800)


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)
    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, 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())

    seconds = (pygame.time.get_ticks() - start_time) // 1000
    speed = 3 + seconds // 5
    kept = []
    for rock in rocks:
        rock.y += speed
        if rock.colliderect(player):
            lives -= 1
        elif rock.top <= 360:
            kept.append(rock)
    rocks = 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