0

I've been working on collisions in Pygame and the issue is that when the player moves lands of the top of the tile they are immediately blitted onto either the left or the right. How would I make it so that it the player lands on top of the platform that they aren't blitted to the left or the right? In the code below between the lines of ////// is where the collision issues seems to be occurring.

import pygame

pygame.init()
pygame.mixer.init()

clock = pygame.time.Clock()
running = True

# game properties
WIDTH = 800
HEIGHT = 600
FPS = 60

# colors
BLACK = (0, 0, 0)
BLUE = (0, 255, 255)
GREEN = (0, 255, 0)

# physics
GRAVITY = 0.3
ACCEL = 1
FRICTION = -0.12

window = pygame.display.set_mode((WIDTH, HEIGHT))

# Platform properties
SMALL = (100, 50)
MEDIUM = (20000, 200)
GROUND = (100000, 100)

vec = pygame.math.Vector2

class Sonic(pygame.sprite.Sprite):
    
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 50))
        self.image.fill(BLUE)
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH / 2, HEIGHT - 51)
        self.pos = vec(WIDTH / 2 , HEIGHT - 51)
        self.vel = vec(0, 0)
        self.acc = vec(0, 0)
        self.last_update = 0
        self.time_passed = 0
        

    def update(self):
        self.acc = vec(0, GRAVITY)
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.acc.x = -ACCEL - 2
        if keys[pygame.K_RIGHT]:
            self.acc.x = ACCEL
        if keys[pygame.K_UP]:
            self.acc.y = -ACCEL
                    
       
       # friction check
        self.acc.x += self.vel.x * FRICTION
        if self.vel.x < -2:
            self.vel.x = -2

       # equations of motion
        self.vel += self.acc
        self.pos += self.vel + 0.5 * self.acc

        self.rect.midbottom = self.pos
            
class Ground(pygame.sprite.Sprite):

    def __init__(self, x, y, plat):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((GROUND))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y
        


class Platform(pygame.sprite.Sprite):

    def __init__(self, x, y, plat):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((plat))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.x = x 
        self.rect.y = y

# player sprites
s = Sonic()

# floor sprites
ground = Ground(0, HEIGHT - 50, GROUND)
floor = pygame.sprite.Group()
floor.add(ground)


# platform sprites

platforms = [Platform(WIDTH - 200, HEIGHT - 100, SMALL)]

plats = pygame.sprite.Group()


for i in platforms:
    plats.add(i)
    

# all sprites
sprites = pygame.sprite.Group()
sprites.add(s)
sprites.add(plats)
sprites.add(ground)


while running:
    clock.tick(FPS)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # update
    sprites.update()
    


    if s.vel.y > 0:
        collisions = pygame.sprite.spritecollide(s, floor, False)

        if collisions:
            for collision in collisions:
                if s.pos.y > collision.rect.top: 
                    s.pos.y = collision.rect.top
                    s.rect.bottom = s.pos.y
                    s.vel.y = 0 

    """
    where the collision issue seems to be happening (CODE BELOW)
    """
////////////////////////////////////////////////////////////////////////////////////////////////////////
    if s.vel.x > 0:
        hits = pygame.sprite.spritecollide(s, plats, False)
        if hits:
            for hit in hits:
                if s.pos.x > hit.rect.left:
                    s.pos.x = (hit.rect.left - (s.rect.width / 2) - 1) 
                    s.rect.right = s.pos.x
                    s.vel.x = 0
    if s.vel.x < 0:
        hits = pygame.sprite.spritecollide(s, plats, False)
        if hits:
            for hit in hits:
                if s.pos.x < hit.rect.right:
                    s.pos.x = (hit.rect.right + (s.rect.width / 2) + 1)
                    s.rect.left = s.pos.x
                    s.vel.x = 0
    """
    when player lands on a tile they are blitted to either side of the tile (how do I fix this?)
    """
    
    if s.vel.y > 0:
        hits = pygame.sprite.spritecollide(s, plats, False)
        if hits:
            for hit in hits:
                if s.pos.y > hit.rect.top:
                    s.pos.x = (hit.rect.top - (s.rect.width / 2) + 1)
                    s.rect.bottom = s.pos.y
                    s.vel.x = 0

    
////////////////////////////////////////////////////////////////////////////////////////////////////////


    # draw
    window.fill(BLACK)

    sprites.draw(window)
    
    

    # double buffering 
    pygame.display.flip()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174

1 Answers1

0

A couple things that are all combining to give you your issue: First, inside the area you marked as probably having the issue,

    if s.vel.y > 0:
        hits = pygame.sprite.spritecollide(s, plats, False)
        if hits:
            for hit in hits:
                if s.pos.y > hit.rect.top:
                    s.pos.x = (hit.rect.top - (s.rect.width / 2) + 1)
                    s.rect.bottom = s.pos.y
                    s.vel.x = 0

Should be switched too:

    if s.vel.y > 0:
        hits = pygame.sprite.spritecollide(s, plats, False)
        if hits:
            for hit in hits:
                if s.pos.y > hit.rect.top:
                    s.pos.y = (hit.rect.top - (s.rect.width / 2) + 1)
                    s.rect.bottom = s.pos.y 
                    s.vel.y = 0

Additionally, I'm not sure fully how pygame does its coordinates but for resetting s.pos value, I found that s.pos.y = (hit.rect.top) + 1 worked much better.

Second, the reason it was shooting the cube out to the side was also because after the player sprite landed on the cubes, if the player sprite becomes flush with the cubes it overlaps (and thus intersects) with the sides as well, which triggers the x hits. There are a number of ways to deal with this problem, however all of them (at least the ones I know) have their own pitfalls to beware, but at least now you know why it's happening.

Good luck, gravity sucks.

Edit per the comment: I want to preference this with the fact that my work in this field is minimal at best, you should look up official resources for best help. That being said, when I encountered this issue myself, I switched the system from checking all intersections (top, side, and bottom) to only checking one at a time, via else if, because if the player intersects with the top then they shouldn't intersect with the side of the same block. This will work to an extent, however it creates a bug if the player jumps into the side of the block, as at that point, the player will be intersecting both the side and the top, using the else-if structure causes the player to teleport on top which is not what you want.

To account for this, you can then have a second check inside the if-else determining if you should teleport the player based on their y coordinate, and that should help improve it.. The issue is this then leads down a rabbit hole of fixes which becomes a pain to manage (speaking from experience). But... it does work.

Again, I would recommend looking up a tutorial on the topic for more proper game design.

H. Pope
  • 123
  • 13