Python-pygame如何在sprite之间进行光环碰撞?

时间:2018-04-12 14:48:29

标签: python python-3.x pygame

所以,我试图在精灵播放器传递(而非结束)另一个精灵时得到反应。

到目前为止,我的想法是这样的:

if (sprite.x +100) > player.x > (sprite.x -100) and (sprite.y +100) > player.y > (sprite.y -100):
    print("hit")
# Sprite.x and y are the position of the rect, not the whole image

这有效但有一些方法可以简化它吗?我也在想如何摆脱100像素

2 个答案:

答案 0 :(得分:2)

如果你想使用缩放的矩形来进行碰撞检测,你可以给你的原始矩形充气(或者创建一个新的矩形)并将它作为一个单独的属性分配给你的精灵(我在这里称之为hitbox) 。原始rect仅用于存储位置。

创建自定义碰撞检测功能,该功能必须作为collided参数传递给pygame.sprite.spritecollidegroupcollide。在这个函数中,您可以使用colliderect rect的hitbox方法来检查它是否与另一个精灵的矩形碰撞。

def hitbox_collision(sprite1, sprite2):
    """Check if the hitbox of the first sprite collides with the
    rect of the second sprite.

    `spritecollide` will pass the player object as `sprite1`
    and the sprites in the enemies group as `sprite2`.
    """
    return sprite1.hitbox.colliderect(sprite2.rect)

然后以这种方式致电spritecollide

collided_sprites = pg.sprite.spritecollide(
    player, enemies, False, collided=hitbox_collision)

这是一个完整的例子(红色矩形是命中框,绿色矩形是self.rect,用作blit位置):

import pygame as pg


class Player(pg.sprite.Sprite):

    def __init__(self, pos, *groups):
        super().__init__(*groups)
        self.image = pg.Surface((30, 50))
        self.image.fill(pg.Color('dodgerblue1'))
        self.rect = self.image.get_rect(center=pos)
        # Give the sprite another rect for the collision detection.
        # Scale it to the desired size.
        self.hitbox = self.rect.inflate(100, 100)

    def update(self):
        # Update the position of the hitbox.
        self.hitbox.center = self.rect.center


class Enemy(pg.sprite.Sprite):

    def __init__(self, pos, *groups):
        super().__init__(*groups)
        self.image = pg.Surface((30, 50))
        self.image.fill(pg.Color('sienna1'))
        self.rect = self.image.get_rect(center=pos)


def hitbox_collision(sprite1, sprite2):
    """Check if the hitbox of the first sprite collides with the
    rect of the second sprite.

    `spritecollide` will pass the player object as `sprite1`
    and the sprites in the enemies group as `sprite2`.
    """
    return sprite1.hitbox.colliderect(sprite2.rect)


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

    player = Player((100, 300), all_sprites)
    enemy = Enemy((320, 240), all_sprites, enemies)

    done = False

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

        all_sprites.update()
        collided_sprites = pg.sprite.spritecollide(
            player, enemies, False, collided=hitbox_collision)
        for enemy_sprite in collided_sprites:
            print(enemy_sprite)

        screen.fill((30, 30, 30))
        all_sprites.draw(screen)
        pg.draw.rect(screen, (0, 255, 0), player.rect, 1)
        pg.draw.rect(screen, (255, 0, 0), player.hitbox, 1)

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


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

如果您想要一个圆形碰撞区域,您可以将pygame.sprite.collide_circle函数作为collided参数传递。

为您的精灵提供self.radius属性

class Player(pg.sprite.Sprite):

    def __init__(self, pos, *groups):
        super().__init__(*groups)
        self.radius = 100

并在主循环中将collide_circle传递给spritecollide

collided_sprites = pg.sprite.spritecollide(
    player, enemies, False, collided=pg.sprite.collide_circle)

答案 1 :(得分:1)

所以,这里有几件事。从你的第二个问题开始(正如我从你的编写方式中理解的那样),你想要摆脱100个像素是正确的。拥有" 100"在您的代码中随机出现的是 Magic Number / Constant ,并且编程风格很差,原因如下:

  1. 没有任何迹象表明100应该代表什么
  2. 在许多情况下(特别是你的),Magic Numbers会在多个地方复制。当这个数字发生变化时(10次中有9次,确实如此),您必须深入了解所有代码以及相关脚本以更新数字,并希望您在任何地方都不会错过它
  3. 与2相关,您可能会发现您希望经常更改该数字(在本例中为hitbox / aura radius)。硬编码会使这变得困难或不可能。
  4. 很可能,这部分代码最好还是自己的功能,所以你可以简单地使用" hitradius"作为关键字参数,然后替换" hitradius" 100。

    无论是在旁边还是替代它,您都可以将hitradius作为另一个对象(即玩家或其他精灵)的属性进行跟踪,然后以相同的方式(e.g.- sprite.x - player.hitradius > player.x [etc])替换它。如果你确定它永远不会改变,那么把它作为一个可访问的变量在某处并使用相同的技术。

    def check_aura_collision(player,sprite,hitradius = 100):
        """ Checks if the player sprite is within hitradius (default 100)
            pixels of another sprite.
        """
        if (sprite.x + hitradius > player.x > sprite.x - hitradius )\
            and (sprite.y + hitradius > player.y > sprite.y - hitradius ):
    
            print("hit")
            return True
        return False
    

    至于简化逻辑,它已经足够简单了;我可能会使用绝对值,但这并不是一个有意义的改变。然而,相反,你可能有兴趣通过使用一些基本的触发来检查圆形光环而不是你现在拥有的方形光环来使它稍微复杂一些。