Menu
CoddyTech

Thị sai

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

Nhìn ra ngoài cửa sổ một đoàn tàu đang chạy: hàng rào vụt qua, còn những ngọn đồi ở xa hầu như không chuyển động. Vẽ những vật ở xa chậm hơn những vật ở gần sẽ tạo cảm giác chiều sâu cho trò chơi 2D. Hiệu ứng này gọi là thị sai.

Vẽ nền mây với tốc độ bằng một phần ba chuyển động của camera. Hình nền lặp lại sau mỗi 72 pixel, nên chỉ cần quan tâm đến camera_x // 3 % 72: khi hình nền đã trượt hết một lượt, nó lại trông như cũ.

shift = camera_x // 3 % 72
for y in range(0, 360, 72):
    for x in range(-shift, WIDTH, 72):
        screen.blit(background, (x, y))

Vòng lặp x bắt đầu tại -shift, bên trái cửa sổ, để bản sao đầu tiên che kín mép trái khi nó trượt ra ngoài. Các ô nền và người chơi vẫn dùng toàn bộ giá trị camera_x: chúng ở gần.

challenge icon

Thử thách

Trung bình

Cuộn background chậm hơn màn chơi.

  1. Thay vì dùng screen.fill, hãy vẽ background (đã được tải trong code khởi đầu) theo lưới 72 pixel.
  2. Ở mỗi khung hình, đặt shift = camera_x // 3 % 72 và bắt đầu mỗi hàng bản sao tại x = -shift, mỗi bước 72 pixel cho đến WIDTH.
  3. Vẽ các ô và người chơi sau đó.

Nhấn Chơi rồi đi sang phải: những đám mây sẽ trễ lại phía sau mặt đất. Sau đó nhấn Kiểm tra.

Tự làm thử

import pygame

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

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

pygame.init()
screen = pygame.display.set_mode((WIDTH, 360))
pygame.display.set_caption("Parallax")
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()

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)

level_width = len(LEVEL[0]) * TILE
player = player_img.get_rect(midbottom=start)
vel_y = 0
on_ground = False
camera_x = 0

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

    camera_x = player.centerx - WIDTH // 2
    camera_x = max(0, min(camera_x, level_width - WIDTH))

    screen.fill((140, 200, 240))
    for wall in walls:
        screen.blit(block_img, (wall.x - camera_x, wall.y))
    screen.blit(player_img, (player.x - camera_x, player.y))
    pygame.display.flip()
    clock.tick(60)

pygame.quit()
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

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

Tự luyện tập: Playground Pygame