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

如何在Pygame游戏中实现碰撞检测?解决输入失效问题

Pygame 玩家碰撞方块问题修复

你的代码主要有几个问题导致输入被禁用:

  1. 全局禁用移动的逻辑错误:check_collisions方法里,只要遍历到一个不与玩家碰撞的方块,就会把movement设为True,最后一个方块的判断会覆盖之前的结果。而且只要玩家碰过任何方块,就直接锁死所有移动,这完全不合理——应该是针对每个移动方向单独判断是否会碰撞,而不是全局禁用输入。

  2. 移动时的碰撞判断搞反了:比如按D键时,你写的是如果方块碰到玩家右侧一点就移动,这逻辑完全反了,应该是如果移动后不会碰到方块,才允许移动。

  3. 方块删除的遍历问题: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 07:20:37