Menu
CoddyTech

القائمة وأفضل نتيجة

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

تُستمد قواعد Sky Runner من هذا القسم: قائمة، وبدء لعبة جديدة بالضغط على مفتاح، وأفضل نتيجة.

تنشئ reset() عدّاءً جديدًا ومجموعة فارغة، وتعيد النتيجة والسرعة إلى قيمتيهما الابتدائيتين، ثم تعيد تشغيل المؤقتين. تبدأ اللعبة في الحالة "menu"، وتُضبط قيمة best مرة واحدة خارج reset().

يؤدي مفتاح المسافة الآن ثلاث مهام، واحدة لكل حالة:

if state == "play":
    runner.jump()
elif state == "menu":
    reset()
    state = "play"
elif pygame.time.get_ticks() - over_time > 500:
    reset()
    state = "play"

مفتاح المسافة هو مفتاح القفز، لذا غالبًا ما يضغط عليه اللاعب لحظة وقوع الاصطدام. ومن دون انتظار، ستُعاد اللعبة قبل أن يرى اللاعب النتيجة. احفظ وقت الاصطدام في over_time، ولا تقبل الضغط على مفتاح المسافة في شاشة انتهاء اللعبة إلا بعد 500 ms.

ترسم الدالة الصغيرة show(text, text_font, y) سطرًا يتوسط عند x 320، لأن القائمة وشاشة انتهاء اللعبة تحتاجان إلى خمسة أسطر.

challenge icon

التحدي

صعب
  1. اكتب reset() للعدّاء والمجموعة وspeed وscore والمؤقّتين؛ واستدعِها قبل الحلقة، واضبط state = "menu" وbest = 0 وover_time = 0.
  2. في القائمة، اعرض Sky Runner (بحجم 48) وPress Space to start؛ يبدأ اللعب بالضغط على Space أو Up.
  3. عند الاصطدام، حدّث best واضبط over_time.
  4. في شاشة انتهاء اللعبة، اعرض Game over وBest: 00120 (خمسة أرقام) وPress Space to play again؛ يبدأ الضغط على Space لعبة جديدة، ولكن بعد مرور 500 ms على الاصطدام فقط.
  5. اعرض النتيجة أثناء اللعب وبعد انتهاء اللعبة فقط.

جرّب بنفسك

import random
import pygame

pygame.init()
screen = pygame.display.set_mode((640, 320))
pygame.display.set_caption("Sky Runner")
clock = pygame.time.Clock()
font = pygame.font.Font("assets/fonts/pixel.ttf", 32)

SKY = (140, 200, 240)
GROUND = (90, 160, 80)
GROUND_Y = 260
GRAVITY = 1
SPAWN = pygame.USEREVENT + 1
POINT = pygame.USEREVENT + 2


class Runner(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.frames = [
            pygame.image.load("assets/images/player.png").convert_alpha(),
            pygame.image.load("assets/images/player_walk.png").convert_alpha(),
        ]
        self.image = self.frames[0]
        self.step = 0
        self.rect = self.image.get_rect(midbottom=(100, GROUND_Y))
        self.vy = 0

    def jump(self):
        if self.rect.bottom == GROUND_Y:
            self.vy = -16

    def update(self):
        self.vy += GRAVITY
        self.rect.y += self.vy
        if self.rect.bottom >= GROUND_Y:
            self.rect.bottom = GROUND_Y
            self.vy = 0
        if self.rect.bottom == GROUND_Y:
            self.step += 1
            self.image = self.frames[self.step // 8 % 2]
        else:
            self.image = self.frames[1]


class Obstacle(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = random.choice(obstacle_images)
        self.rect = self.image.get_rect(midbottom=(664, GROUND_Y))

    def update(self):
        self.rect.x -= speed
        if self.rect.right < 0:
            self.kill()


obstacle_images = [
    pygame.image.load("assets/images/spike_ball.png").convert_alpha(),
    pygame.image.load("assets/images/blob.png").convert_alpha(),
]
runner = Runner()
obstacles = pygame.sprite.Group()
speed = 6
pygame.time.set_timer(SPAWN, 1500)
pygame.time.set_timer(POINT, 100)
score = 0
state = "play"

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN and event.key in (pygame.K_SPACE, pygame.K_UP):
            if state == "play":
                runner.jump()
        if event.type == SPAWN and state == "play":
            obstacles.add(Obstacle())
            pygame.time.set_timer(SPAWN, random.randint(1000, 2000))
        if event.type == POINT and state == "play":
            score += 1

    if state == "play":
        speed = min(14, 6 + score // 100)
        runner.update()
        obstacles.update()
        if pygame.sprite.spritecollideany(runner, obstacles, pygame.sprite.collide_mask):
            state = "over"

    screen.fill(SKY)
    pygame.draw.rect(screen, GROUND, (0, GROUND_Y, 640, 60))
    screen.blit(runner.image, runner.rect)
    obstacles.draw(screen)
    score_text = font.render(f"{score:05}", True, (40, 50, 80))
    screen.blit(score_text, score_text.get_rect(topright=(630, 10)))
    if state == "over":
        message = font.render("Game over", True, (40, 50, 80))
        screen.blit(message, message.get_rect(center=(320, 140)))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
quiz iconاختبر نفسك

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

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

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