Bolzen
Lektion 9 von 20 im Kurs Pygame-Projekt: Verteidige den Bergfried von Coddy.
Die erste Waffe des Ritters feuert von selbst: Alle 30 Frames fliegt ein magischer Bolzen auf das nächstgelegene Monster zu. Um das nächstgelegene zu finden, behältst du das bisher beste im Blick und vergleichst jedes Monster damit:
def nearest(self):
point = pygame.Vector2(self.knight.rect.center)
best = None
for monster in self.monsters:
if best is None or point.distance_to(monster.pos) < point.distance_to(best.pos):
best = monster
return bestEin Bolzen ist ein Sprite, das in einer geraden Linie fliegt. Er berechnet seine Richtung einmal, wenn er erstellt wird, wie der Schritt eines Monsters, und behält sie bei: velocity = step.normalize() * 7. Wenn er den Bildschirm verlässt, entfernt er sich mit self.kill() selbst. Dadurch wird ein Sprite aus jeder Group entfernt, zu der es gehört.
Ein Timer zählt die Frames seit dem letzten Bolzen. Wenn er den Wert bolt_wait des Ritters erreicht hat und ein Ziel vorhanden ist, feuert der Ritter und setzt den Timer wieder auf 0. Diese Wartezeit zwischen den Einsätzen nennt man Abklingzeit.
Aufgabe
Mittel- Füge
BOLT = (120, 200, 255)hinzu. Gib dem Ritterbolt_wait = 30undbolt_damage = 1. - Schreibe
class Bolt(pygame.sprite.Sprite):__init__(self, start, target, damage)speichertpos,velocity(7 Pixel pro Frame in Richtung des Ziels),damageund ein 10 mal 10 großesrect;updatebewegt es und entfernt es, sobald sich sein rect außerhalb des Bildschirms befindet. - In
Night:self.bolts = pygame.sprite.Group(),self.bolt_timer = 0,nearest()undshoot(). Rufe inupdateself.bolts.update()auf, nachdem sich die Monster bewegt haben, und dannself.shoot(). - Zeichne jeden Bolzen zuletzt im Innenhof:
pygame.draw.circle(screen, BOLT, bolt.rect.center, 5).
Probier es selbst
import random
import pygame
WIDTH, HEIGHT = 720, 480
TILE = 48
NIGHT = 60 * 60 * 4
WAVES = [
["slime"],
["slime", "bat"],
["slime", "bat", "spider"],
["bat", "spider"],
["slime", "spider", "ghost"],
["bat", "spider", "ghost"],
["spider", "ghost"],
["bat", "ghost"],
]
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)
big_font = pygame.font.Font("assets/fonts/pixel.ttf", 48)
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")
heart_img = load("heart.png")
empty_img = load("heart_empty.png")
MONSTERS = {
"slime": {"image": load("slime.png"), "speed": 1.0},
"bat": {"image": load("dungeon_bat.png"), "speed": 1.6},
"spider": {"image": load("spider.png"), "speed": 1.2},
"ghost": {"image": load("ghost.png"), "speed": 0.8},
}
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}"
def edge_point():
side = random.randint(0, 3)
if side == 0:
return (random.randint(0, WIDTH), -24)
if side == 1:
return (random.randint(0, WIDTH), HEIGHT + 24)
if side == 2:
return (-24, random.randint(0, HEIGHT))
return (WIDTH + 24, random.randint(0, HEIGHT))
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
self.hearts = 5
self.max_hearts = 5
self.hurt = 0
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 Monster(pygame.sprite.Sprite):
def __init__(self, kind, pos):
super().__init__()
self.kind = kind
self.image = MONSTERS[kind]["image"]
self.speed = MONSTERS[kind]["speed"]
self.pos = pygame.Vector2(pos)
self.rect = pygame.Rect(0, 0, 28, 28)
self.rect.center = self.pos
def update(self, target):
step = pygame.Vector2(target) - self.pos
if step.length() > 0:
self.pos += step.normalize() * self.speed
self.rect.center = self.pos
class Night:
def __init__(self):
self.knight = Knight()
self.time = 0
self.monsters = pygame.sprite.Group()
self.state = "playing"
def wave(self):
return min(len(WAVES), self.time * len(WAVES) // NIGHT + 1)
def spawn(self):
wave = self.wave()
if self.time % max(16, 46 - wave * 5) == 0:
kind = random.choice(WAVES[wave - 1])
self.monsters.add(Monster(kind, edge_point()))
def take_hits(self):
knight = self.knight
if knight.hurt > 0:
knight.hurt -= 1
elif pygame.sprite.spritecollideany(knight, self.monsters):
knight.hearts -= 1
knight.hurt = 75
if knight.hearts <= 0:
self.state = "lost"
def update(self, keys):
self.time += 1
self.knight.move(keys)
self.spawn()
self.monsters.update(self.knight.rect.center)
self.take_hits()
night = Night()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if night.state == "playing":
night.update(pygame.key.get_pressed())
screen.blit(background, (0, 0))
knight = night.knight
for monster in night.monsters:
draw(monster.image, monster.rect.center)
if knight.hurt % 10 < 5:
draw(knight.image, knight.rect.center)
for i in range(knight.max_hearts):
if i < knight.hearts:
screen.blit(heart_img, (8 + i * 34, 6))
else:
screen.blit(empty_img, (8 + i * 34, 6))
left = font.render(clock_text(max(0, NIGHT - night.time)) + " until dawn", True, WHITE)
screen.blit(left, left.get_rect(center=(WIDTH // 2, 24)))
if night.state == "lost":
message = big_font.render("The keep has fallen", True, WHITE)
screen.blit(message, message.get_rect(center=(WIDTH // 2, 200)))
pygame.display.flip()
clock.tick(60)
pygame.quit()
Alle Lektionen in Pygame-Projekt: Verteidige den Bergfried
Übe selbstständig: Pygame-Playground