1

I have not found any questions similar to this on the internet. I want pygame to detect when the player is colliding with another sprite, such as the chest or one of the monsters, but it is detecting the player is colliding with them even when they are not touching, also note it is not 100% of the time, its only in certain areas they are colliding.

My goal is to get the game to detect when the player touches a monster and then have one of the player's swords removed and the monster be killed or if the player has no sword the Player dies. Also want the chest to add a sword and key to inventory[] when touching the player but only once. I got that to work, but it open the chest when you get near it instead of when you touch it, same with monsters.

Here is the folder with all the images I am using.

And here is my code:

# TODO NOTES
# 1. create a title screen
# 2. put all walls into game
# 3. make it so you win when you beat all monsters


import pygame, sys, time
import random
import os
from pygame.locals import *
pygame.init()
pygame.font.init()

display_width = 800
display_height = 600
gameDisplay = pygame.display.set_mode((display_width, display_height))

pygame.display.set_caption('Welcome to G\'s game')
clock = pygame.time.Clock()

#def text_objects(text, font):
 #   textSurface = font.render(text, True, (0, 0, 0))
  #  return textSurface, textSurface.get_rect()

#def message_display(text):
 #   largeText = pygame.font.Font('freesansbold.ttf', 25)
  #  TextSurf, TextRect = text_objects(text, largeText)
   # TextRect.center = ((display_width-600),(display_height-200))
    #gameDisplay.blit(TextSurf, TextRect)

#myfont = pygame.font.SysFont('ariel', 50)
#textsurface = myfont.render('Welcome to \n G\'s game', False, (0, 0, 0))



# TODO NOTES
# 1. create a title screen
# 2. put all walls into game
# 3. make it so you win when you beat all monsters


import pygame, sys, time
import random
import os
from pygame.locals import *
pygame.init()
pygame.font.init()

display_width = 800
display_height = 600
gameDisplay = pygame.display.set_mode((display_width, display_height))

pygame.display.set_caption('Welcome to G\'s game')
clock = pygame.time.Clock()

#def text_objects(text, font):
 #   textSurface = font.render(text, True, (0, 0, 0))
  #  return textSurface, textSurface.get_rect()

#def message_display(text):
 #   largeText = pygame.font.Font('freesansbold.ttf', 25)
  #  TextSurf, TextRect = text_objects(text, largeText)
   # TextRect.center = ((display_width-600),(display_height-200))
    #gameDisplay.blit(TextSurf, TextRect)

#myfont = pygame.font.SysFont('ariel', 50)
#textsurface = myfont.render('Welcome to \n G\'s game', False, (0, 0, 0))



    #Initial Values
len_sprt_x = 21
len_sprt_y = 32 #sprite size
sprt_rect_x = 5
sprt_rect_y = 160 #where to find first sprite on sheet

SPRT_RECT_X=0
SPRT_RECT_Y=0
#This is where the sprite is found on the sheet

LEN_SPRT_X=100
LEN_SPRT_Y=100
#This is the length of the sprite

screen = pygame.display.set_mode((20, 30)) #Create the screen
sheet = pygame.image.load('/Users/***/Downloads/3KvKpwY.png') #Load the sheet
sheet_chests = pygame.image.load("/Users/GrantSmith/***/154057568119963649.png")

monsters = pygame.image.load("/Users/GrantSmith/***/Typhon_Monster-Sire_Sprite.png")
draw_monsters = pygame.transform.scale(monsters, (55, 45))

sheet.set_clip(pygame.Rect(sprt_rect_x, sprt_rect_y, len_sprt_x, len_sprt_y)) #Locate the sprite you want
sheet_chests.set_clip(pygame.Rect(32, 8, 34, 33))
draw_me2 = sheet_chests.subsurface(sheet_chests.get_clip())
draw_me = sheet.subsurface(sheet.get_clip()) #Extract the sprite you want
direction = "none"

def text_objects(text, font):   #dont need this anymore
    textSurface = font.render(text, True, (0, 0, 0))
    return textSurface, textSurface.get_rect()

def game_intro():       #dont need this anymore
    intro = True

    while intro:
        gameDisplay.fill(WHITE)
        largeText = pygame.font.SysFont('ariel', 50)
        TextSurf, TextRect = text_objects("Welcome to Grant\'s game", largeText)
        TextRect.center = ((display_width / 2), (display_height / 2))
        gameDisplay.blit(TextSurf, TextRect)
        pygame.display.update()
        time.sleep(5)
        intro = False

class EasyChest(pygame.sprite.Sprite):
    def __init__(self, x, y):
        """ Constructor for the wall that the player can run into. """
        # Call the parent's constructor
        super().__init__()
        self.image = pygame.image.load("/Users/***/Downloads/154057568119963649.png")
        self.rect = self.image.get_rect()
        self.rect.x = x  # 425
        self.rect.y = y  # 327
        self.rect.topleft = self.rect.x, self.rect.y


class Monster(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.image.load("/Users/***/Downloads/Typhon_Monster-Sire_Sprite.png")
        self.rect = self.image.get_rect()
        self.rect.x = x #175
        self.rect.y = y #520
        self.rect.topleft = self.rect.x, self.rect.y


class Monster2(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.image.load("/Users/***/Downloads/Typhon_Monster-Sire_Sprite.png")
        self.rect = self.image.get_rect()
        self.rect.x = x #330
        self.rect.y = y #400
        self.rect.topleft = self.rect.x, self.rect.y


class Monster3(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.image.load("/Users/***/Downloads/Typhon_Monster-Sire_Sprite.png")
        self.rect = self.image.get_rect()
        self.rect.x = x #285
        self.rect.y = y #190
        self.rect.topleft = self.rect.x, self.rect.y


inventory = []
inventory.append("sword")
class Player(pygame.sprite.Sprite):
    """Spawn a player."""

    def __init__(self):
        self.image = draw_me  # Assign the player image.
        # Assign the topleft coords of the rect.
        self.rect = self.image.get_rect(topleft=(450, 685))
        # The direction should be an instance attribute not a global variable.
        self.direction = None
        self.rect.topleft = self.rect.x, self.rect.y

    def keys(self, event):
        """Control player movement."""
        if event.type == pygame.KEYDOWN:
            if event.key in (pygame.K_LEFT, pygame.K_a):
                self.direction = "left"
                self.rect.x += -steps
            elif event.key in (pygame.K_RIGHT, pygame.K_d):
                self.direction = "right"
                self.rect.x += steps
            elif event.key in (pygame.K_UP, pygame.K_w):
                self.direction = "up"
                self.rect.y += -steps
            elif event.key in (pygame.K_DOWN, pygame.K_s):
                self.direction = "down"
                self.rect.y += steps
            elif event.key == pygame.K_i:
                print(inventory)
            print(self.direction)
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                print('left stop')
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                print('right stop')
            if event.key == pygame.K_UP or event.key == ord('w'):
                print('up stop')
            if event.key == pygame.K_DOWN or event.key == ord('s'):
                print('down stop')

class Wall(pygame.sprite.Sprite):

    def __init__(self, x, y, width, height, color1):
        """ Constructor for the wall that the player can run into. """
        # Call the parent's constructor
        super().__init__()

        # Make a blue wall, of the size specified in the parameters
        self.image = pygame.Surface([width, height])
        self.image.fill(color1)

        # Make the top-left corner the passed-in location.
        self.rect = self.image.get_rect()
        self.rect.y = y
        self.rect.x = x


mapwidth = 20
mapheight= 30
tilesize = 25


#define colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)
TROPICBLUE = (152,245,255)
GREY = (142,142,142)
WOOD = (156,102,31)
PATH = (139,115,85)
GINGER = (255,127,0)
FACE = (238,213,183)
FACESHADE = (205,183,158)
RED1 = (165,42,42)

HAIR = 0
SKIN = 1
NOSE = 2
GRASS = 3
WATER = 4
DEEPWATER = 5
MOUTH = 6
DIRT = 7
PATH = 8

colours = {
    HAIR: GINGER,
    SKIN:FACE,
    NOSE: FACESHADE,
    GRASS: GREEN,
    WATER: TROPICBLUE,
    DEEPWATER: BLUE,
    MOUTH: RED1,
    DIRT: WOOD,
    PATH: PATH
}

tilemap = [
    [DEEPWATER, WATER, DIRT, DIRT, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, WATER, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, DIRT],
    [DEEPWATER, WATER, WATER, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, WATER, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, DEEPWATER, WATER, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, DEEPWATER, WATER, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, DEEPWATER, WATER, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, DEEPWATER, WATER, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, DEEPWATER, WATER, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, DIRT, DIRT, DIRT, DIRT, DIRT],
    [DEEPWATER, DEEPWATER, WATER, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH],
    [DEEPWATER, DEEPWATER, WATER, WATER, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, PATH, PATH, PATH, PATH, PATH, PATH, PATH, PATH],
    [DEEPWATER, DEEPWATER, WATER, WATER, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT, DIRT],
]

#walls = []
#for i in range(0, 35):
#    boxes.append(Boxes())

# LETS MAKE SOME WALLS!
# "IM GOING TO BUILD A WALL!" -Pdaddy trump
#21 walls to go

wall_list = pygame.sprite.Group()

wall1 = Wall(96, 0, 5, 100, BLUE)
wall_list.add(wall1)
wall2 = Wall(96, 100, 29, 5, BLUE)
wall_list.add(wall2)
wall3 = Wall(120, 100, 5, 350, BLUE)
wall_list.add(wall3)
wall4 = Wall(120, 450, 55, 5, BLUE)
wall_list.add(wall4)
wall4 = Wall(175, 450, 5, 50, BLUE)
wall_list.add(wall4)
wall5 = Wall(150, 495, 29, 5, BLUE)
wall_list.add(wall5)
wall6 = Wall(145, 495, 5, 85, BLUE)
wall_list.add(wall6)
wall7 = Wall(145, 575, 55, 5, BLUE)
wall_list.add(wall7)
wall8 = Wall(195, 575, 5, 75, BLUE)
wall_list.add(wall8)
wall9 = Wall(195, 650, 105, 5, BLUE)
wall_list.add(wall9)
wall10 = Wall(295, 650, 5, 75, BLUE)
wall_list.add(wall10)
wall11 = Wall(295, 725, 205, 5, BLUE)
wall_list.add(wall11)
wall12 = Wall(495, 675, 5, 50, BLACK)
wall_list.add(wall12)
wall13 = Wall(374, 675, 165, 5, BLUE)
wall_list.add(wall13)
wall14 = Wall(374, 450, 5, 228, BLUE)
wall_list.add(wall14)
wall13 = Wall(374, 450, 103, 5, BLUE)
wall_list.add(wall13)


steps = 5
done = False

monster_list = pygame.sprite.Group()
chest_list = pygame.sprite.Group()

c1 = EasyChest(425, 327)
c1Open = False
chest_list.add(c1)
m1 = Monster(175, 520)
monster_list.add(m1)
m2 = Monster2(330, 400)
monster_list.add(m2)
m3 = Monster3(285, 190)
monster_list.add(m3)
p1 = Player()

offset_x = p1.rect[0] - p1.rect[0]
offset_y = p1.rect[1] - p1.rect[1]

m1Alive = True
m2Alive = True
m3Alive = True

clock = pygame.time.Clock()  # A clock to limit the frame rate.
main = True
main1 = True

while main:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            main = False
        # Pass the previous event to the player in the event loop
        # (once per event not per frame).
        p1.keys(event)

    if pygame.sprite.spritecollide(p1, wall_list, False):
        print("sprites have collided!")
        # Move the player back one step if a collision occurred.
        if p1.direction == "left":
            p1.rect.x += steps
        elif p1.direction == "right":
            p1.rect.x -= steps
        elif p1.direction == "up":
            p1.rect.y += steps
        elif p1.direction == "down":
            p1.rect.y -= steps
    if pygame.sprite.spritecollide(p1, chest_list, False):
        if c1Open == False:
            x = 1
            while x < 10:
                print("You opened a chest")
                x=x+1
            inventory.append("sword")
            inventory.append("key")
            c1Open = True

    if pygame.sprite.collide_rect(p1, m1):
        print("You are now fighting a monster!")
        if inventory.count("sword") > 0:
            monster_list.remove(m1)
            m1Alive = False
            inventory.remove("sword")
        else:
            print("you died")
            main = False
    if pygame.sprite.collide_rect(p1, m2):
        print("You are now fighting a monster!")
        if inventory.count("sword") > 0:
            monster_list.remove(m2)
            m2Alive = False
            inventory.remove("sword")
        else:
            print("you died")
            main = False
    if pygame.sprite.collide_rect(p1, m3):
        print("You are now fighting a monster!")
        if inventory.count("sword") > 0:
            monster_list.remove(m3)
            m3Alive = False
            inventory.remove("sword")
        else:
            print("you died")
            main = False


    screen = pygame.display.set_mode((mapwidth * tilesize, mapheight * tilesize))
    for row in range(mapheight):
        for column in range(mapwidth):
            backdrop1 = pygame.draw.rect(screen, colours[tilemap[row][column]],
                                         (column * tilesize, row * tilesize, tilesize, tilesize))

    backdrop = backdrop1
    screen.blit(draw_me2, c1.rect)
    if m1Alive == True:
        screen.blit(draw_monsters, m1.rect)
    if m2Alive == True:
        screen.blit(draw_monsters, m2.rect)
    if m3Alive == True:
        screen.blit(draw_monsters, m3.rect)
    wall_list.draw(screen)
    # Blit the player image at the player rect.
    screen.blit(p1.image, p1.rect)
    # Test if the player rect moves correctly.
    pygame.draw.rect(screen, (0, 0, 0), p1.rect, 1)
    pygame.display.update()  # Call `display.update` only once per frame.
    clock.tick(60)  # Limit the frame rate to 60 FPS.
gsmith257
  • 45
  • 5
  • Please read the [Minimal, Complete and Verifiable example page](https://stackoverflow.com/help/mcve). It can be very time intensive to analyze and debug so much unknown code. – skrx Nov 01 '18 at 19:06

1 Answers1

2

After playing around a bit (pun!): you don't set the chest's sprite width and height, so it stays the size of your image – 99x99 pixels. The call to collide works, but it checks against the much larger entire image.

If you set it explicitly to 32x32, collide starts to work as expected:

    self.rect = self.image.get_rect()
    self.rect.x = x  # 425
    self.rect.y = y  # 327
    self.rect.topleft = self.rect.x, self.rect.y
    print (self.rect.width,self.rect.height)
    self.rect.width = 32
    self.rect.height = 32

-- here the print shows that its size is still 99x99, the size of the image itself. (I didn't test but your player sprite may have the same problem, as you construct it the same way.)

To get it to open when you are nearby, change its rect.x, rect.y, rect.width, and rect.height to a larger rectangle.

Also want the chest to add a sword and key to inventory[] when touching the player but only once.

Umm, you must mean the other way around here. Your code prints "you opened a chest" nine times, but you only add a single sword and key once. From your description, I assume you want this instead:

print("You opened a chest")
for x in range(9):
    inventory.append("sword")
    inventory.append("key")

or in a single line

inventory += ['sword','key']*9
Jongware
  • 22,200
  • 8
  • 54
  • 100
  • Ok so this fixed the original problem but now I am running into an additional problem. Whenever you collide with a monster you should kill it and it dissapears but it is killing the monster and the monster is staying so your player dies when you touch the monster. I have updated the code in the original question so that you can have a look at it and see if you can see what I am not. – gsmith257 Nov 01 '18 at 03:29
  • 1
    ok I apologize. I have reverted it back to the previous version. I will start a new question. Thank you for your help. – gsmith257 Nov 01 '18 at 14:41