【问题标题】:PyGame group of spritesPyGame 精灵组
【发布时间】:2014-07-22 02:04:14
【问题描述】:

总的来说,我是 PyGame 和 python 的新用户。话虽如此,我已经创建了一些精灵并将它们放在一个组中。例如,

gems = pygame.sprite.Group()

for i in range (0,4):
    gem = Gem()
    all_sprite_list.add(gem)
    gems.add(gem)

现在,我主要想通过按下特定键来操纵组中的一颗宝石。我目前正在通过为每个 gem 设置一个 id 属性并遍历该组来完成此操作,直到我将 id 与我想要操作的那个匹配。即:

if event.type == pygame.KEYDOWN and event.key == pygame.K_F1:
    for gem in gems:
        if gem.id == 1:
           gem.state = normal
elif event.type == pygame.KEYDOWN and event.key == pygame.K_F2:
    for gem in gems:
        if gem.id == 2:
           gem.state = normal

我知道这是一个肮脏肮脏的把戏,我确信有更好的方法,而不是遍历整个组。我查看了参考资料并尝试做类似

的事情
gems.sprites().index(0) 

无济于事。有什么建议么?

【问题讨论】:

  • 您还可以将精灵保存在字典中并使用id 作为键。

标签: python pygame sprite


【解决方案1】:

您似乎知道哪些键对应于哪些 ID,因此您可以将该信息放在一个 dict 中,然后只对 gem 进行一次迭代:

gem_keys = {
    1: pygame.K_F1,
    2: pygame.K_F2,
}

if event.type == pygame.KEYDOWN:
    for gem in gems:
        # Check if key pressed corresponds to current gem
        if event.key == gem_keys[gem.id]:
           gem.state = normal

除了代码笨拙之外,您是否遇到过任何具体问题?

【讨论】:

    【解决方案2】:

    在 PyGame 的 Sprite 类中,您有一个函数 update(*args) (documentation)。

    (顺便说一句,*args 表示您可以传入任意数量的参数:

    >>> def f(*args):
    ...     print args
    ...
    >>> f(1,2)
    (1, 2)
    >>> f(3)
    (3,)
    >>> f(3,5,6)
    (3, 5, 6)
    >>> def g(*args):
    ...     print args[0]
    ...
    >>> g(1,2)
    1
    

    默认情况下,update 什么都不做,但您可以出于自己的目的覆盖它,例如通过创建一个覆盖基础 Sprite 类的类:

    class MySprite(Sprite):
        def update(self,*args):
            key = args[0]
            if key == pygame.K_F1 and self.id == 1 :
                self.state = normal
            elif key == pygame.K_F2 and self.id == 2 :
                self.state = normal
    

    现在,Sprite Groups 也有一个update 函数(具有相同的签名update(*args)

    Official documentation.

    这个函数的作用是调用update,并在每个精灵上传入参数

    # group has sprite1 and sprite2
    group.update(1,2,3) # same as calling sprite1.update(1,2,3) 
                        #  and sprite2.update(1,2,3)
    

    所以如果gems 是您的组,您可以这样做(假设您使用类似于上面的MySprite 类)

    if event.type == pygame.KEYDOWN:
        gems.update(event.key) 
        # this will call the MySprite.update function for all the gems
    

    【讨论】:

    • 这当然看起来更干净,并且几乎是我想要的......但它是否尽可能高效?我们真的必须对组中的每个项目调用更新吗?有没有办法只操纵我想要的精灵?
    • 当您有一个更通用的更新周期(您可能会在每个 gem 上检查许多不同的属性)时,这是“最佳”解决方案,但我正在为你更具体的情况
    【解决方案3】:

    我写了another answer,这似乎是更新 PyGame 精灵的规范答案(尤其是在每次更新更新大量精灵时)。但是,对于在许多精灵中更新少数精灵的情况,这里有一个替代解决方案。


    目标是在一个大集合中找到一小部分对象。在这里,我提出的解决方案是与您的精灵组有一个并行字典。这个字典将被键控到触发它的键。

    在这里,我们将使用defaultdict(查看question(特别是第二个答案)来了解如何使用它们)。基本上它是一个带有默认值的字典(这里因为它是 list 它填充了空列表)。

    key_dict = defaultdict(list)
    # add the sprite triggered by F1
    key_dict[pygame.K_F1].append(gem1)
    # you can add multiple sprites as well (these are lists)
    key_dict[pygame.K_F2].append(gem2)
    key_dict[pygame.K_F2].append(gem3)
    
    #...  in your update loop
    if event.type == pygame.KEYDOWN:
        for gem in key_dict[event.key] : #this gets all the gems for this key event 
                                         # (empty if not defined because defaultdict)
            gem.state = normal
    

    如果您有很多 颗宝石并且真的只需要碰几个小的宝石,这个解决方案就很好。

    但是有一些问题:

    • 你需要维护两个集合(你的精灵组和这个)

    • 如果你对很多宝石都使用这个,那么速度优势会更少

    • 这会让你的代码更混乱

    我认为只有当您处于要更新的​​宝石数量远小于宝石总数的情况下才值得这样做。如果您还要更新许多其他属性,我建议您使用其他答案,因为它更干净。

    【讨论】:

    • “你需要维护两个集合(你的精灵组和这个)”这正是我想尝试使用精灵组的原因。如果没有必要,我不想维护 2 个列表。
    • 我查看了精灵组代码,似乎没有一个简单的方法来查询它,所以这可能是你最好的选择。
    猜你喜欢
    • 1970-01-01
    • 2017-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-24
    • 2023-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多