gsmith257
gsmith257

Reputation: 43

pygame collisions between sprites

I have searched all over the internet and stack for why my pygame won't move my sprite when it collides with a wall that I have in the game. I have the collision detection loop print out "hit2" whenever the player collides with a wall in wall_list (a sprite group). It instead is only detecting that the walls are colliding, by the looks of it, and not when the player collides with the wall. Could anyone take a second look at this and tell me what I have done wrong?

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

len_sprt_x = 21
len_sprt_y = 32 
sprt_rect_x = 5
sprt_rect_y = 160 

SPRT_RECT_X=0
SPRT_RECT_Y=0

LEN_SPRT_X=100
LEN_SPRT_Y=100

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

monsters = pygame.image.load("/Users/****/Downloads/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)) 
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"

class EasyChest(pygame.sprite.Sprite):
    def __init__(self):
        self.x = 425
        self.y = 327
        self.image = pygame.image.load("/Users/***/Downloads/154057568119963649.png")
        self.rect = self.image.get_rect()

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

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

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

class Player(pygame.sprite.Sprite):
    #direction = "none"
    '''
    Spawn a player
    '''
    def __init__(self):
        self.x = 450
        self.y = 685
        self.image = pygame.image.load("/Users/***/Downloads/3KvKpwY.png")
        self.rect = self.image.get_rect()

    def keys(self):
        '''
        control player movement
        '''
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT or event.key == ord('a'):
                print('left')
                direction = "left"
                self.x += -steps
            if event.key == pygame.K_RIGHT or event.key == ord('d'):
                print('right')
                direction = "right"
                self.x += steps
            if event.key == pygame.K_UP or event.key == ord('w'):
                print('up')
                direction = "up"
                self.y += -steps
            if event.key == pygame.K_DOWN or event.key == ord('s'):
                print('down')
                direction = "down"
                self.y += steps
            print(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')
            if event.key == ord('q'):
               pygame.quit()
            sys.exit()

    def update(self, wall_list):
        '''
        Update sprite position
        '''


class Wall(pygame.sprite.Sprite):

    def __init__(self, x, y, width, height):
        """ 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(BLUE)

        # 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],
]

wall_list = pygame.sprite.Group()

wall1 = Wall(96, 0, 5, 100)
wall_list.add(wall1)
wall2 = Wall(96, 100, 29, 5)
wall_list.add(wall2)
wall3 = Wall(120, 100, 5, 350)
wall_list.add(wall3)
wall4 = Wall(120, 450, 55, 5)
wall_list.add(wall4)
wall4 = Wall(175, 450, 5, 50)
wall_list.add(wall4)

steps = 5
done = False

monster_list = pygame.sprite.Group()

c1 = EasyChest()
m1 = Monster()
monster_list.add(m1)
m2 = Monster2()
monster_list.add(m2)
m3 = Monster3()
monster_list.add(m3)
p1 = Player()
main = 1

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

while main == 1: #game loop
    for event in pygame.event.get():
        if event.type == pygame.QUIT:  # lets user close the program
            print("UserExit")
            main = 0
    if pygame.sprite.spritecollide(p1, wall_list, False):
        print("sprites have collided!")
        print("hit2")
        if direction == "left":
            screen.blit(p1.image, (p1.x, p1.y + steps))
        if direction == "right":
            screen.blit(p1.image, (p1.x, p1.y - steps))
        if direction == "up":
            screen.blit(p1.image, (p1.x - steps, p1.y))
        if direction == "down":
            screen.blit(p1.image, (p1.x, p1.y + steps))

    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
    print(p1.x, p1.y)
    screen.blit(draw_me, (p1.x, p1.y))
    screen.blit(draw_me2, (c1.x, c1.y))
    screen.blit(draw_monsters, (m1.x, m1.y))
    screen.blit(draw_monsters, (m2.x, m2.y))
    screen.blit(draw_monsters, (m3.x, m3.y))
    wall_list.draw(screen)
    pygame.display.update()
    p1.keys()

    screen.blit(draw_me, (p1.x, p1.y))
    pygame.display.update()

Upvotes: 2

Views: 393

Answers (1)

skrx
skrx

Reputation: 20488

You have to move the self.rect of the player because it is used for the collision detection. If you only want to move in integer steps, you can remove the self.x, self.y attributes and just use the self.rect to store the coordinates.

I've also changed a few more things:

  • I'm passing the previous event to the player from the event loop, otherwise the last global event variable would be used all the time. Move the p1.keys(event) call into the event loop.
  • To reset the position after a collision, you can just move the player back one step.
  • I've cleaned up the drawing section of the code (there were two pygame.display.update() calls and you blitted the player twice.

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

    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
            print(self.direction)

The modified main loop:

# I added a wall at the start to test the collisions.
wall5 = Wall(420, 670, 50, 5)
wall_list.add(wall5)

clock = pygame.time.Clock()  # A clock to limit the frame rate.
main = 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

    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.x, c1.y))
    screen.blit(draw_monsters, (m1.x, m1.y))
    screen.blit(draw_monsters, (m2.x, m2.y))
    screen.blit(draw_monsters, (m3.x, m3.y))
    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, (255, 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.

Upvotes: 1

Related Questions