pygame platformer - How can I make the bottom solid?

后端 未结 1 1721
一生所求
一生所求 2020-11-27 08:11

So I was making a pygame platformer and I got stuck on one thing. I coudn\'t find a way to make the bottom of my platforms solid. The player could land on the top of it but

相关标签:
1条回答
  • 2020-11-27 09:04

    Here's a short platformer example. Especially the movement is important. You have to move along the x-axis first, check if the player collides with a wall and move it back if a collision occurred. Afterwards do the same with the y-axis. If you don't split the movement into these two parts, your player will jump to the sides, top or bottom of the wall if you press more than one movement key at the same time.

    import pygame as pg
    
    
    pg.init()
    WINDOW_WIDTH, WINDOW_HEIGHT = 800, 600
    screen = pg.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
    GRAY = pg.Color('gray24')
    GRAVITY = 800
    
    
    class Player(pg.sprite.Sprite):
    
        def __init__(self, pos, blocks):
            super().__init__()
            self.image = pg.Surface((30, 50))
            self.image.fill(pg.Color(0, 110, 170))
            self.rect = self.image.get_rect(topleft=pos)
            self.vel = pg.math.Vector2(0, 0)
            self.pos = pg.math.Vector2(pos)
            self.blocks = blocks
            self.on_ground = False
    
        def update(self, dt):
            # Move along x-axis.
            self.pos.x += self.vel.x * dt
            self.rect.x = self.pos.x
    
            collisions = pg.sprite.spritecollide(self, self.blocks, False)
            for block in collisions:  # Horizontal collision occurred.
                if self.vel.x > 0:  # Moving right.
                    self.rect.right = block.rect.left  # Reset the rect pos.
                elif self.vel.x < 0:  # Moving left.
                    self.rect.left = block.rect.right  # Reset the rect pos.
                self.pos.x = self.rect.x  # Update the actual x-position.
    
            # Move along y-axis.
            self.pos.y += self.vel.y * dt
            # +1 to check if we're on a platform each frame.
            self.rect.y = self.pos.y + 1
            # Prevent air jumping when falling.
            if self.vel.y > 0:
                self.on_ground = False
    
            collisions = pg.sprite.spritecollide(self, self.blocks, False)
            for block in collisions:  # Vertical collision occurred.
                if self.vel.y > 0:  # Moving down.
                    self.rect.bottom = block.rect.top  # Reset the rect pos.
                    self.vel.y = 0  # Stop falling.
                    self.on_ground = True
                elif self.vel.y < 0:  # Moving up.
                    self.rect.top = block.rect.bottom  # Reset the rect pos.
                    self.vel.y = 0  # Stop jumping.
                self.pos.y = self.rect.y  # Update the actual y-position.
    
            # Stop the player at screen bottom.
            if self.rect.bottom >= WINDOW_HEIGHT:
                self.vel.y = 0
                self.rect.bottom = WINDOW_HEIGHT
                self.pos.y = self.rect.y
                self.on_ground = True
            else:
                self.vel.y += GRAVITY * dt  # Gravity
    
    
    class Block(pg.sprite.Sprite):
    
        def __init__(self, rect):
            super().__init__()
            self.image = pg.Surface(rect.size)
            self.image.fill(pg.Color('paleturquoise2'))
            self.rect = rect
    
    
    def main():
        clock = pg.time.Clock()
        done = False
        dt = 0
    
        all_sprites = pg.sprite.Group()
        blocks = pg.sprite.Group()
        player = Player((300, 100), blocks)
        all_sprites.add(player)
        rects = ((300, 200, 30, 70), (100, 350, 270, 30),
                 (500, 450, 30, 170), (400, 570, 270, 30),
                 (500, 150, 70, 170), (535, 310, 270, 70))
        for rect in rects:  # Create the walls/platforms.
            block = Block(pg.Rect(rect))
            all_sprites.add(block)
            blocks.add(block)
    
        while not done:
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    done = True
                elif event.type == pg.KEYDOWN:
                    if event.key == pg.K_a:
                        player.vel.x = -220
                    elif event.key == pg.K_d:
                        player.vel.x = 220
                    elif event.key == pg.K_w:  # Jump
                        if player.on_ground:
                            player.vel.y = -470
                            player.pos.y -= 20
                            player.on_ground = False
                elif event.type == pg.KEYUP:
                    if event.key == pg.K_a and player.vel.x < 0:
                        player.vel.x = 0
                    elif event.key == pg.K_d and player.vel.x > 0:
                        player.vel.x = 0
    
            all_sprites.update(dt)
    
            screen.fill(GRAY)
            all_sprites.draw(screen)
    
            pg.display.flip()
            dt = clock.tick(60) / 1000
    
    
    if __name__ == '__main__':
        main()
        pg.quit()
    

    Here's a working version of the code that you've posted in the comments (only with vertical collisions, you need to add horizontal collisions as well). So when the player is jumping and collides with a platform, you have to set the player.rect.top to the platform.rect.bottom and change the vel.y.

    import pygame as pg
    from pygame.math import Vector2 as vec
    
    
    pg.init()
    WIDTH, HEIGHT = 800, 600
    YELLOW = pg.Color('yellow')
    GREEN = pg.Color('green')
    BLACK = pg.Color('gray11')
    screen = pg.display.set_mode((WIDTH,HEIGHT))
    clock = pg.time.Clock()
    FPS = 60
    PLAYER_FRICTION = .95
    PLAYER_ACC = .2
    
    
    class Player(pg.sprite.Sprite):
    
        def __init__(self):
            pg.sprite.Sprite.__init__(self)
            self.image = pg.Surface((30, 40))
            self.image.fill(YELLOW)
            self.rect = self.image.get_rect(center=(WIDTH/2, HEIGHT-30))
            self.pos = vec(WIDTH/2, HEIGHT/2)
            self.vel = vec(0,0)
            self.acc = vec(0,0)
    
        def jump(self):
            self.rect.y += 1
            hits = pg.sprite.spritecollide(self, platforms, False)
            self.rect.y -= 1
            if hits:
                self.vel.y = -13
    
        def update(self):
            self.acc = vec(0, 0.5)
            keys = pg.key.get_pressed()
            if keys[pg.K_a]:
                self.acc.x = -PLAYER_ACC
            if keys[pg.K_d]:
                self.acc.x = PLAYER_ACC
    
            # apply friction
            self.vel.x *= PLAYER_FRICTION
            self.vel += self.acc
            self.pos += self.vel
            # wrap around the sides of the screen
            if self.pos.x > WIDTH:
                self.pos.x = 0
            if self.pos.x < 0:
                self.pos.x = WIDTH
    
            self.rect.midbottom = self.pos
    
    
    class Platform(pg.sprite.Sprite):
        def __init__(self, x, y, w, h):
            pg.sprite.Sprite.__init__(self)
            self.image = pg.Surface((w, h))
            self.image.fill(GREEN)
            self.rect = self.image.get_rect(topleft=(x, y))
    
    
    all_sprites = pg.sprite.Group()
    platforms = pg.sprite.Group()
    
    player = Player()
    all_sprites.add(player)
    # spawns and adds platforms to group
    p1 = Platform(0, HEIGHT - 40, WIDTH, 40)
    p2 = Platform(WIDTH / 2 - 50, HEIGHT - 300, 100, 20)
    p3 = Platform(WIDTH / 2 - 100, HEIGHT - 150, 200, 20)
    all_sprites.add(p1, p2, p3)
    platforms.add(p1, p2, p3)
    
    running = True
    while running:
        clock.tick(FPS)
        for event in pg.event.get():
            if event.type == pg.QUIT:
                running = False
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_SPACE:
                    player.jump()
    
        all_sprites.update()
        # Check if we hit a wall/platform.
        hits = pg.sprite.spritecollide(player, platforms, False)
        for platform in hits:  # Iterate over the collided platforms.
            if player.vel.y > 0:  # We're falling.
                player.rect.bottom = platform.rect.top
                player.vel.y = 0
            elif player.vel.y < 0:  # We're jumping.
                player.rect.top = platform.rect.bottom
                player.vel.y = 3
    
            player.pos.y = player.rect.bottom
    
        #Draw / render
        screen.fill(BLACK)
        all_sprites.draw(screen)
        pg.display.flip()
    
    
    pg.quit()
    

    BTW, in the jump method you have to change self.rect.y not self.rect.x.

    0 讨论(0)
提交回复
热议问题