sprite.rect和sprite.image坐标原点之间的偏移量

时间:2017-11-18 23:40:26

标签: python pygame

我在pygame文档中发现精灵的.rect属性通常使用与.image属性相同的宽度和相同的高度进行初始化。

但是,您可以更改.rect宽度或高度,以使.rect变得大于或小于.image,但.image和.rect的原点(topleft corner)始终是相同的点。

所以这是我的问题: 有没有办法在精灵的.rect和.image的原点(即topleft corner)之间创建一个偏移量?

我知道你可以通过以下方式避免这种情况:

  • 创建并使用第二个Rect属性(例如.collide_rect),每次.rect将

  • 时更新
  • 在提供的.image中使用透明度

但这些方法效率很低,我真的很感激"是/否"答案。

我已经在官方文档中搜索了几个小时,但我找不到创建这种偏移量的方法,所以我想我的问题的答案是" No" (即使.inflate_ip()方法不起作用,因为它没有像文档中所写的那样保持.rect的中心。)

1 个答案:

答案 0 :(得分:0)

您可以为sprite类添加一个offset属性(Vector2),然后使用for循环遍历精灵,并将此offset添加到.rect.topleft当你对图像进行blitting时的位置。

import pygame as pg
from pygame.math import Vector2


class Player(pg.sprite.Sprite):

    def __init__(self, pos, *groups):
        super().__init__(*groups)
        self.image = pg.Surface((220, 120))
        self.image.fill(pg.Color('steelblue2'))
        self.rect = self.image.get_rect(center=pos)
        self.rect.inflate_ip(-42, -72)  # Make the rect smaller.
        self.vel = Vector2(0, 0)
        self.pos = Vector2(pos)
        # Offset is half of the inflation size,
        # so that the rect will be centered.
        self.offset = Vector2(-21, -36)

    def update(self):
        self.pos += self.vel
        self.rect.center = self.pos


def main():
    screen = pg.display.set_mode((640, 480))
    clock = pg.time.Clock()
    all_sprites = pg.sprite.Group()
    player = Player((300, 200), all_sprites)

    done = False

    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_d:
                    player.vel.x = 5
            elif event.type == pg.KEYUP:
                if event.key == pg.K_d:
                    player.vel.x = 0

        all_sprites.update()

        screen.fill((30, 30, 30))
        # Assign `blit` to a local variable to improve the performance.
        screen_blit = screen.blit
        for sprite in all_sprites:
            # Now blit the sprites at topleft + offset.
            screen_blit(sprite.image, sprite.rect.topleft+sprite.offset)
            pg.draw.rect(screen, (250, 30, 0), sprite.rect, 2)

        pg.display.flip()
        clock.tick(30)


if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()

为了完整起见,这里的解决方案是使用第二个,缩放的" hitbox" rect和自定义collided回调函数,您可以将其传递给其中一个碰撞检测函数,如pygame.sprite.spritecollide

import pygame as pg
from pygame.math import Vector2


class Player(pg.sprite.Sprite):

    def __init__(self, pos, *groups):
        super().__init__(*groups)
        self.image = pg.Surface((70, 40))
        self.image.fill(pg.Color('steelblue4'))
        self.rect = self.image.get_rect(center=pos)
        # A inflated rect as the hitbox.
        self.hitbox = self.rect.copy()
        self.hitbox.inflate_ip(-42, -22)
        self.vel = Vector2(0, 0)
        self.pos = Vector2(pos)

    def update(self):
        self.pos += self.vel
        self.rect.center = self.pos
        self.hitbox.center = self.pos  # Also update the hitbox coords.


def collided(sprite, other):
    """Check if the hitboxes of the two sprites collide."""
    return sprite.hitbox.colliderect(other.hitbox)


def main():
    screen = pg.display.set_mode((640, 480))
    clock = pg.time.Clock()
    all_sprites = pg.sprite.Group()
    player = Player((300, 200), all_sprites)
    enemies = pg.sprite.Group(
        Player((100, 250), all_sprites),
        Player((400, 300), all_sprites),
        )

    done = False

    while not done:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                done = True
            elif event.type == pg.MOUSEMOTION:
                player.pos = event.pos

        all_sprites.update()
        # Pass the custom collided callback function to spritecollide.
        collided_sprites = pg.sprite.spritecollide(
            player, enemies, False, collided)
        for sp in collided_sprites:
            print('Collision', sp)

        screen.fill((30, 30, 30))

        all_sprites.draw(screen)
        for sprite in all_sprites:
            # Draw rects and hitboxes.
            pg.draw.rect(screen, (0, 230, 0), sprite.rect, 2)
            pg.draw.rect(screen, (250, 30, 0), sprite.hitbox, 2)

        pg.display.flip()
        clock.tick(30)


if __name__ == '__main__':
    pg.init()
    main()
    pg.quit()