如何在Pygame游戏中实现碰撞检测?解决输入失效问题
Pygame 玩家碰撞方块问题修复
你的代码主要有几个问题导致输入被禁用:
全局禁用移动的逻辑错误:
check_collisions方法里,只要遍历到一个不与玩家碰撞的方块,就会把movement设为True,最后一个方块的判断会覆盖之前的结果。而且只要玩家碰过任何方块,就直接锁死所有移动,这完全不合理——应该是针对每个移动方向单独判断是否会碰撞,而不是全局禁用输入。移动时的碰撞判断搞反了:比如按D键时,你写的是如果方块碰到玩家右侧一点就移动,这逻辑完全反了,应该是如果移动后不会碰到方块,才允许移动。
方块删除的遍历问题:
break_block里直接遍历self.blocks并删除元素,会导致循环漏过某些方块,应该用反向遍历或者创建副本遍历。
下面是修正后的完整代码:
import pygame, sys from pygame.math import Vector2 pygame.init() tile_size = 16 tile_number = 32 screen = pygame.display.set_mode((tile_size * tile_number, tile_size * tile_number)) clock = pygame.time.Clock() # 注意:如果背景图不存在,注释掉下面这行或者替换成你的图路径 background = pygame.image.load("background.png").convert_alpha() class MAIN: def __init__(self): self.pos = Vector2(0,0) self.blocks = [] # 玩家矩形初始位置,后续通过pos更新 self.player = pygame.Rect(int(self.pos.x * tile_size), int(self.pos.y * tile_size), tile_size, tile_size) def draw_player(self): # 根据pos更新玩家矩形位置 self.player.topleft = (int(self.pos.x * tile_size), int(self.pos.y * tile_size)) pygame.draw.rect(screen,(255,0,0), self.player) def place_block(self): mouse_pos = Vector2(pygame.mouse.get_pos()[0]//16,pygame.mouse.get_pos()[1]//16) block_rect = pygame.Rect(mouse_pos.x*16, mouse_pos.y*16, tile_size, tile_size) # 避免在玩家位置放方块 if not block_rect.colliderect(self.player): self.blocks.append(block_rect) def break_block(self): # 反向遍历列表,避免删除元素时漏循环 for i in range(len(self.blocks)-1, -1, -1): block = self.blocks[i] if block.collidepoint(pygame.mouse.get_pos()): del self.blocks[i] def draw_blocks(self): for block in self.blocks: pygame.draw.rect(screen,(0,0,0), block) # 新增方法:判断移动后的玩家是否会碰撞方块 def will_collide(self, new_pos): # 创建移动后的玩家矩形 new_player_rect = pygame.Rect(int(new_pos.x * tile_size), int(new_pos.y * tile_size), tile_size, tile_size) for block in self.blocks: if new_player_rect.colliderect(block): return True return False game = MAIN() while True: keys = pygame.key.get_pressed() # 针对每个方向单独判断是否可以移动 if keys[pygame.K_d]: new_pos = game.pos + Vector2(1, 0) if not game.will_collide(new_pos): game.pos = new_pos if keys[pygame.K_a]: new_pos = game.pos + Vector2(-1, 0) if not game.will_collide(new_pos): game.pos = new_pos if keys[pygame.K_w]: new_pos = game.pos + Vector2(0, -1) if not game.will_collide(new_pos): game.pos = new_pos if keys[pygame.K_s]: new_pos = game.pos + Vector2(0, 1) if not game.will_collide(new_pos): game.pos = new_pos # 处理方块放置和破坏 mouse = pygame.mouse.get_pressed() if mouse[2]: game.place_block() if mouse[0]: game.break_block() # 处理退出事件 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() # 绘制画面 screen.blit(background,(0,0)) if 'background' in locals() else screen.fill((100,100,100)) game.draw_blocks() game.draw_player() pygame.display.update() clock.tick(30)
关键改动说明:
- 删除了全局的
movement变量,改成每个移动方向单独预测碰撞:每次按键时先计算移动后的位置,判断该位置的玩家矩形是否会和方块碰撞,只有不碰撞才允许移动。 - 重写了
will_collide方法,专门用于检测移动后的位置是否合法。 - 修复了
break_block的遍历问题,改用反向索引删除,避免循环遗漏。 - 新增了放置方块时的判断,避免在玩家当前位置放方块,导致一开始就卡住。
- 优化了玩家矩形的更新方式,用
topleft直接赋值更清晰。
内容的提问来源于stack exchange,提问作者Liam Berube
相关产品推荐
相关产品推荐

