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

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

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