Menu
CoddyTech

Từng trục một

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

Mã va chạm của bạn di chuyển rồi xử lý va chạm theo trục x, sau đó di chuyển rồi xử lý va chạm theo trục y. Đoạn khởi đầu này di chuyển theo cả hai trục rồi chỉ xử lý va chạm một lần. Nhấn Play rồi đi sang phải: nhân vật bị giật lùi dọc theo mặt đất.

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

Khi đứng trên mặt đất, trọng lực đẩy nhân vật xuống đất 1 pixel mỗi khung hình. Nếu di chuyển sang phải trong cùng khung hình, nhân vật chồng lên ô đất bên dưới, và code không thể biết nguyên nhân. Code thấy dx > 0 nên đặt cạnh phải của nhân vật ở cạnh trái của ô đó, khiến nhân vật giật lùi dọc theo mặt đất.

Một lần chồng lấn, hai chuyển động, không có cách nào biết chuyển động nào gây ra. Khi chỉ di chuyển theo từng trục một, mỗi lần chồng lấn chỉ có một nguyên nhân. Sau khi di chuyển theo trục x, nhân vật chưa chạm vào đất, nên chỉ có tường thật mới chặn được nhân vật. Sau khi di chuyển theo trục y, lần chồng lấn chỉ có thể do chuyển động theo trục y gây ra.

challenge icon

Thử thách

Trung bình

Sửa lỗi giật bằng cách xử lý từng trục một.

  1. Tách chuyển động: player.x += dx và một vòng lặp qua walls chỉ chỉnh trục x.
  2. Sau đó xử lý trọng lực, player.y += vel_y, on_ground = False và một vòng lặp thứ hai chỉ chỉnh trục y.

Nhấn “Chơi” rồi đi sang phải: nhân vật di chuyển mượt mà dọc theo mặt sàn. 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("One Axis at a Time")
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)

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
    vel_y = min(vel_y + GRAVITY, MAX_FALL)
    player.y += vel_y
    on_ground = False
    for wall in walls:
        if player.colliderect(wall):
            if dx > 0:
                player.right = wall.left
            elif dx < 0:
                player.left = wall.right
            if vel_y > 0:
                player.bottom = wall.top
                on_ground = True
            elif vel_y < 0:
                player.top = wall.bottom
            vel_y = 0

    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)
    screen.blit(player_img, player)
    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