You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame玩家与Tile碰撞失效问题求助

Pygame角色与Tile碰撞失效问题解决

问题描述

学习Pygame时参照教程编写代码(非完全照搬),但出现玩家角色穿过Tile掉落的碰撞失效问题,教程代码可正常运行。期望角色能停留在Tile上方而非穿过。

错误原因

核心问题出在collsion_test函数中,return hit_col被错误地放在了for tile in tiles:循环内部,导致函数只检查第一个Tile后就立即返回结果,不会遍历所有Tile进行碰撞检测,大部分碰撞情况都无法被正确识别。

修正后的代码

把return hit_col移到for循环外面,确保遍历完所有Tile后再返回碰撞列表:

import sys

import pygame

clock = pygame.time.Clock()  # initiate clock
from pygame.locals import *

pygame.init()  # initiate pygame


WINDOW = (600, 400)
screen = pygame.display.set_mode(WINDOW, 0, 32)
pygame.display.set_caption("window")  # set window name
display = pygame.Surface(
    (300, 200)
)  # create display surface, basically makes another image, since all images are surfaces.. kinda
# you can render one surface onto anoher surface


player_image = pygame.image.load(
    "images\me1ns.png"
).convert_alpha()  # load player image, define player image as variable
player_y_momentum = 0

grass_image = pygame.image.load("images\grass.png").convert()
dirt_image = pygame.image.load("images\dirt.png").convert()
game_map = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2],
    [1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
]
TILE_SIZE = 16  # size of each tile


def collsion_test(rect, tiles):
    hit_col = []
    for tile in tiles:
        if rect.colliderect(tile):
            hit_col.append(tile)
    # 遍历所有Tile后再返回碰撞列表
    return hit_col
    # tile collision test, returns a list of tiles that collided with the rect


def move(
    rect, movement, tiles
):  # thing that's being moved, how it's moved, and what tiles it's colliding with
    global player_y_momentum
    rect.x += movement[0]  # move on the x axis
    collsions = collsion_test(
        rect, tiles
    )  # returns a list of tiles that collided with the rect
    for tile in collsions:
        if movement[0] > 0:
            rect.right = tile.left
        elif movement[0] < 0:
            rect.left = tile.right
        # if moving right, place on left of tile; if moving left, place on right of tile
    rect.y += movement[1]  # move on the y axis
    collsions = collsion_test(
        rect, tiles
    )  # returns a list of tiles that collided with the rect
    for tile in collsions:
        if movement[1] > 0:
            rect.bottom = tile.top
            # 碰撞到地面时重置y方向动量,防止持续下落
            player_y_momentum = 0
        elif movement[1] < 0:
            rect.top = tile.bottom
        # if moving up, place on bottom of tile; if moving down, place on top of tile
    return rect


# colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
GRAY = (128, 128, 128)
LBLUE = (146, 244, 255)

moving_right = False
moving_left = False
player_rect = pygame.Rect(50, 50, player_image.get_width(), player_image.get_height())
# getting the player rect(collision), x,y,w,h
# when defining a rect, use pygame.Rect(x,y,w,h), not use pygame.rect(x,y,w,h)
while True:
    display.fill(LBLUE)  # fills screen with color, put images after this
    tile_rects = []
    y = 0
    for row in game_map:
        x = 0
        for tile in row:
            if tile == 2:
                display.blit(grass_image, (x * TILE_SIZE, y * TILE_SIZE))
            if tile == 1:
                display.blit(dirt_image, (x * TILE_SIZE, y * TILE_SIZE))
            if tile != 0:
                tile_rects.append(
                    pygame.Rect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE)
                )
            x += 1
        y += 1
    # displays tile map, x and y are the location of the tile. We blit based on what the number is, tile is the individual number in map
    # multiply by tile size to get true full size.
    # we make a list of all the tiles in the map, then make a rect for each tile, base on the position of the tile, and ofc TILE_SIZE, TILE_SIZE

    # update player collision location
    # colliderect checks if something is colliding with something else

    # movement
    player_movement = [0, 0]
    if moving_right:
        player_movement[0] += 2
    if moving_left:
        player_movement[0] -= 2
    player_movement[1] += player_y_momentum
    player_y_momentum += 0.2
    if player_y_momentum > 3:
        player_y_momentum = 3

    player_rect = move(player_rect, player_movement, tile_rects)
    # moves the player, does collision testing, and returns the new rect.

    display.blit(player_image, (player_rect.x, player_rect.y))
    # puts player image on the screen, at specific location
    # by having it so that you make change, and then check, you can hold and still move

    for event in pygame.event.get():  # main loop for pygame events
        if event.type == QUIT:
            pygame.quit()
            print(tile_rects)
            sys.exit()
        if event.type == KEYDOWN:  # loop for key pressed down
            if event.key == K_RIGHT:
                moving_right = True
            if event.key == K_LEFT:
                moving_left = True
        if event.type == KEYUP:  # checks if key is pressed up
            if event.key == K_RIGHT:
                moving_right = False
            if event.key == K_LEFT:
                moving_left = False
    # changes the size of one thing to another
    screen.blit(pygame.transform.scale(display, (600, 400)), (0, 0))
    # what to blit and where
    pygame.display.update()  # updates the screen
    clock.tick(60)  # 60 fps, has game running at 60 fps

额外优化说明

除了修正碰撞检测的遍历问题,还添加了两个关键优化:

  1. 在move函数中声明global player_y_momentum,确保可以修改全局的动量变量
  2. 当角色向下碰撞到Tile时,重置player_y_momentum为0,避免动量持续累加导致角色再次穿入Tile,确保稳定停留在地面上

内容的提问来源于stack exchange,提问作者Dzbice

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:55:03