Menu
CoddyTech

Một khối nhầy tuần tra

Thuộc phần Thế giới trò chơi trong hành trình Pygame của Coddy. Bài học 24 trên 36.

Hai blob đi sang phải rồi đi tiếp ra khoảng không. Một kẻ địch tuần tra đi qua đi lại: nó đổi hướng khi va vào tường hoặc đến mép nền. Lớp Blob lưu direction, 1 là sang phải và -1 là sang trái.

Sau mỗi bước, hãy kiểm tra điểm nằm cách chân trước của blob một pixel, ngang độ cao bàn chân. Nếu không có bức tường nào đỡ điểm đó, bước tiếp theo sẽ đi vào khoảng không:

if self.direction > 0:
    ahead = self.rect.right
else:
    ahead = self.rect.left - 1
hit_wall = any(self.rect.colliderect(wall) for wall in walls)
ground_ahead = any(wall.collidepoint(ahead, self.rect.bottom) for wall in walls)
if hit_wall or not ground_ahead:
    self.rect.x -= self.direction
    self.direction = -self.direction

any(...) là True khi có ít nhất một bức tường vượt qua bài kiểm tra. Khi đổi hướng, blob hoàn tác bước đi rồi đảo hướng. Rect của blob vừa với thân nó, kích thước 30 x 26, nhỏ hơn hình ảnh 48 pixel; hình ảnh được vẽ sao cho điểm giữa cạnh dưới trùng với điểm giữa cạnh dưới của Rect.

challenge icon

Thử thách

Khó

Khiến các blob quay đầu ở tường và mép vực.

  1. Trong Blob.update, sau bước di chuyển, đặt ahead thành self.rect.right khi di chuyển sang phải, nếu không thì đặt thành self.rect.left - 1.
  2. Kiểm tra xem blob có va chạm với tường hay không, và xem có tường nào chứa (ahead, self.rect.bottom) hay không.
  3. Nếu blob va vào tường hoặc không có mặt đất phía trước, hãy hoàn tác bước di chuyển và đảo ngược self.direction.

Nhấn Chơi: mỗi blob đi qua đi lại trên bục của mình. Sau đó nhấn Kiểm tra.

Tự làm thử

import pygame

TILE = 36
SPEED = 4
GRAVITY = 1
JUMP = -15
MAX_FALL = 12

LEVEL = [
    "................",
    "................",
    "................",
    "................",
    "..........####..",
    "................",
    "...####.........",
    "................",
    "#.P............#",
    "################",
]

pygame.init()
screen = pygame.display.set_mode((576, 360))
pygame.display.set_caption("Patrol")
clock = pygame.time.Clock()

background = pygame.image.load("assets/backgrounds/clouds.png").convert_alpha()
block_img = pygame.image.load("assets/images/block.png").convert_alpha()
player_img = pygame.image.load("assets/images/player.png").convert_alpha()
blob_img = pygame.image.load("assets/images/blob.png").convert_alpha()


class Blob(pygame.sprite.Sprite):
    def __init__(self, midbottom):
        super().__init__()
        self.image = blob_img
        self.rect = pygame.Rect(0, 0, 30, 26)
        self.rect.midbottom = midbottom
        self.direction = 1

    def update(self):
        self.rect.x += self.direction

walls = []
for row, line in enumerate(LEVEL):
    for col, char in enumerate(line):
        if char == "#":
            walls.append(pygame.Rect(col * TILE, row * TILE, TILE, TILE))
        elif char == "P":
            start = (col * TILE + TILE // 2, row * TILE + TILE)

blobs = pygame.sprite.Group(Blob((432, 144)), Blob((432, 324)))
player = player_img.get_rect(midbottom=start)
vel_y = 0
on_ground = False

running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN and event.key in (pygame.K_SPACE, pygame.K_UP):
            if on_ground:
                vel_y = JUMP
        elif event.type == pygame.KEYUP and event.key in (pygame.K_SPACE, pygame.K_UP):
            if vel_y < -4:
                vel_y = -4

    keys = pygame.key.get_pressed()
    dx = 0
    if keys[pygame.K_LEFT]:
        dx -= SPEED
    if keys[pygame.K_RIGHT]:
        dx += SPEED

    player.x += dx
    for wall in walls:
        if player.colliderect(wall):
            if dx > 0:
                player.right = wall.left
            elif dx < 0:
                player.left = wall.right

    vel_y = min(vel_y + GRAVITY, MAX_FALL)
    player.y += vel_y
    on_ground = False
    for wall in walls:
        if player.colliderect(wall):
            if vel_y > 0:
                player.bottom = wall.top
                on_ground = True
            elif vel_y < 0:
                player.top = wall.bottom
            vel_y = 0

    blobs.update()

    for y in range(0, 360, 72):
        for x in range(0, 576, 72):
            screen.blit(background, (x, y))
    for wall in walls:
        screen.blit(block_img, wall)
    for blob in blobs:
        screen.blit(blob.image, blob.image.get_rect(midbottom=blob.rect.midbottom))
    screen.blit(player_img, player)
    pygame.display.flip()
    clock.tick(60)

pygame.quit()

Tất cả bài học trong Thế giới trò chơi

Tự luyện tập: Playground Pygame