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
额外优化说明
除了修正碰撞检测的遍历问题,还添加了两个关键优化:
- 在
move函数中声明global player_y_momentum,确保可以修改全局的动量变量 - 当角色向下碰撞到Tile时,重置
player_y_momentum为0,避免动量持续累加导致角色再次穿入Tile,确保稳定停留在地面上
内容的提问来源于stack exchange,提问作者Dzbice
相关产品推荐
相关产品推荐

