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 אלפיות השנייה.

הפונקציה הקטנה 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; לחיצה על מקש הרווח או על החץ למעלה מתחילה משחק.
  3. בעת התנגשות, עדכנו את best והגדירו את over_time.
  4. במסך סיום המשחק הציגו את Game over, את Best: 00120 (חמש ספרות) ואת Press Space to play again; לחיצה על מקש הרווח מתחילה משחק חדש, אך רק 500 אלפיות השנייה לאחר ההתנגשות.
  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בחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה חוקי המשחק

תרגלו בעצמכם: Pygame אונליין