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()
4

1 回答 1

0

有几件事结合在一起会给您带来问题:首先,在您标记为可能存在问题的区域内,

    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

也应该切换:

    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

此外,我不确定 pygame 是如何完成其​​坐标的,但对于重置 s.pos 值,我发现s.pos.y = (hit.rect.top) + 1效果要好得多。

其次,将立方体射到一边的原因也是因为玩家精灵落在立方体上后,如果玩家精灵与立方体齐平,它也会与侧面重叠(并因此相交),这会触发x命中。有很多方法可以解决这个问题,但是所有方法(至少我知道的那些)都有自己的陷阱需要提防,但至少现在你知道它为什么会发生。

祝你好运,重力很烂。

根据评论编辑:我想优先考虑这一点,因为我在该领域的工作充其量是最少的,您应该查找官方资源以获得最佳帮助。话虽如此,当我自己遇到这个问题时,我将系统从检查所有交叉点(顶部、侧面和底部)切换为一次只检查一个,通过 else if,因为如果玩家与顶部相交,那么他们应该不与同一块的一侧相交。这在一定程度上会起作用,但是如果玩家跳到块的侧面会产生错误,因为此时,玩家将与侧面和顶部相交,使用 else-if 结构会导致玩家传送在顶部,这不是您想要的。

为了解决这个问题,您可以在 if-else 中进行第二次检查,确定是否应该根据玩家的 y 坐标传送玩家,这应该有助于改进它。问题是这会导致修复的兔子洞这变得很难管理(从经验中说)。但是……它确实有效。

同样,我建议您查找有关该主题的教程,以获得更合适的游戏设计。

于 2021-05-05T23:43:39.313 回答