שיקויים אדומים
חלק מהיחידה פרויקטים במסלול ה-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()
כל השיעורים ביחידה פרויקטים
תרגלו בעצמכם: Pygame אונליין