Colliderect firing even when 2 items aren't touching

Question:

I am making a system where the items fall from the sky and you collect them however the collision is being registered even when the character doesn’t touch the items. The collision happens when the character collides with the item on the x axis but it can collide at any point on the y axis.

Here is an example from the image below:

Example Image

I have tried printing the positions of both the character and item to see if for some reason the position wasn’t up to date with where it was on screen but for both the character and item the positions were fine.

Here is my code:

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)
`
Asked By: Dev Blox

||

Answers:

You have to get the bounding rectangle of the scaled image instead of the original image:

self.rect = self.image.get_rect()

self.rect = self.scaledImage.get_rect()

Note that the pygame.sprite.Sprite object does not need a render or draw method if you use pygame.sprite.Group. See How can I add objects to a "pygame.sprite.Group()"? and What does pygame.sprite.Group() do

Answered By: Rabbid76
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.