الجرعات الحمراء
جزء من قسم المشاريع في رحلة Pygame على Coddy. الدرس 27 من 31.
كل H على الخريطة هو جرعة حمراء. المرور فوقها يعيد قلبًا، ولكن فقط عندما يكون الفارس قد فقد قلبًا، لذا تبقى الجرعة على الأرض إلى أن يحتاج إليها.
الجرعات قائمة من Rects. knight.rect.collidelist(room.potions) يعطي فهرس الجرعة التي يلمسها، أو -1. room.potions.pop(index) يزيل تلك الجرعة من القائمة، فلا تُرسم مجددًا ولا تُشرب مرة أخرى.
index = knight.rect.collidelist(room.potions)
if index != -1 and knight.hearts < 3:
knight.hearts += 1
room.potions.pop(index)إزالة العناصر من قائمة أثناء مرور حلقة for عليها يؤدي إلى تخطي العنصر الذي يلي كل إزالة. وباستخدام الفهرس، لا توجد حلقة قد تسبب التباسًا.
ضع الجرعات بعيدًا عن أقصر طريق عبر الغرفة. عندها يجد اللاعب الذي لديه عدد قليل من القلوب سببًا للاستكشاف، وللمخاطرة بسلوك طريق جانبي يمر بجوار وحش الوحل.
التحدي
متوسط- في
Room، عيّنself.potions = []؛ ويضيفHالقيمةcell_rect(col, row). - بعد اختبار التعرّض للإصابة، أضف الشيفرة الخاصة بالجرعة المذكورة أعلاه.
- ارسم كل جرعة باستخدام
draw(potion_img, potion)قبل الأعداء.
جرّب بنفسك
import random
import pygame
TILE = 48
MAP = [
"##############",
"#............#",
"#.P......K...#",
"#............#",
"#....##......D",
"#....##......#",
"##.#######.###",
"#............#",
"#...S....H...#",
"##############",
]
pygame.init()
screen = pygame.display.set_mode((672, 480))
pygame.display.set_caption("Rune Keep")
clock = pygame.time.Clock()
font = pygame.font.Font("assets/fonts/pixel.ttf", 32)
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")
door_img = load("dungeon_door.png")
key_img = load("key.png")
slime_img = load("slime.png")
spider_img = load("spider.png")
heart_img = load("heart.png")
empty_img = load("heart_empty.png")
sword_img = load("sword.png")
potion_img = load("potion_red.png")
ANGLES = {(0, -1): 0, (-1, 0): 90, (0, 1): 180, (1, 0): -90}
def draw(image, rect):
screen.blit(image, image.get_rect(center=rect.center))
def cell_rect(col, row):
rect = pygame.Rect(0, 0, 32, 32)
rect.center = (col * TILE + 24, row * TILE + 24)
return rect
class Enemy(pygame.sprite.Sprite):
def __init__(self, image, rect, dx, dy):
super().__init__()
self.image = image
self.rect = rect
self.dx = dx
self.dy = dy
def update(self, blocks):
self.rect.x += self.dx
self.rect.y += self.dy
if self.rect.collidelist(blocks) != -1:
self.rect.x -= self.dx
self.rect.y -= self.dy
self.dx = -self.dx
self.dy = -self.dy
class Room:
def __init__(self, lines):
self.walls = []
self.enemies = pygame.sprite.Group()
self.start = (72, 72)
self.has_key = False
self.door_open = False
for row, line in enumerate(lines):
for col, char in enumerate(line):
if char == "#":
self.walls.append(pygame.Rect(col * TILE, row * TILE, TILE, TILE))
elif char == "P":
self.start = cell_rect(col, row).center
elif char == "K":
self.key = cell_rect(col, row)
elif char == "D":
self.door = pygame.Rect(col * TILE, row * TILE, TILE, TILE)
elif char == "S":
speed = random.choice([-2, 2])
self.enemies.add(Enemy(slime_img, cell_rect(col, row), speed, 0))
def blocks(self):
if self.door_open:
return self.walls
return self.walls + [self.door]
class Knight(pygame.sprite.Sprite):
def __init__(self, center):
super().__init__()
self.image = knight_img
self.rect = pygame.Rect(0, 0, 32, 32)
self.rect.center = center
self.hearts = 3
self.hurt = 0
self.facing = (1, 0)
self.swing = 0
def move(self, dx, dy, blocks):
self.rect.x += dx
for block in blocks:
if self.rect.colliderect(block):
if dx > 0:
self.rect.right = block.left
elif dx < 0:
self.rect.left = block.right
self.rect.y += dy
for block in blocks:
if self.rect.colliderect(block):
if dy > 0:
self.rect.bottom = block.top
elif dy < 0:
self.rect.top = block.bottom
def sword(self):
box = pygame.Rect(0, 0, 36, 36)
box.centerx = self.rect.centerx + self.facing[0] * 34
box.centery = self.rect.centery + self.facing[1] * 34
return box
room = Room(MAP)
knight = Knight(room.start)
state = "playing"
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and state == "playing" and knight.swing == 0:
knight.swing = 12
if state == "playing":
keys = pygame.key.get_pressed()
dx = 0
dy = 0
if keys[pygame.K_LEFT]:
dx = -3
knight.facing = (-1, 0)
if keys[pygame.K_RIGHT]:
dx = 3
knight.facing = (1, 0)
if keys[pygame.K_UP]:
dy = -3
knight.facing = (0, -1)
if keys[pygame.K_DOWN]:
dy = 3
knight.facing = (0, 1)
knight.move(dx, dy, room.blocks())
knight.rect.clamp_ip(screen.get_rect())
if not room.has_key and knight.rect.colliderect(room.key):
room.has_key = True
if room.has_key and knight.rect.inflate(8, 8).colliderect(room.door):
room.door_open = True
room.enemies.update(room.walls + [room.door])
if knight.swing > 0:
knight.swing -= 1
for enemy in room.enemies:
if enemy.rect.colliderect(knight.sword()):
enemy.kill()
if knight.hurt > 0:
knight.hurt -= 1
elif pygame.sprite.spritecollide(knight, room.enemies, False):
knight.hearts -= 1
knight.hurt = 60
if knight.hearts == 0:
state = "lost"
for row in range(10):
for col in range(14):
screen.blit(floor_img, (col * TILE, row * TILE))
for wall in room.walls:
screen.blit(wall_img, wall)
if not room.door_open:
screen.blit(door_img, room.door)
if not room.has_key:
draw(key_img, room.key)
for enemy in room.enemies:
draw(enemy.image, enemy.rect)
if knight.hurt % 10 < 5:
draw(knight.image, knight.rect)
if knight.swing > 0:
blade = pygame.transform.rotate(sword_img, ANGLES[knight.facing])
draw(blade, knight.sword())
for i in range(3):
if i < knight.hearts:
screen.blit(heart_img, (8 + i * 40, 6))
else:
screen.blit(empty_img, (8 + i * 40, 6))
if room.has_key:
screen.blit(key_img, (136, 6))
if state == "lost":
message = font.render("Game over", True, (240, 240, 240))
screen.blit(message, message.get_rect(center=(336, 240)))
pygame.display.flip()
clock.tick(60)
pygame.quit()
جميع دروس المشاريع
تدرّب بنفسك: Playground لـ Pygame