How would I fix my characters position after hitting a block?

可紊 提交于 2020-01-02 23:14:36

问题


After I hit a platform my character can't move along the x-axis anymore unless you press the left or right keys again. For example, if I jump and I clip the corner of the platform the character doesn't continue moving and I have to either do the jump again or react quickly and press the corresponding key. When I remove the player.friction() in the collision detecting system, if I stop pressing the right key it ends up clipping into the edge of the block, which causes the player to get permanently stuck unless they press the right and jump keys at the same time. I presume this could be fixed by changing the blocks position again, but as of now I don't know how to implement this.

Here is my code:

import pygame

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLUE = (0, 255, 255)

DISPLAY_WIDTH = 800
DISPLAY_HEIGHT = 600
gameDisplay = pygame.display.set_mode((DISPLAY_WIDTH, DISPLAY_HEIGHT))

def textObjects(text, font):
    textSurface = font.render(text, True, BLACK)
    return textSurface, textSurface.get_rect()
def messageDisplay(text):
    largeText = pygame.font.Font('freesansbold.ttf', 115)
    textSurf, textRect = textObjects(text, largeText)
    textRect.center = ((DISPLAY_WIDTH/2), (DISPLAY_HEIGHT/2))
    gameDisplay.blit(textSurf, textRect)


class Player(pygame.sprite.Sprite):

    def __init__(self):


        super().__init__()


        width = 100
        height = 150
        self.image = pygame.Surface([width, height])
        self.image.fill(RED)


        self.rect = self.image.get_rect()


        self.xVel = 0
        self.yVel = 0


        self.level = None

    def update(self):
        self.calc_grav()


        self.level.bgX += self.xVel



        correction = 0
        block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
        for block in block_hit_list:
            if self.xVel < 0:
                self.friction()
                correction = self.rect.right - block.rect.left
            elif self.xVel > 0:
                self.friction()
                correction = self.rect.left - block.rect.right

        if correction != 0:
            for block in self.level.platform_list:
                block.rect.x += correction


        self.rect.y += self.yVel


        block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
        for block in block_hit_list:
            if self.yVel > 0:
                self.rect.bottom = block.rect.top
            if self.yVel < 0:
                self.rect.top = block.rect.bottom


            self.yVel = 0

            if isinstance(block, MovingPlatform):
                self.level.bgX += block.xVel

    def calc_grav(self):

        if self.yVel == 0:
            self.yVel = 1
        else:
            self.yVel +=0.3

        if self.rect.y >= DISPLAY_HEIGHT - self.rect.height and self.yVel >= 0:
            self.yVel = 0
            self.rect.y = DISPLAY_HEIGHT - self.rect.height


    def jump(self):
        self.rect.y += 2
        platform_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
        self.rect.y -= 2


        if len(platform_hit_list) > 0 or self.rect.bottom >= DISPLAY_HEIGHT:
            self.yVel = -10

    def moveLeft(self):

        self.xVel = 10

    def moveRight(self):

        self.xVel = -10

    def friction(self):

        self.xVel = 0


class Platform(pygame.sprite.Sprite):

    def __init__(self, width, height):
        super().__init__()

        self.image = pygame.Surface([width, height])
        self.image.fill(GREEN)

        self.rect = self.image.get_rect()


class MovingPlatform(Platform):

    xVel = 0
    yVel = 0

    boundary_top = 0
    boundary_bottom = 0
    boundary_left = 0
    boundary_right = 0

    player = None

    level = None

    def update(self):
        self.rect.x += self.xVel
        correction = 0
        hit = pygame.sprite.collide_rect(self, self.player)
        if hit:
            if self.xVel < 0:
                self.player.friction()
                correction = self.player.rect.right - self.rect.left
            elif self.xVel > 0:
                self.player.friction()
                correction = self.player.rect.left - self.rect.right

        if correction != 0:
            for block in self.level.platform_list:
                block.rect.x += correction

        self.rect.y += self.yVel


        hit = pygame.sprite.collide_rect(self, self.player)
        if hit:
            if self.yVel < 0:
                self.player.rect.bottom = self.rect.top
            else:
                self.player.rect.top = self.rect.bottom

        if self.rect.bottom > self.boundary_bottom or self.rect.top < self.boundary_top:
            self.yVel *= -1
        cur_pos = self.rect.x - self.level.bgX
        if cur_pos < self.boundary_left or cur_pos > self.boundary_right:
            self.xVel *= -1


class Level(object):

    def __init__(self, player):
        self.platform_list = pygame.sprite.Group()
        self.enemy_list = pygame.sprite.Group()
        self.player = player

        self.background = None


        self.bgX = 0
        self.level_limit = -1000

    def update(self):
        self.platform_list.update()
        self.enemy_list.update()

    def draw(self, screen):

        screen.fill(BLUE)


        self.platform_list.draw(screen)
        self.enemy_list.draw(screen)

        for platform in self.platform_list:
            platform.rect.x += self.player.xVel

        for enemy in self.enemy_list:
            enemy.rect.x += self.player.xVel

class Level_01(Level):

    def __init__(self, player):


        Level.__init__(self, player)

        self.level_limit = -1500


        level = [[210, 70, 500, 500],
                 [210, 70, 800, 400],
                 [210, 70, 1000, 500],
                 [210, 70, 1120, 280],
                 ]


        for platform in level:
            block = Platform(platform[0], platform[1])
            block.rect.x = platform[2]
            block.rect.y = platform[3]
            block.player = self.player
            self.platform_list.add(block)


        block = MovingPlatform(100, 40)
        block.rect.x = 1350
        block.rect.y = 280
        block.boundary_left = 1350
        block.boundary_right = 1600
        block.xVel = -1
        block.player = self.player
        block.level = self
        self.platform_list.add(block)


class Level_02(Level):

    def __init__(self, player):

        Level.__init__(self, player)

        self.level_limit = -1000


        level = [[210, 70, 500, 550],
                 [210, 70, 800, 400],
                 [210, 70, 1000, 500],
                 [210, 70, 1120, 280],
                 ]


        for platform in level:
            block = Platform(platform[0], platform[1])
            block.rect.x = platform[2]
            block.rect.y = platform[3]
            block.player = self.player
            self.platform_list.add(block)

        block = MovingPlatform(70, 70)
        block.rect.x = 1500
        block.rect.y = 300
        block.boundary_top = 100
        block.boundary_bottom = 550
        block.yVel = -1
        block.player = self.player
        block.level = self
        self.platform_list.add(block)


def main():
    pygame.init()


    size = [DISPLAY_WIDTH, DISPLAY_HEIGHT]
    screen = pygame.display.set_mode(size)

    pygame.display.set_caption("Platformer with moving platforms")

    player = Player()

    level_list = []
    level_list.append(Level_01(player))
    level_list.append(Level_02(player))

    current_level_no = 0
    current_level = level_list[current_level_no]

    active_sprite_list = pygame.sprite.Group()
    player.level = current_level

    player.rect.x = 350
    player.rect.y = DISPLAY_HEIGHT - player.rect.height
    active_sprite_list.add(player)


    done = False


    clock = pygame.time.Clock()

    # -------- Main Program Loop -----------
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    player.moveLeft()
                if event.key == pygame.K_RIGHT:
                    player.moveRight()
                if event.key == pygame.K_UP:
                    player.jump()

            if event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT and player.xVel > 0:
                    player.friction()
                if event.key == pygame.K_RIGHT and player.xVel < 0:
                    player.friction()

        active_sprite_list.update()

        current_level.update()


        current_position = current_level.bgX
        if current_position < current_level.level_limit:
            if current_level_no < len(level_list)-1:
                player.rect.x = 350
                current_level_no += 1
                current_level = level_list[current_level_no]
                player.level = current_level
            else:

                done = True


        current_level.draw(screen)
        active_sprite_list.draw(screen)


        clock.tick(60)


        pygame.display.flip()

    pygame.quit()

if __name__ == "__main__":
    main()

Here is the part of my code that controls the movement and the platform collision system:

Collision (located within the player class):

hit = pygame.sprite.collide_rect(self, self.player)
if hit:
    if self.xVel < 0:
        self.player.friction()
        correction = self.player.rect.right - self.rect.left
    elif self.xVel > 0:
        self.player.friction()
        correction = self.player.rect.left - self.rect.right

if correction != 0:
    for block in self.level.platform_list:
        block.rect.x += correction

Movement (located within the game loop):

if event.type == pygame.KEYDOWN:
    if event.key == pygame.K_LEFT:
        player.moveLeft()
    if event.key == pygame.K_RIGHT:
        player.moveRight()
    if event.key == pygame.K_UP:
        player.jump()

if event.type == pygame.KEYUP:
    if event.key == pygame.K_LEFT and player.xVel > 0:
        player.friction()
    if event.key == pygame.K_RIGHT and player.xVel < 0:
        player.friction()

回答1:


For the problem of having to re-press a left/right key to continue in the desired direction:

You should be acting on key state (pressed/released) not key changes (up/down). Here is the modified event loop you are probably looking for:

for event in pygame.event.get():
    if event.type == pygame.QUIT:
        done = True
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_LEFT:
            player.left_pressed=True
            #player.moveLeft()
        if event.key == pygame.K_RIGHT:
            player.right_pressed=True
            #player.moveRight()
        if event.key == pygame.K_UP:
            player.jump()
    if event.type == pygame.KEYUP:
        if event.key == pygame.K_LEFT:
            player.left_pressed=False
            if player.xVel > 0:
                player.friction()
        if event.key == pygame.K_RIGHT:
            player.right_pressed=False
            if player.xVel < 0:
                player.friction()

if player.left_pressed:
    player.moveLeft()
if player.right_pressed:
    player.moveRight()

Make sure to add left_pressed and right_pressed variables in your player class, initialized to False. Also I haven't used pygame in a while, so there may already be a key state variable available so you don't need key change events.



来源:https://stackoverflow.com/questions/59525136/how-would-i-fix-my-characters-position-after-hitting-a-block

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!