【问题标题】:Crouching in python platformer with pygame用pygame蹲在python平台游戏中
【发布时间】:2019-07-08 18:04:26
【问题描述】:

我正在为我的 python/pygame 类中的最终项目制作游戏。我遇到了“蹲伏”能力的问题。我希望能够改变播放器的大小,但目前唯一改变的是顶部的精灵。

我的班级正在使用来自this 站点的这个特定示例:

我如何缩放或更改播放器的大小以适合精灵,并且能够在我“松开”时将其更改回来?

不确定哪些部分是相关的,但这是我的代码(我很抱歉它是如此的科学怪人):

class Bink(pygame.sprite.Sprite):
    """ This class represents the bar at the bottom that the player
    controls. """

    # -- Methods
    def __init__(self):
        """ Constructor function """

        # Call the parent's constructor
        super().__init__()

        # -- Attributes
        # Set speed vector of player
        self.change_x = 0
        self.change_y = 0

        # Set fall rate
        self.y_speedup = .45

        # Double jump functions
        self.jump_no = 0

        # Set the players height and length
        self.length = 40
        self.height = 60

        # Set crouching to false
        self.crouching = False

        # This holds all the images for the animated walk left/right
        # of our player
        self.walking_frames_l = []
        self.walking_frames_r = []

        self.standing_frames_l = []
        self.standing_frames_r = []

        self.jumping_frames_l = []
        self.jumping_frames_r = []

        self.falling_frames_l = []
        self.falling_frames_r = []

        self.crouching_frames_l = []
        self.crouching_frames_r = []

        # What direction is the player facing?
        self.direction = "Right"

        # What 'state' is the player in
        self.state = "Stand"

        # List of sprites we can bump against
        self.level = None

        sprite_sheet = SpriteSheet("player_sprite_sheet.png")

        # Load all the right walking images into a list
        image = sprite_sheet.get_image(419, 65, 40, 60)
        self.walking_frames_r.append(image)
        image = sprite_sheet.get_image(460, 65, 40, 60)
        self.walking_frames_r.append(image)
        image = sprite_sheet.get_image(501, 65, 40, 60)
        self.walking_frames_r.append(image)
        image = sprite_sheet.get_image(542, 65, 40, 60)
        self.walking_frames_r.append(image)
        image = sprite_sheet.get_image(583, 65, 40, 60)
        self.walking_frames_r.append(image)
        image = sprite_sheet.get_image(624, 65, 40, 60)
        self.walking_frames_r.append(image)
        image = sprite_sheet.get_image(665, 65, 40, 60)
        self.walking_frames_r.append(image)
        image = sprite_sheet.get_image(706, 65, 40, 60)
        self.walking_frames_r.append(image)
        image = sprite_sheet.get_image(747, 65, 40, 60)
        self.walking_frames_r.append(image)
        image = sprite_sheet.get_image(788, 65, 40, 60)
        self.walking_frames_r.append(image)

        # Load all the left walking images

        image = sprite_sheet.get_image(419, 0, 40, 60)
        self.walking_frames_l.append(image)
        image = sprite_sheet.get_image(460, 0, 40, 60)
        self.walking_frames_l.append(image)
        image = sprite_sheet.get_image(501, 0, 40, 60)
        self.walking_frames_l.append(image)
        image = sprite_sheet.get_image(542, 0, 40, 60)
        self.walking_frames_l.append(image)
        image = sprite_sheet.get_image(583, 0, 40, 60)
        self.walking_frames_l.append(image)
        image = sprite_sheet.get_image(624, 0, 40, 60)
        self.walking_frames_l.append(image)
        image = sprite_sheet.get_image(665, 0, 40, 60)
        self.walking_frames_l.append(image)
        image = sprite_sheet.get_image(706, 0, 40, 60)
        self.walking_frames_l.append(image)
        image = sprite_sheet.get_image(747, 0, 40, 60)
        self.walking_frames_l.append(image)
        image = sprite_sheet.get_image(788, 0, 40, 60)
        self.walking_frames_l.append(image)

        # Load all the left standing sprites
        image = sprite_sheet.get_image(0, 0, 40, 60)
        self.standing_frames_l.append(image)

        # Load all the right standing sprites
        image = sprite_sheet.get_image(419, 195, 40, 60)
        self.standing_frames_r.append(image)

        # Load all the left jumping sprites
        image = sprite_sheet.get_image(0, 65, 40, 60)
        self.jumping_frames_l.append(image)
        image = sprite_sheet.get_image(41, 65, 40, 60)
        self.jumping_frames_l.append(image)
        image = sprite_sheet.get_image(82, 65, 40, 60)
        self.jumping_frames_l.append(image)
        image = sprite_sheet.get_image(123, 65, 40, 60)
        self.jumping_frames_l.append(image)
        image = sprite_sheet.get_image(164, 65, 40, 60)
        self.jumping_frames_l.append(image)
        image = sprite_sheet.get_image(205, 65, 40, 60)
        self.jumping_frames_l.append(image)
        image = sprite_sheet.get_image(246, 65, 40, 60)
        self.jumping_frames_l.append(image)
        image = sprite_sheet.get_image(287, 65, 40, 60)
        self.jumping_frames_l.append(image)
        image = sprite_sheet.get_image(328, 65, 40, 60)
        self.jumping_frames_l.append(image)
        image = sprite_sheet.get_image(369, 65, 40, 60)
        self.jumping_frames_l.append(image)

        # Load all the right jumping sprites
        image = sprite_sheet.get_image(419, 130, 40, 60)
        self.jumping_frames_r.append(image)
        image = sprite_sheet.get_image(460, 130, 40, 60)
        self.jumping_frames_r.append(image)
        image = sprite_sheet.get_image(501, 130, 40, 60)
        self.jumping_frames_r.append(image)
        image = sprite_sheet.get_image(542, 130, 40, 60)
        self.jumping_frames_r.append(image)
        image = sprite_sheet.get_image(583, 130, 40, 60)
        self.jumping_frames_r.append(image)
        image = sprite_sheet.get_image(624, 130, 40, 60)
        self.jumping_frames_r.append(image)
        image = sprite_sheet.get_image(665, 130, 40, 60)
        self.jumping_frames_r.append(image)
        image = sprite_sheet.get_image(706, 130, 40, 60)
        self.jumping_frames_r.append(image)
        image = sprite_sheet.get_image(747, 130, 40, 60)
        self.jumping_frames_r.append(image)
        image = sprite_sheet.get_image(788, 130, 40, 60)
        self.jumping_frames_r.append(image)

        # Load all left falling sprites
        image = sprite_sheet.get_image(0, 130, 40, 60)
        self.falling_frames_l.append(image)
        image = sprite_sheet.get_image(41, 130, 40, 60)
        self.falling_frames_l.append(image)
        image = sprite_sheet.get_image(82, 130, 40, 60)
        self.falling_frames_l.append(image)
        image = sprite_sheet.get_image(123, 130, 40, 60)
        self.falling_frames_l.append(image)
        image = sprite_sheet.get_image(164, 130, 40, 60)
        self.falling_frames_l.append(image)

        # Load all right falling sprites
        image = sprite_sheet.get_image(205, 130, 40, 60)
        self.falling_frames_r.append(image)
        image = sprite_sheet.get_image(246, 130, 40, 60)
        self.falling_frames_r.append(image)
        image = sprite_sheet.get_image(287, 130, 40, 60)
        self.falling_frames_r.append(image)
        image = sprite_sheet.get_image(328, 130, 40, 60)
        self.falling_frames_r.append(image)
        image = sprite_sheet.get_image(369, 130, 40, 60)
        self.falling_frames_r.append(image)

        # Load all the left crouching sprites
        image = sprite_sheet.get_image(0, 195, 45, 40)
        self.crouching_frames_l.append(image)
        image = sprite_sheet.get_image(46, 195, 45, 40)
        self.crouching_frames_l.append(image)
        image = sprite_sheet.get_image(92, 195, 45, 40)
        self.crouching_frames_l.append(image)
        image = sprite_sheet.get_image(138, 195, 45, 40)
        self.crouching_frames_l.append(image)
        image = sprite_sheet.get_image(184, 195, 45, 40)
        self.crouching_frames_l.append(image)

        # Load all right crouching sprites
        image = sprite_sheet.get_image(0, 240, 45, 40)
        self.crouching_frames_r.append(image)
        image = sprite_sheet.get_image(46, 240, 45, 40)
        self.crouching_frames_r.append(image)
        image = sprite_sheet.get_image(92, 240, 45, 40)
        self.crouching_frames_r.append(image)
        image = sprite_sheet.get_image(138, 240, 45, 40)
        self.crouching_frames_r.append(image)
        image = sprite_sheet.get_image(184, 240, 45, 40)
        self.crouching_frames_r.append(image)

        # Set the image the player starts with
        self.image = self.standing_frames_r[0]

        # Set a reference to the image rect.
        self.rect = self.image.get_rect()

        # Build a hitbox that objects collide with
        self.hitbox = self.rect.inflate(0, 0)

        # Add or subtract to see if the player is crouching
        self.is_crouching = 0

    def update(self):
        """ Move the player. """
        # Gravity
        self.calc_grav()

        # Set the hitbox on top of the player
        self.hitbox.center = self.rect.center

        # Check for the direction
        if self.change_x < 0:
            self.direction = "Left"
        elif self.change_x > 0:
            self.direction = "Right"

        # Check for the state
        # See if we hit anything
        self.rect.y += 2
        platform_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
        self.rect.y -= 2

        # If it is ok to jump, set our speed upwards
        if len(platform_hit_list) > 0 or self.hitbox.bottom >= SCREEN_HEIGHT and self.change_x == 0:
            self.state = "Stand"
        elif len(platform_hit_list) > 0 or self.hitbox.bottom >= SCREEN_HEIGHT and self.change_x != 0:
            self.state = "Walk"
        elif self.change_y < 0 or self.change_y < 0 and self.change_x != 0:
            self.state = "Jump"
        elif self.change_y > 0 or self.change_y > 0 and self.change_x != 0:
            self.state = "Fall"
        if self.is_crouching == 1:
            self.state = "Crouch"

        # Move left/right
        self.rect.x += self.change_x
        x_pos = self.rect.x + self.level.world_shift
        y_pos = self.rect.y + self.level.world_shift

        # "If" in 'this' state and 'this' direction use 'that' sprite list for reference
        if self.direction == "Right" and self.state == "Stand":
            frame = (x_pos // 30) % len(self.standing_frames_r)
            self.image = self.standing_frames_r[frame]
        elif self.direction == "Left" and self.state == "Stand":
            frame = (x_pos // 30) % len(self.standing_frames_l)
            self.image = self.standing_frames_l[frame]

        elif self.direction == "Right" and self.state == "Walk":
            frame = (x_pos // 30) % len(self.walking_frames_r)
            self.image = self.walking_frames_r[frame]
        elif self.direction == "Left" and self.state == "Walk":
            frame = (x_pos // 30) % len(self.walking_frames_l)
            self.image = self.walking_frames_l[frame]

        elif self.direction == "Right" and self.state == "Jump":
            frame = (y_pos // 30) % len(self.jumping_frames_r)
            self.image = self.jumping_frames_r[frame]
        elif self.direction == "Left" and self.state == "Jump":
            frame = (y_pos // 30) % len(self.jumping_frames_l)
            self.image = self.jumping_frames_l[frame]

        elif self.direction == "Right" and self.state == "Fall":
            frame = (y_pos // 30) % len(self.falling_frames_r)
            self.image = self.falling_frames_r[frame]
        elif self.direction == "Left" and self.state == "Fall":
            frame = (y_pos // 30) % len(self.falling_frames_l)
            self.image = self.falling_frames_l[frame]

        elif self.direction == "Right" and self.state == "Crouch":
            frame = (x_pos // 30) % len(self.crouching_frames_r)
            self.rect.inflate(5, -20)
            # if self.length != 40:
            #     self.rect.inflate(0, 0)
            self.image = self.crouching_frames_r[frame]
        elif self.direction == "Left" and self.state == "Crouch":
            frame = (x_pos // 30) % len(self.crouching_frames_l)
            self.image = self.crouching_frames_l[frame]

        # See if we hit anything
        block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
        for block in block_hit_list:
            # If we are moving right,
            # set our right side to the left side of the item we hit
            if self.change_x > 0:
                self.rect.right = block.rect.left
            elif self.change_x < 0:
                # Otherwise if we are moving left, do the opposite.
                self.rect.left = block.rect.right

        # Move up/down
        self.rect.y += self.change_y

        # Check and see if we hit anything
        block_hit_list = pygame.sprite.spritecollide(self, self.level.platform_list, False)
        for block in block_hit_list:

            # Reset our position based on the top/bottom of the object.
            if self.change_y > 0:
                self.rect.bottom = block.rect.top
            elif self.change_y < 0:
                self.rect.top = block.rect.bottom

            # Stop our vertical movement
            self.change_y = 0

            if isinstance(block, MovingPlatform):
                self.rect.x += block.change_x

        self.length = self.rect.right - self.rect.left
        print(self.length)

    def calc_grav(self):
        """ Calculate effect of gravity. """

        # Import the wallslide method
        self.wall_slide()

        if self.change_y == 0:
            self.change_y = 1.75
        else:
            self.change_y += .45

        # See if we are on the ground.
        if self.rect.y >= SCREEN_HEIGHT - self.rect.height and self.change_y >= 0:
            self.change_y = 0
            self.rect.y = SCREEN_HEIGHT - self.rect.height

我非常感谢任何和所有反馈!我对此很陌生,所以我完全愿意完全和完全错误。

【问题讨论】:

  • 蹲下的图像帧比直立的帧短吗?切换图像时代码似乎没有调整Sprite.rect,所以我猜它们的大小都一样,但也许这就是问题所在......
  • @Kingsley,有没有我可以用来即时调整宽度和高度的 pygame 函数?或者也许是初始化播放器的不同方式,使其更具延展性>
  • 是的,有几种方法可以做到这一点。可以仅缩放精灵:stackoverflow.com/questions/21082145/pygame-scaling-a-sprite,但您的代码看起来像是具有单独的“蹲伏”图像。这些已经短了 20 像素,所以设置 sprite.rect = sprite.image.get_rect() 并重新调整位置 y += 20 像素,因为框尺寸更小,但从左上角绘制。我认为您不需要扩展它们。只需以正确的尺寸和正确的位置绘制它们即可。

标签: python python-3.x pygame


【解决方案1】:

我决定将我的评论扩展到答案。

所以程序正在从一个精灵表中加载一堆精灵。请注意,“标准”字符图像似乎是40 x 60 像素,而“蹲伏”图像是45 x 40。相差 20 像素(蹲下的图像也宽了 5 像素。)

当精灵进入“蹲伏”模式时,self.state == "Crouch",代码只是改变精灵图像。它不考虑位置的变化。如果角色通过向上拉它的腿/轮子/触手来“蹲下”,并保持相同的高度,那很好。但是,如果它在传统意义上蹲伏,Y 的位置需要通过 sprite-image 高度的差异来增加。这是因为 PyGame 从左上角绘制精灵。

问题中不包含代码,但是无论什么操作导致self.state变为"Crouch",这也需要在进入蹲伏模式时将角色Y位置调整20 pixels,并@ 987654333@ 返回直立状态时。这种差异可能最好在加载的图像上计算,而不是作为一个常数。此外精灵rect 需要重新设置,因为精灵图像大小现在不同并且需要调整碰撞框。

我写了一些快速演示代码,说明如何在精灵上工作。它只是一个螺旋弹簧 - 结果是生成了一个像素化程度很差的位图,但它说明了压缩/未压缩状态。代码的重要部分是SpringSprite.crouch()函数,它实现了Y位置的变化,通过图像高度的差异。由于此代码位于精灵居中矩形上,显然高度变化减半。

向上/向下按压缩和解压缩弹簧。

import pygame

# Window size
WINDOW_WIDTH  = 400
WINDOW_HEIGHT = 400
GREY = ( 226, 232, 233 )


### 
### Spring sprite that is either upright or compressed 
###
class SpringSprite( pygame.sprite.Sprite ):
    def __init__( self ):
        pygame.sprite.Sprite.__init__( self )
        self.upright     = pygame.image.load("spring.png").convert_alpha()            # 64 x 100
        self.compressed  = pygame.image.load("spring_compressed.png").convert_alpha() # 64 x  70
        self.image       = self.upright
        self.rect        = self.image.get_rect()
        self.rect.center = ( WINDOW_WIDTH//2, WINDOW_HEIGHT//2 )
        self.crouched    = False
        self.height_delta= ( self.upright.get_height() - self.compressed.get_height() ) // 2 # 15 pixels (centred)

    def crouch( self, crouch=True ):
        if ( self.crouched != crouch ):
            x,y = self.rect.center
            if ( crouch ):
                # We've changed from upright to crouched
                self.image = self.compressed
                y += self.height_delta # difference is centred sprite height
            else:
                # Changed from crouched back to upright
                self.image = self.upright
                y -= self.height_delta # difference is centred-sprite height
            self.crouched    = crouch;
            self.rect        = self.image.get_rect()
            self.rect.center = ( x, y )

    def update( self ):
        pass

### MAIN
# Open the window
pygame.init()
WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )
SPRITES = pygame.sprite.Group()
pygame.display.set_caption("Coiled Spring")
# Add a sprite
spring = SpringSprite()
SPRITES.add( spring )


clock = pygame.time.Clock()
done = False
# Main loop
while not done:

    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True
        elif ( event.type == pygame.VIDEORESIZE ):
            WINDOW_WIDTH  = event.w
            WINDOW_HEIGHT = event.h
            WINDOW  = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE )

    # Movement keys
    keys = pygame.key.get_pressed()
    if ( keys[pygame.K_UP] ):
        spring.crouch( False ) # Upright
    elif ( keys[pygame.K_DOWN] ):
        spring.crouch( True )  # Compressed

    WINDOW.fill( GREY )
    SPRITES.draw( WINDOW )
    pygame.display.update()
    pygame.display.flip()
    clock.tick_busy_loop(60)

pygame.quit()

【讨论】:

  • 谢谢你,这是一个巨大的帮助!但是,每当我重绘 sprite.rect 时,它会跳到左上角,我如何让它保持在当前位置?
  • @SmittyBoy_ 当sprite.rect 设置为蹲伏精灵的rect 时,它也需要重新定位。类似old_x, old_y = self.rect.x, self.rect.y,然后重新分配矩形以匹配蹲伏的图像,最后重新定位精灵矩形self.rect = self.crouch_image.get_rect()self.rect.x = old_xself.rect.y = old_y + image_height_difference
  • 非常感谢!我用这个把头发拉出来,你让它看起来很容易。
猜你喜欢
  • 2015-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多