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

Pygame中colliderect未接触时仍触发碰撞问题求助

问题描述

我正在制作一个物品从天而降供玩家收集的系统,但即使角色未触碰物品,colliderect仍会触发碰撞。碰撞仅在角色与物品的X轴重合时发生,Y轴任意位置都会触发。我已尝试打印角色和物品的位置,确认位置与屏幕显示一致,但问题仍存在。

代码片段
import pygame
import sys
import os
import time
from pygame.locals import *

os.environ["SDL_VIDEO_CENTERED"] = "1"
pygame.init()

SCREENWIDTH = 580
SCREENHEIGHT = 375
SCREENSIZE = [SCREENWIDTH, SCREENHEIGHT]
SCREEN = pygame.display.set_mode(SCREENSIZE)

CLOCK = pygame.time.Clock()
FPS = 60


jumping = False
backgroundImage = pygame.image.load('Images/backgroundImage.jpg')
backgroundScaled = pygame.transform.scale(backgroundImage,(580,375))
pygame.display.set_caption("Christmas Game")

santaWidth, santaHeight = 93,64
santaImage = pygame.image.load("Images/santa.png")
santaScaled = pygame.transform.scale(santaImage,(santaWidth,santaHeight))
santa = pygame.Rect(100,100,santaWidth,santaHeight)
                                     
font = pygame.font.SysFont("myriadProFont",50)
timeText = font.render("", 1,(255,255,255))
startTime = time.time()

itemsGroup = pygame.sprite.Group()

#IDs 0 = coin, 
class Item(pygame.sprite.Sprite):
  def __init__(self,x,y,ID,imageName):
    super().__init__()
    self.ID = ID
    self.image = pygame.image.load(imageName)
    self.scaledImage = pygame.transform.scale(self.image,(50,50))
    self.x = x
    self.y = y
    self.rect = self.image.get_rect()
    self.rect.topleft = (self.x,self.y)
  def render(self, screen):
    screen.blit(self.scaledImage,(self.x,self.y))
  def collect(self, santa):
    hit = self.rect.colliderect(santa)
    if hit:
      if self.ID == 0:
        print("Coin")
      self.kill()
  def itemGravity(self):
    if self.y < 370:
      self.y += 1
      self.rect = self.image.get_rect()
      self.rect.topleft = (self.x,self.y)
    else:
      self.kill()
      print("killed")
  
    
coin = Item(200,0,0,"Images/coin.png")
itemsGroup.add(coin)
coin2 = Item(500,0,0,"Images/coin.png")
itemsGroup.add(coin2)


class Floor(pygame.sprite.Sprite):
  def __init__(self, width, height, x, y, name):
    super().__init__()
    self.image = pygame.image.load(name)
    self.image = pygame.transform.scale(self.image, (width, height))
    self.rect = self.image.get_rect()
    self.size = width, height
    self.pos = (x, y)
    self.rect.topleft = self.pos
    self.isTouching = False
  def render(self, display):
    display.blit(self.image, self.pos)
  def collision(self,santa):
    global jumping
    hit = self.rect.colliderect(santa)
    if hit and self.isTouching == False:
      self.isTouching = True
    elif not hit:
      self.isTouching = False
floorGroup = pygame.sprite.Group()
floor = Floor(291,70,-50,305,"Images/floor.webp")
floorGroup.add(floor)
floor2 = Floor(291,70,330,305,"Images/floor.webp")
floorGroup.add(floor2)


def adjustTime():
  global timeText
  global jumping
  newTime = time.time()
  difference = newTime - startTime
  if difference > 1:
    timeText = font.render(time.strftime('%M:%S', time.gmtime(difference)), 1,(255,255,255))



def playerMovement(keyPressed,santa):
  global jumping
  if (keyPressed[pygame.K_UP] or keyPressed[pygame.K_w] or keyPressed[pygame.K_SPACE]) and santa.y > 0 and jumping == False:
    jumping = True
    santa.y = santa.y - 50
  elif(keyPressed[pygame.K_LEFT] or keyPressed[pygame.K_a]) and santa.x > -20:
    santa.x = santa.x - 5
  elif(keyPressed[pygame.K_RIGHT] or keyPressed[pygame.K_d]) and santa.x < 525:
   santa.x = santa.x + 5
  

def updateScreen(santa):
  SCREEN.blit(backgroundScaled, (0, 0))
  for floor in floorGroup:
    floor.render(SCREEN)
  for item in itemsGroup:
    item.render(SCREEN)
  SCREEN.blit(santaScaled,(santa.x,santa.y))
  SCREEN.blit(timeText,(250,0))
  pygame.display.update()


def gravity(santa): 
  global jumping
  if santa.y < 320:
    santa.y += 3
  elif santa.y == 320:
    jumping = False
  else:
    santa.y = 320


while True:
  timeDelta = CLOCK.tick(FPS)/1000.0 
  adjustTime()

  for event in pygame.event.get():
    if event.type == pygame.QUIT:
      pygame.quit()
      sys.exit()
  user_input = pygame.key.get_pressed()  
  playerMovement(user_input,santa)
  gravity(santa)
  for item in itemsGroup:
    item.collect(santa)
    item.itemGravity()
  for floor in floorGroup:
    floor.collision(santa)
  updateScreen(santa)
  CLOCK.tick(FPS)
问题原因与修复方案

核心问题

碰撞检测异常是因为Item类的rect尺寸错误:

  • 初始化和更新时,你用了未缩放的原始图片self.image创建rect,但实际显示的是缩放后的50x50图片。如果原始图片尺寸远大于50x50,会导致rect的碰撞范围远大于视觉上的物品,从而出现X轴重合就触发碰撞的情况。

修复步骤

  1. 用缩放后的图片初始化rect
    修改Item类的__init__方法,将self.rect = self.image.get_rect()替换为:
self.rect = self.scaledImage.get_rect()  # 基于显示的缩放图片生成正确尺寸的rect
  1. 更新位置时无需重建rect
    修改Item类的itemGravity方法,直接更新rect的y坐标,避免每次重建rect导致的尺寸错误:
def itemGravity(self):
  if self.y < 370:
    self.y += 1
    self.rect.y = self.y  # 直接同步rect的y值,保持与显示位置一致
  else:
    self.kill()
    print("killed")
  1. 可选优化:统一用Rect属性管理位置
    你的santa是Rect对象,但目前直接修改santa.x和santa.y,建议改为操作santa.rect.x和santa.rect.y,保持Rect对象的一致性,避免潜在的位置同步问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 00:35:35