Quantcast
Channel: Active questions tagged python - Stack Overflow
Viewing all articles
Browse latest Browse all 14271

Horizontal collision is not working as intended [duplicate]

$
0
0

EDIT:

After countless hours, I'm still unable to fix the horizontal collision issue. I've used this link I was suggested to implement into my code:pygame platformer collision causes weird spacing issue

I found that the issue most likely stems from the collision function. I've honestly tried everything and time is running out to submit my code to my teachers. I would be forever grateful if someone could help me pinpoint what is exactly wrong with the code that doesn't fix the horizontal collision.

Here is the newly revised code I made recently where horizontal collision still doesn't work:

#Neccessary libraries importedimport osimport randomimport mathimport pygamefrom os import listdirfrom os.path import isfile, join#Initialises necessary pygame functionspygame.init()pygame.display.set_caption("Gameplay")BG_COLOR = (255, 255, 255)width, height = 1920, 1080FPS = 60#Speed at which sprite movesPLAYER_VEL = 10window = pygame.display.set_mode((width, height))background_image = pygame.image.load("assets/background/BG.png").convert()background_image = pygame.transform.scale(background_image, (width, height))#Function sets how the block (platforms in this case) will look like.def load_block(size):    COLOUR = (0, 0, 0)    surface = pygame.Surface((size, size), pygame.SRCALPHA, 32)    rect = pygame.Rect(96, 0, size, size)    surface.fill(COLOUR, rect)    return pygame.transform.scale2x(surface)#drawing the object and player onto the windowdef draw(player, objects, win, offset_x):    window.blit(background_image, (0, 0))    # Draw the player sprite at its current position considering the offset    win.blit(player.sprite, (player.rect.x - offset_x, player.rect.y))    # Draw each object in the objects list, adjusting their positions…    # …relative to the offset.    for obj in objects:        obj.draw(win, offset_x)    pygame.display.update()#function deals with collisions with platforms and ceiling.def vertical_collision(player, objects, dy):    collided_objects = []    for obj in objects:  # all possible objects sprite could collide with        if pygame.sprite.collide_rect(player, obj):  # Check for collision            if dy >= 0:                player.rect.bottom = obj.rect.top                player.landed()            else:                player.rect.top = obj.rect.bottom                player.hit_head()            collided_objects.append(obj)    return collided_objects#handles horizontal collisionsdef collide(player, objects, dx):    player.move(dx, 0)  # moves the player horizontally    player.update()  # updates the player's rectangle    collided_object = None    for obj in objects:        for sprite in player.tiles.sprites():                if sprite.rect.colliderect(player.rect):                    if player.direction.x < 0:                        player.rect.left = sprite.rect.right                    elif player.direction.x > 0:                        player.rect.right = sprite.rect.left    player.move(-dx, 0)  # moves the player back to its original position    player.update()  # updates the player's rectangle    return collided_object#class created to design a template of how the sprite will look like and how it will operateclass Player(pygame.sprite.Sprite):    #sets colour of sprite    COLOUR = (0, 0, 0)    #sets rate of speed at which the sprite falls    GRAVITY = 1    #initialises    def __init__(self, x, y, width, height, image_path):        super().__init__()        self.image = pygame.transform.scale(pygame.image.load(image_path).convert_alpha(), (width * 4, height * 2))        self.rect = self.image.get_rect(topleft=(x, y))        self.x_vel = 0        self.y_vel = 0        self.direction = "right"        self.animation_count = 0        self.fall_count = 0        self.jump_count = 0        self.sprite = self.image        self.tiles = pygame.sprite.Group()    def jump(self):        self.y_vel = -self.GRAVITY*8 #When jumping, sprite will jump into air.        self.animation_count=0        self.jump_count += 1        if self.jump_count==1:            self.count=0 #As soon as sprite jumps, the gravity is removed.             #then reapplies gravity when sprite is falling / has landed.    #movement    def move(self, dx, dy):        self.rect.x += dx        self.rect.y += dy    #movement to left direction    def move_left(self, vel):        self.x_vel = -vel        self.direction = "left"  # Update direction        self.animation_count = 0    #movement to right direction    def move_right(self, vel):        self.x_vel = vel        self.direction = "right"  # Update direction        self.animation_count = 0    #keeps movement in loop    def loop(self, fps):        self.y_vel += min(1, (self.fall_count / fps) * self.GRAVITY)        self.move(self.x_vel, self.y_vel)        self.fall_count += 1    #function to determine if sprite has landed on a platform    def landed(self):        self.fall_count=0        self.y_vel=0        self.jump_count=0    #function to determine if sprite has collided with the ceiling    def hit_head(self):        self.count=0        self.y_vel*=-1    def update_sprite(self):        sprite_sheet = "idle"        #jump animations, will write this up later        if self.y_vel != 0:            if self.jump_count == 1:                sprite_sheet = "jump"            elif self.jump_count==2:                sprite_sheet = "double_jump"        elif self.y_vel> self.GRAVITY*2:            sprite_sheet = "fall"        elif self.x_vel != 0:            sprite_sheet = "run"        if self.x_vel != 0:            sprite_sheet = "run"        sprite_sheet_name = sprite_sheet +"_" + self.direction        sprites = self.SPRITES[sprite_sheet_name]        sprite_index = (self.animation_count // self. ANIMATION_DELAY) % len(sprites)        self.sprite = sprites[sprite_index]        self.animation_count += 1        self.update()    def update(self):        self.rect = self.sprite.get_rect(topleft=(self.rect.x, self.rect.y))        self.mask = pygame.mask.from_surface(self.sprite)class Object(pygame.sprite.Sprite):    def __init__(self, x, y, width, height, name=None):        super().__init__()        self.rect = pygame.Rect(x, y, width, height)        self.image = pygame.Surface((width, height))        self.image.fill(self.COLOUR)        self.width = width        self.height = height        self.name = name    def draw(self, win, offset_x):        win.blit(self.image, (self.rect.x - offset_x, self.rect.y))class Block(Object):    COLOUR = (0, 0, 0)    def __init__(self, x, y, size):        super().__init__(x, y, size, size)        block = load_block(size)        self.image.blit(block, (0, 0))        self.mask = pygame.mask.from_surface(self.image)#Function addresses any keys pressed to move the spritedef move(player, objects):    keys = pygame.key.get_pressed()    player.x_vel = 0    #handling horizontal collision    collide_left = collide(player,objects, -PLAYER_VEL)    collide_right = collide(player,objects, PLAYER_VEL)    #only allows sprite to move if there is no block next to it    if keys[pygame.K_d] and not collide_right:        player.move_right(PLAYER_VEL)    if keys[pygame.K_a] and not collide_left:        player.move_left(PLAYER_VEL)    #up key removed here, explained later in the code.    #calls the vertical collision function    vertical_collision(player,objects,player.y_vel) def gameplay(window):    clock = pygame.time.Clock()    block_size = 96    player = Player(400, 800, 25, 50, "assets/Sprite.png",)    blocks = [Block(0, height - block_size, block_size)]    #fills the bottom of the screen with platforms.    floor = [Block  (i * block_size, height - block_size, block_size)              for i in range (-width // block_size, width * 2 // block_size)]    #used to keep track of how much the player has moved horizontally relative to the window's view.    offset_x = 0    #when sprite reaches x amount of pixels in either direction, the game starts to scroll.    scroll_area_width = 200    #creating a block above platform    objects = [*floor, Block(0,height - block_size*2, block_size)]    run = True    while run:        clock.tick(FPS)        # Event handling        for event in pygame.event.get():            if event.type == pygame.QUIT:                run = False                break            if event.type == pygame.KEYDOWN:                if event.key == pygame.K_w and player.jump_count < 2:                    player.jump()        # Update player and objects        player.loop(FPS)        move(player, objects + player.tiles.sprites())        # Check if player has reached the scrolling area and update offset accordingly        if ((player.rect.right - offset_x >= width - scroll_area_width) and player.x_vel > 0) \                or ((player.rect.left - offset_x <= scroll_area_width) and player.x_vel < 0):            offset_x += player.x_vel        # Draw everything with the updated offset        draw(player, objects, window, offset_x)    pygame.quit()    quit()if __name__ == "__main__":    gameplay(window)

ORIGINAL POST:

When the sprite is moving to a block along the horizontal axis, the sprite should collide with the block. Instead, it just teleports the sprite to the top of the block.

#Neccessary libraries importedimport osimport randomimport mathimport pygamefrom os import listdirfrom os.path import isfile, join#Initialises necessary pygame functionspygame.init()pygame.display.set_caption("Gameplay")BG_COLOR = (255, 255, 255)width, height = 1920, 1080FPS = 60#Speed at which sprite movesPLAYER_VEL = 10window = pygame.display.set_mode((width, height))#Function sets how the block (platforms in this case) will look like.def load_block(size):    COLOUR = (0, 0, 0)    surface = pygame.Surface((size, size), pygame.SRCALPHA, 32)    rect = pygame.Rect(96, 0, size, size)    surface.fill(COLOUR, rect)    return pygame.transform.scale2x(surface)#drawing the object and player onto the windowdef draw(player, objects, win, offset_x):    win.fill(BG_COLOR)  # Clear the window    # Draw the player sprite at its current position considering the offset    win.blit(player.sprite, (player.rect.x - offset_x, player.rect.y))    # Draw each object in the objects list, adjusting their positions…    # …relative to the offset.    for obj in objects:        obj.draw(win, offset_x)    pygame.display.update()#function deals with collisions with platforms and ceiling.def vertical_collision(player, objects, dy):    collided_objects = []    for obj in objects:  # all possible objects sprite could collide with        if pygame.sprite.collide_rect(player, obj):  # Check for collision            if dy > 0:                player.rect.bottom = obj.rect.top                player.landed()            else: #################THIS WAS CAUSING THE BLOODY PROBLEM                player.rect.top = obj.rect.bottom                player.hit_head()            collided_objects.append(obj)    return collided_objects#handles horizontal collisionsdef collide(player,objects,dx):    player.move(dx,0) #checks if sprite moves to the right    player.update() #calls update to update rectangle    collided_object = None    for obj in objects:        if pygame.sprite.collide_mask(player,obj):            collided_object = obj            break    player.move(-dx,0)    player.update()    return collided_object#class created to design a template of how the sprite will look like and how it will operateclass Player(pygame.sprite.Sprite):    #sets colour of sprite    COLOUR = (0, 0, 0)    #sets rate of speed at which the sprite falls    GRAVITY = 1    #initialises    def __init__(self, x, y, width, height):        super().__init__()        self.sprite = pygame.Surface((width, height))        self.sprite.fill(self.COLOUR)        self.rect = self.sprite.get_rect(topleft=(x, y))        self.x_vel = 0        self.y_vel = 0        self.direction = "right"  # Set initial direction        self.animation_count = 0        self.fall_count = 0        self.jump_count = 0 #declares count for jump.    def jump(self):        self.y_vel = -self.GRAVITY*8 #When jumping, sprite will jump into air.        self.animation_count=0        self.jump_count += 1        if self.jump_count==1:            self.count=0 #As soon as sprite jumps, the gravity is removed.             #then reapplies gravity when sprite is falling / has landed.    #movement    def move(self, dx, dy):        self.rect.x += dx        self.rect.y += dy    #movement to left direction    def move_left(self, vel):        self.x_vel = -vel        self.direction = "left"  # Update direction        self.animation_count = 0    #movement to right direction    def move_right(self, vel):        self.x_vel = vel        self.direction = "right"  # Update direction        self.animation_count = 0    #keeps movement in loop    def loop(self, fps):        self.y_vel += min(1, (self.fall_count / fps) * self.GRAVITY)        self.move(self.x_vel, self.y_vel)        self.fall_count += 1    #function to determine if sprite has landed on a platform    def landed(self):        self.fall_count=0        self.y_vel=0        self.jump_count=0    #function to determine if sprite has collided with the ceiling    def hit_head(self):        self.count=0        self.y_vel*=-1    def update_sprite(self):        sprite_sheet = "idle"        #jump animations, will write this up later        if self.y_vel != 0:            if self.jump_count == 1:                sprite_sheet = "jump"            elif self.jump_count==2:                sprite_sheet = "double_jump"        elif self.y_vel> self.GRAVITY*2:            sprite_sheet = "fall"        elif self.x_vel != 0:            sprite_sheet = "run"        if self.x_vel != 0:            sprite_sheet = "run"        sprite_sheet_name = sprite_sheet +"_" + self.direction        sprites = self.SPRITES[sprite_sheet_name]        sprite_index = (self.animation_count // self. ANIMATION_DELAY) % len(sprites)        self.sprite = sprites[sprite_index]        self.animation_count += 1        self.update()    def update(self):        self.rect = self.sprite.get_rect(topleft=(self.rect.x, self.rect.y))        self.mask = pygame.mask.from_surface(self.sprite)class Object(pygame.sprite.Sprite):    def __init__(self, x, y, width, height, name=None):        super().__init__()        self.rect = pygame.Rect(x, y, width, height)        self.image = pygame.Surface((width, height))        self.image.fill(self.COLOUR)        self.width = width        self.height = height        self.name = name    def draw(self, win, offset_x):        win.blit(self.image, (self.rect.x - offset_x, self.rect.y))class Block(Object):    COLOUR = (0, 0, 0)    def __init__(self, x, y, size):        super().__init__(x, y, size, size)        block = load_block(size)        self.image.blit(block, (0, 0))        self.mask = pygame.mask.from_surface(self.image)#Function addresses any keys pressed to move the spritedef move(player, objects):    keys = pygame.key.get_pressed()    player.x_vel = 0    #handling horizontal collision    collide_left = collide(player,objects, -PLAYER_VEL)    collide_right = collide(player,objects, PLAYER_VEL)    #only allows sprite to move if there is no block next to it    if keys[pygame.K_d] and not collide_right:        player.move_right(PLAYER_VEL)    if keys[pygame.K_a] and not collide_left:        player.move_left(PLAYER_VEL)    #up key removed here, explained later in the code.    #calls the vertical collision function    vertical_collision(player,objects,player.y_vel) def gameplay(window):    clock = pygame.time.Clock()    block_size = 96    player = Player (400, 800, 25, 50)    blocks = [Block(0, height - block_size, block_size)]    #fills the bottom of the screen with platforms.    floor = [Block  (i * block_size, height - block_size, block_size)              for i in range (-width // block_size, width * 2 // block_size)]    #used to keep track of how much the player has moved horizontally relative to the window's view.    offset_x = 0    #when sprite reaches x amount of pixels in either direction, the game starts to scroll.    scroll_area_width = 200    #creating a block above platform    objects = [*floor, Block(0,height - block_size*4, block_size)]    run = True    while run:        clock.tick(FPS)        # Event handling        for event in pygame.event.get():            if event.type == pygame.QUIT:                run = False                break            if event.type == pygame.KEYDOWN:                if event.key == pygame.K_w and player.jump_count < 2:                    player.jump()        # Update player and objects        player.loop(FPS)        move(player, objects)        # Check if player has reached the scrolling area and update offset accordingly        if ((player.rect.right - offset_x >= width - scroll_area_width) and player.x_vel > 0) \                or ((player.rect.left - offset_x <= scroll_area_width) and player.x_vel < 0):            offset_x += player.x_vel        # Draw everything with the updated offset        draw(player, objects, window, offset_x)    pygame.quit()    quit()if __name__ == "__main__":    gameplay(window)

I adjusted the move() function and added collision() function for horizontal collisions.

Move() function was adjusted to add and not collide_right and and not collide_leftDeclared new variables within the move() function collide_left and collide_right

Collision() function was added where player.move(dx,0) and player.update moves the object and updates the mask.

When I added these two functions, I initially had an error that mentioned AttributeError: 'Player' object has no attribute 'sprite', but I fixed that by changing self.sprite to self.image, but I'm not sure if that correlates with the problem I am having.

Any help would be greatly appreciated, and for context this is a platformer game I'm trying to code that is part of a school coursework I have to do.

Thanks in advance.


Viewing all articles
Browse latest Browse all 14271

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>