Menu
CoddyTech

מפלצת

שיעור 5 מתוך 20 בקורס פרויקט Pygame: להגן על המצודה של Coddy.

מפלצת הולכת ישר לעבר האביר. pygame.Vector2 מכיל ערכי x ו־y שיכולים להיות מספרים עשרוניים, ואפשר לחבר ולחסר ערכי Vector2 שלמים:

step = pygame.Vector2(target) - self.pos
if step.length() > 0:
    self.pos += step.normalize() * self.speed
self.rect.center = self.pos

step מצביע מהמפלצת אל האביר, רחוק ככל שיהיה. normalize() שומר על הכיוון שלו, אבל הופך את אורכו לפיקסל אחד, כך שכפול self.speed המפלצת נעה בדיוק במהירות שלה בכל פריים, לכל כיוון. Rect מכיל מספרים שלמים בלבד, לכן המיקום המדויק נשמר ב־self.pos וה־Rect עוקב אחריו.

כל סוג של מפלצת מקבל שורה אחת בטבלה, כך שהוספת סוג בהמשך דורשת שורה אחת:

MONSTERS = {
    "slime": {"image": load("slime.png"), "speed": 1.0},
}

מפלצות מגיעות מחוץ לחלון. edge_point() בוחר צד באמצעות random.randint(0, 3) ומיקום אקראי לאורכו, במרחק 24 פיקסלים ממנו.

challenge icon

אתגר

בינוני
  1. import random והוסיפו את טבלת MONSTERS עם הרפש.
  2. כתבו את edge_point(): צד 0 הוא (random.randint(0, WIDTH), -24), צד 1 הוא אותו הדבר ב־HEIGHT + 24, צד 2 הוא (-24, random.randint(0, HEIGHT)) וצד 3 הוא אותו הדבר ב־WIDTH + 24.
  3. כתבו את class Monster(pygame.sprite.Sprite): __init__(self, kind, pos) שומר את kind, את image ואת speed מהטבלה, את pos כ־Vector2 ואת rect בגודל 28 על 28 שממורכז עליו; update(self, target) נע כפי שתואר לעיל.
  4. בתוך Night: self.monsters = pygame.sprite.Group() עם רפש אחד ב־edge_point(); update קוראת ל־self.monsters.update(self.knight.rect.center). ציירו כל מפלצת באמצעות draw לפני האביר.

נסו בעצמכם

import pygame

WIDTH, HEIGHT = 720, 480
TILE = 48
NIGHT = 60 * 60 * 4
WHITE = (240, 240, 240)

pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Hold the Keep")
clock = pygame.time.Clock()
font = pygame.font.Font("assets/fonts/pixel.ttf", 24)


def load(name):
    return pygame.image.load("assets/images/" + name).convert_alpha()


floor_img = load("floor.png")
wall_img = load("wall.png")
knight_img = load("knight.png")

courtyard = pygame.Rect(TILE, TILE, WIDTH - 2 * TILE, HEIGHT - 2 * TILE)
background = pygame.Surface((WIDTH, HEIGHT))
for row in range(HEIGHT // TILE):
    for col in range(WIDTH // TILE):
        x = col * TILE
        y = row * TILE
        if courtyard.collidepoint(x, y):
            background.blit(floor_img, (x, y))
        else:
            background.blit(wall_img, (x, y))


def draw(image, center):
    screen.blit(image, image.get_rect(center=center))


def clock_text(frames):
    seconds = frames // 60
    return f"{seconds // 60}:{seconds % 60:02d}"


class Knight(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = knight_img
        self.rect = pygame.Rect(0, 0, 24, 24)
        self.rect.center = courtyard.center
        self.speed = 3

    def move(self, keys):
        if keys[pygame.K_LEFT]:
            self.rect.x -= self.speed
        if keys[pygame.K_RIGHT]:
            self.rect.x += self.speed
        if keys[pygame.K_UP]:
            self.rect.y -= self.speed
        if keys[pygame.K_DOWN]:
            self.rect.y += self.speed
        self.rect.clamp_ip(courtyard)


class Night:
    def __init__(self):
        self.knight = Knight()
        self.time = 0

    def update(self, keys):
        self.time += 1
        self.knight.move(keys)


night = Night()

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    night.update(pygame.key.get_pressed())

    screen.blit(background, (0, 0))
    knight = night.knight
    draw(knight.image, knight.rect.center)
    left = font.render(clock_text(max(0, NIGHT - night.time)) + " until dawn", True, WHITE)
    screen.blit(left, left.get_rect(center=(WIDTH // 2, 24)))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

כל השיעורים ביחידה פרויקט Pygame: להגן על המצודה

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