简体   繁体   中英

collision detection not functioning properly

Im trying to add collision detection on a rock sprite using the following code;

import pygame

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)


class Player(pygame.sprite.Sprite):


    def __init__(self, filename, x, y):

        super().__init__()

        self.image = pygame.image.load(filename).convert()
        self.image.set_colorkey(BLACK)

        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y


        self.change_x = 0
        self.change_y = 0

    def changespeed(self, x, y):
        self.change_x += x
        self.change_y += y

    def update(self):
        self.rect.x += self.change_x
        self.rect.y += self.change_y


    def rockCollision(self, rock):

        block_hit_list = pygame.sprite.spritecollide(self, rock, False)
        for block in block_hit_list:
            if self.change_x > 0:
                self.rect.right = block.rect.left
            else:
                self.rect.left = block.rect.right


        block_hit_list = pygame.sprite.spritecollide(self, rock, False)
        for block in block_hit_list:

            if self.change_y > 0:
                self.rect.bottom = block.rect.top
            else:
                self.rect.top = block.rect.bottom


class Rock(pygame.sprite.Sprite):

    def __init__(self, filename, x, y):

        super().__init__()    

        self.image = pygame.image.load(filename).convert()
        self.image.set_colorkey(BLACK)

        self.rect = self.image.get_rect().inflate(1, 1)
        self.rect.y = y
        self.rect.x = x



pygame.init()

screen_width = 1080
screen_height = 607
screen = pygame.display.set_mode([screen_width, screen_height])

pygame.display.set_caption('Labyrinth')

block_list = pygame.sprite.Group()
all_sprites_list = pygame.sprite.Group()
rock_list = pygame.sprite.Group()

rock = Rock("Rock.png", 380, 280)
rock_list.add(rock)

player = Player("Isaac.png", 420, 150)
all_sprites_list.add(player)


clock = pygame.time.Clock()

background_position = [0, 0]
background_image = pygame.image.load("Floor.png").convert()

done = False


# -- MAIN PROGRAM LOOP -- #

# -- Event processing --

while not done:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

# Player controls

        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                player.changespeed(-7, 0)
            elif event.key == pygame.K_RIGHT:
                player.changespeed(7, 0)
            elif event.key == pygame.K_UP:
                player.changespeed(0, -7)
            elif event.key == pygame.K_DOWN:
                player.changespeed(0, 7)

        elif event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT:
                player.changespeed(7, 0)
            elif event.key == pygame.K_RIGHT:
                player.changespeed(-7, 0)
            elif event.key == pygame.K_UP:
                player.changespeed(0, 7)
            elif event.key == pygame.K_DOWN:
                player.changespeed(0, -7)

# -- Game Logic --

    all_sprites_list.update()

# Hit detection 

    blocks_hit_list = pygame.sprite.spritecollide(player, block_list, True)
    player.rockCollision(rock_list)



    screen.blit(background_image, background_position)

    all_sprites_list.draw(screen)
    rock_list.draw(screen)

    pygame.display.flip()

    clock.tick(60)

pygame.quit()

Moving left or right into the rock, the collision works perfectly. However, moving up or down into the rock clips my player to the right of the rock (I can never actually walk up into the rock and get stuck there i get immediately clipped to the right).

My question is why does this happen? I can see no reason as to why it only happens when i walk up or down into the rock, not left or right as the code for each is identical.

I want to be able to walk into the rock while walking up or down and be able to actually get stuck on the rock instead of clipping around it

Your collision detection checks for self.change_x > 0 , and self.change_y > 0 , but doesn't handle the case where those values are equal to zero. This means you are assuming that if the player isn't moving up he must be moving down, whereas in reality, it's possible to be doing neither up or down while moving sideways. And similarly for left/right movement while moving vertically.

This only causes a problem when walking vertically because when there's a horizontal collision, the problem is cleared by the first block of code, so when you call spritecollide() the second time, block_hit_list is empty, so the second for loop is skipped.

To improve things, you could change your rockCollision() method as follows:

def rockCollision(self, rock):
    block_hit_list = pygame.sprite.spritecollide(self, rock, False)
    for block in block_hit_list:
        if self.change_x > 0:
            self.rect.right = block.rect.left
        elif self.change_x < 0:
            self.rect.left = block.rect.right
        # else not moving sideways, so don't change our x position

    block_hit_list = pygame.sprite.spritecollide(self, rock, False)
    for block in block_hit_list:
        if self.change_y > 0:
            self.rect.bottom = block.rect.top
        elif self.change_y < 0:
            self.rect.top = block.rect.bottom
        # else not moving vertically, so don't change our y position

This fixes the problem when moving left-right or up-down. But you'll notice that it still fails if you're moving diagonally: even though both self.change_x and self.change_y are non-zero, your code can't tell which of those two movements have caused the collision.

There's a tutorial here that shows how to handle collisions in a more robust way, by decomposing diagonal movements into 2 steps (one vertical and one horizontal), with a collision check for each step.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM